This is a question with many different answers, but have not found one working for Windows Phone.
So here goes:
I have an Items Control that displays UserControls.
I bind an ObservableCollection to this ItemsControl like this:
<Canvas Name="CanvasGrid" Grid.Column="1" Background="Transparent" Canvas.ZIndex="5">
<ItemsControl ItemsSource="{Binding InGame}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Canvas>
<View:SInGame/>
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Canvas>
When an element is added in Itemscontrol the element is given a zindex independent of what I give the element, and the usercontrol which is showed. What I want to do is have to buttons where I can move the selected element up and down in the z direction.
View SInGame
The Canvas inside the itemscontrol is needed for the position to be binded correctly from the following view:
<UserControl x:Class="MVVMTestApp.View.SInGame"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WP8"
xmlns:View="clr-namespace:MVVMTestApp.View"
mc:Ignorable="d"
Canvas.Left="{Binding pos.x}" Canvas.Top="{Binding pos.y}">
<Path >
<.....
....../>
</Path>
</UserControl>
It could then be argued that the view should consist of a Canvas that has the position binded. But this would still leave the issue of the zindex.
As stated in the comments. The ItemsControl
cannot see the outside canvas and can therefore not use the zindex to reach that. So how is it possible to set the zindex? Or is it not possible using the ItemsControl
element?
The ItemsControl takes each item, wraps it in a container (ContentPresenter), and adds it to the items panel (StackPanel by default). So, schematically, you will end up with this tree:
<!-- the items panel (StackPanel by default) -->
<StackPanel>
<!-- the first item wrapper -->
<ContentPresenter>
<!-- your item template -->
<Canvas>
<View:SInGame/>
</Canvas>
</ContentPresenter>
<!-- more items ... -->
</StackPanel>
You will want to do 2 things:
ItemsControl.ItemsPanel
to give the items a shared Canvas container.ZIndex
you want gets applied to the ContentPresenter
wrappers.These two points will give you a schema like this, which should allow you to position the items front-to-back:
<!-- the items panel (change to a Canvas) -->
<Canvas>
<!-- the first item wrapper (need to set Canvas.ZIndex to position) -->
<ContentPresenter Canvas.ZIndex="1">
<!-- your item template -->
<Canvas>
<View:SInGame/>
</Canvas>
</ContentPresenter>
<!-- more items ... -->
</Canvas>
To achieve the second point, I believe you will need to subclass ItemsControl
, and override PrepareContainerForItemOverride
. For example, the following modifies each item container, binding its ZIndex property to the ZIndex of the item itself:
public class CanvasItemsControl : ItemsControl
{
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
var contentPresenter = (ContentPresenter)element;
var binding = new Binding("ZIndex") { Source = contentPresenter.Content };
contentPresenter.SetBinding(Canvas.ZIndexProperty, binding);
}
}
Now you can just bind Canvas.ZIndex
on the View:SInGame
item to the underlying model property ("pos.x"?).
Putting it all together:
<local:CanvasItemsControl ItemsSource="{Binding InGame}">
<local:CanvasItemsControl.ItemTemplate>
<DataTemplate>
<Canvas Canvas.ZIndex="{Binding pos.x}" >
<View:SInGame />
</Canvas>
</DataTemplate>
</local:CanvasItemsControl.ItemTemplate>
<local:CanvasItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
<local:CanvasItemsControl.ItemsPanel>
</local:CanvasItemsControl>
This way, each SInGame
instance gets a Z-index, and each should be positioned relative to the others. You can re-position the items front-to-back by modifying the "MyZIndex" property of each view-model item in "InGame".
Edit
There's a simpler alternative to the above, which might work for you. This approach will give you a flattened tree like this:
<!-- the items panel (change to a Canvas) -->
<Canvas>
<!-- the items -->
<View:SInGame/>
<View:SInGame/>
</Canvas>
To do this, override GetContainerForItemOverride()
to return the SInGame
object:
public class CanvasItemsControl : ItemsControl
{
protected override DependencyObject GetContainerForItemOverride()
{
return new SInGame();
}
// Any setup of the `SInGame` items should be done here
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
var container = (SInGame)element;
}
}
This allows you to define the ItemsControl without any ItemTemplate:
<local:CanvasItemsControl ItemsSource="{Binding InGame}">
<local:CanvasItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
<local:CanvasItemsControl.ItemsPanel>
</local:CanvasItemsControl>