0

I'm learning wpf and how to properly bind. This is the code I am working on:

<ItemsControl ItemsSource="{Binding CanvasChildren}">
    <ItemsControl.Resources>
        <DataTemplate DataType="{x:Type espace:Entity}" />
    </ItemsControl.Resources>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemContainerStyle>
        <Style>
            <Setter Property="Canvas.Left" Value="{Binding X}" />
            <Setter Property="Canvas.Top" Value="{Binding Y}" />
            <Setter Property="Canvas.ZIndex" Value="{Binding Z}" />
        </Style>
    </ItemsControl.ItemContainerStyle>
</ItemsControl>

So you can see I'm binding CanvasChildren in this ItemsControl. CanvasChildren is an ObservableCollection of type "Entity". Entity is my own class and it's another canvas object that will have images and such in it.

Entity has properties X,Y,Z in it and I WANT to be able to bind those properties to the Canvas.Left,Canvas.Top,Canvas.ZIndex but I am TOTALLY lost on how to do that. The style setters I have defined here do NOT WORK.

For one the binding values are checking for X,Y,Z coordinates in my viewmodel which is defined:

<base:SceneBase.DataContext>
    <sceneGame:SceneGameViewModel />
</base:SceneBase.DataContext>

But changing the setters to something like:

    <ItemsControl.ItemContainerStyle>
        <Style>
            <Setter Property="Canvas.Left" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type espace:Entity}}, Path=X}" />
            <Setter Property="Canvas.Top" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type espace:Entity}}, Path=Y}" />
            <Setter Property="Canvas.ZIndex" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type espace:Entity}}, Path=Z}" />
        </Style>
    </ItemsControl.ItemContainerStyle>

does not work either.

It may possibly be something simple that I'm overlooking but I'm still learning WPF and I'm lost.

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
Philip Vaughn
  • 606
  • 6
  • 20

1 Answers1

2

If you use an ItemsControl with an item type that is a UIElement (which is a bit unusual), the control will not create an additional item container - i.e. a ContentPresenter - element for it, but instead apply the ItemContainerStyle directly to the item. You can verify this by setting TargetType="espace:Entity" on the Style.

In this case, the ItemsControl does also not set the DataContext of the UIElement item, which means that Bindings without an explictly set source won't work. The Bindings in the ItemContainerStyle would use the item object directly as its source, i.e. use RelativeSource Self.

It is also useless to declare a DataTemplate for the item type (especially an empty one), because it would be ignored. The item is not considered to be "data", but UI.

<ItemsControl ItemsSource="{Binding CanvasChildren}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemContainerStyle>
        <Style TargetType="espace:Entity">
            <Setter
                Property="Canvas.Left"
                Value="{Binding X, RelativeSource={RelativeSource Self}}"/>
            <Setter
                Property="Canvas.Top"
                Value="{Binding Y, RelativeSource={RelativeSource Self}}"/>
            <Setter
                Property="Panel.ZIndex"
                Value="{Binding Z, RelativeSource={RelativeSource Self}}"/>
        </Style>
    </ItemsControl.ItemContainerStyle>
</ItemsControl>
Clemens
  • 123,504
  • 12
  • 155
  • 268
  • So thank you for the response. I'll check it tonight after I get off work but this is a game. The Canvas Children is a list of characters walking around on the screen and I want to be able to adjust the x,y,z values of them by just changing those variables on the npc. The reason they are ui elements is because they will have images (like a person or tree). – Philip Vaughn Sep 21 '20 at 19:35
  • Those UI elements would usually be declared in the ItemTemplate and have their properties bound to properties of the data item. E.g. as shown here: https://stackoverflow.com/a/22325266/1136211 – Clemens Sep 21 '20 at 19:39
  • This is a dynamic list that can change at any time. Say a new NCP is added or one is removed. This list also includes map images like houses, trees, grass... it includes animations that play when you open a box or get hit... all kinds of stuff. This is why I had to use this format with itemcontrol and canvas binding to CanvasChildren. I'm using mvvm obviously. – Philip Vaughn Sep 21 '20 at 19:41
  • 1
    That would all work without creating UI elements in code behind. But there's of course nothing wrong with your approach. ItemsControl supports it out of the box. – Clemens Sep 21 '20 at 19:42
  • 1
    This worked perfectly, thanks!! I still have a lot to learn about the items control apparently. – Philip Vaughn Sep 25 '20 at 21:14