【问题标题】:changing zindex of elements in itemscontrol windows phone更改 itemscontrol windows phone 中元素的 zindex
【发布时间】:2014-04-18 08:20:09
【问题描述】:

这个问题有很多不同的答案,但没有找到适用于 Windows Phone 的问题。
所以这里是:

我有一个显示用户控件的项目控件。
我像这样将 ObservableCollection 绑定到这个 ItemsControl:

<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>

当一个元素被添加到 Itemscontrol 中时,该元素会被赋予一个 zindex,这与我赋予该元素的内容以及显示的用户控件无关。我想要做的是拥有可以在 z 方向上上下移动所选元素的按钮。

查看 SInGame

itemscontrol 中的 Canvas 需要从以下视图正确绑定位置:

<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>

然后可以说视图应该由一个绑定了位置的 Canvas 组成。但这仍然会留下 zindex 的问题。

如 cmets 中所述。 ItemsControl 看不到外面的画布,因此不能使用 zindex 来达到它。那么如何设置zindex呢?还是不能使用ItemsControl 元素?

【问题讨论】:

  • 移除 ItemsControl 的 DataTemplate 中的 Canvas。拥有另一个画布会混淆 ZIndex 的渲染。之后,我相信您可以将 ZIndex 绑定到您的实体的属性(InGame 集合中的项目)
  • @ShawnKendrot 没有画布元素不会被渲染,所以这是不可能的?
  • 当然会渲染。 ItemsControl 不需要 Canvas 位于 DataTemplate 中
  • @ShawnKendrot 对不起,我记错了为什么画布在那里。我更新了问题。但问题是 itemscontrol 无法看到外围画布,因此视图中的位置绑定将无法到达画布。

标签: c# xaml windows-phone-8 observablecollection itemscontrol


【解决方案1】:

ItemsControl 获取每个项目,将其包装在一个容器 (ContentPresenter) 中,并将其添加到项目面板(默认为 StackPanel)。因此,示意性地,您最终会得到这棵树:

<!-- the items panel (StackPanel by default) -->
<StackPanel>
    <!-- the first item wrapper -->
    <ContentPresenter>
        <!-- your item template -->
        <Canvas>
            <View:SInGame/>
        </Canvas>
    </ContentPresenter>

    <!-- more items ... -->
</StackPanel>

你需要做两件事:

  • 设置 ItemsControl.ItemsPanel 为项目提供一个共享的 Canvas 容器。
  • 确保将您想要的 ZIndex 应用到 ContentPresenter 包装器。

这两点会给你一个这样的模式,它应该允许你从前到后定位项目:

<!-- 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>

要实现第二点,我相信您需要继承ItemsControl,并覆盖PrepareContainerForItemOverride。例如,以下修改每个项目容器,将其 ZIndex 属性绑定到项目本身的 ZIndex:

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);
    }
}

现在您可以将View:SInGame 项上的Canvas.ZIndex 绑定到基础模型属性(“pos.x”?)。

把它们放在一起:

<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>

这样,每个SInGame 实例都会获得一个 Z 索引,并且每个实例都应该相对于其他实例定位。您可以通过修改“InGame”中每个视图模型项目的“MyZIndex”属性来重新定位项目的前后位置。


编辑

上面有一个更简单的替代方法,它可能对您有用。这种方法会给你一个扁平的树,如下所示:

<!-- the items panel (change to a Canvas) -->
<Canvas>
    <!-- the items -->
    <View:SInGame/>
    <View:SInGame/>
</Canvas>

为此,覆盖GetContainerForItemOverride() 以返回SInGame 对象:

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;
    }
}

这允许您在没有任何 ItemTemplate 的情况下定义 ItemsControl:

<local:CanvasItemsControl ItemsSource="{Binding InGame}">
    <local:CanvasItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas />
        </ItemsPanelTemplate>
    <local:CanvasItemsControl.ItemsPanel>
</local:CanvasItemsControl>

【讨论】:

  • 谢谢,这周五有空我会测试一下,再反馈。但是感谢您详细的回答!
  • 我猜 DataTemplate 中的 Canvas 是不需要的。在这种情况下,我们有带有 ContentPresenters 的 Canvas 和带有 SInGame 的 Canvas。是不是有点复杂?
  • @ad1Dima 再次查看 OP -- SInGame 控件定义了 Canvas.Left。仅当其直接父级是 Canvas 时才有效...
  • @McGarnagle 好的,我有一个关于最佳使用的问题。由于您现在说不需要 DataTemplate 因为 .cs 代码中的容器。您的第一个答案似乎过分了。两者有什么优缺点。不幸的是,我可以在星期五先测试它。但只是我可以控制兴趣:)
  • @JTIM 第二种方法的缺点是它没有集成ItemTemplate——至少现在还可以,因为看起来你不需要它。我肯定会采用第二种方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多