How do I do bindings in ItemContainerStyle in WinRT?

8.8k views Asked by At

I'm trying to bind a collection to an ItemsControl, with a Canvas as the items panel, and with each item's Canvas.Left and Top bound to properties on the item objects. Basically I'm trying to re-create the 2-D databinding I described in this post on my blog, but this time in WinRT instead of WPF.

Since ItemsControl wraps your ItemTemplate content in another UI element (a ContentPresenter, in the case of WinRT), and it's those wrapper/container elements that are placed directly inside the items panel, the Left and Top have to be set on those containers; you can't just set them in the DataTemplate. In WPF, it's easy enough to do this with bindings in the ItemContainerStyle, e.g.:

<ItemsControl.ItemContainerStyle>
    <Style>
        <Setter Property="Canvas.Left" Value="{Binding Path=X}"/>
        <Setter Property="Canvas.Top" Value="{Binding Path=Y}"/>
    </Style>
</ItemsControl.ItemContainerStyle>

But when I try the same thing in a WinRT/XAML project, I get nothing. Not even binding errors. If I hard-code a value, it works; but if I use a binding, the property just stays at its default value (zero), and no binding errors are shown in the Output window.

<ItemsControl.ItemContainerStyle>
    <Style TargetType="ContentPresenter">
        <!-- This works, so ItemContainerStyle does work in WinRT: -->
        <Setter Property="Canvas.Left" Value="200"/>
        <!-- But this silently fails, leaves Top as 0, and does not show
             any binding errors in the debugger's Output window: -->
        <Setter Property="Canvas.Top" Value="{Binding Y}"/>
    </Style>
</ItemsControl.ItemContainerStyle>

I've verified that the ContentPresenters do have the correct DataContext (i.e. the collection item, not the collection itself or something else funky), so you'd think these bindings would work just fine. But they don't even seem to get evaluated. If I put a bad binding anywhere else, and run a debug build, I see binding errors in the debugger's Output window; but if I reference a nonsense property inside my ItemContainerStyle, no binding errors are shown.

Here's a more complete example, that (as far as I know) should work fine in WPF, but that leaves everything at the origin in WinRT:

<ItemsControl ItemsSource="{Binding Tiles}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemContainerStyle>
        <Style TargetType="ContentPresenter">
            <Setter Property="Canvas.Left" Value="{Binding DataContext.Left}"/>
        </Style>
    </ItemsControl.ItemContainerStyle>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Rectangle Width="80" Height="80" Fill="Gray"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

I've tried a few of the more exotic options on Binding -- specifically RelativeSource. When I used RelativeSource TemplatedParent, the do-nothing behavior was unchanged. However, when I used RelativeSource Self, I did get a binding error, saying that the property didn't exist on type Setter! It's taking that Self a little too literally, there.

I also played around with TemplateBinding, but I never really grokked what that's supposed to be used for, and all I got was some incomprehensible COM errors (welcome to WinRT, a huge technological step backward).

How can I either (a) make the bindings work correctly (are there other options on Binding that I could use to coerce it to work properly?), or (b) otherwise allow items in my ItemsContainer to be positioned arbitrarily on a Canvas based on databindings to properties on the collection items?

3

There are 3 answers

4
Filip Skakun On BEST ANSWER

Bindings are not supported on Setters. I think Silverlight only got them in version 5 if at all. For workarounds you can look at my older article here. Basically you define an attached dependency property that sets up the binding for you.

0
DrewCan On

Alternatives : Here are other opportunities to attach bindings via code during the creation of the "Item" before presentation.

ItemsControl.PrepareContainerForItemOverride http://msdn.microsoft.com/en-us/library/windows/apps/xaml/windows.ui.xaml.controls.itemscontrol.preparecontainerforitemoverride.aspx

ListViewBase.ContainerContentChanging http://msdn.microsoft.com/en-us/library/windows/apps/xaml/windows.ui.xaml.controls.listviewbase.containercontentchanging.aspx

1
Jason On

Applying a RenderTransform seems to be working nicely for me in silverlight and winrt/metro/8.1:

<ItemsControl ItemsSource="{Binding TreeMapItems}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas Background="White"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Rectangle Width="{Binding Width}" Height="{Binding Height}" Fill="{Binding Brush}" ToolTipService.ToolTip="{Binding Label}">
                <Rectangle.RenderTransform>
                    <TranslateTransform X="{Binding X}" Y="{Binding Y}"/>
                </Rectangle.RenderTransform>
            </Rectangle>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>