ItemsControl和Canvas - 只有第一个项目可见

3

我有一个画布,其中包含几个不同的形状,这些形状都是静态的,并绑定到视图模型(MVVM)中的不同属性。目前,画布定义如下(简化):

<Canvas>
 <Polygon Fill="Red" Stroke="Gray" StrokeThickness="3" Points="{Binding StorageVertices}" />
 <Ellipse Fill="Blue" Width="{Binding NodeWidth}" Height="{Binding NodeHeight}" />

 <!-- And some more static shapes -->
 <!-- ...                         -->
</Canvas>

我想在这个画布上添加一个动态列表,其中每个条目都转换为多边形。我认为最好的方法是使用ItemsControl。这是我在我的方法中使用的,但只显示集合(列表)中的第一项。

<Canvas>
 <!-- ...                                                          -->
 <!-- Same canvas as earlier with the addition of the ItemsControl -->

 <ItemsControl ItemsSource="{Binding Offices, Mode=OneWay, Converter={...}}">
  <ItemsControl.ItemTemplate>
   <DataTemplate>
    <Polygon Fill="AliceBlue" Stroke="Gray" StrokeThickness="1" Points="{Binding Points}" />
   </DataTemplate>
  </ItemsControl.ItemTemplate>
 </ItemsControl>
</Canvas>

使用此代码,仅显示Offices集合中的第一个项目。为什么?如果查看可视树,则所有多边形都在其中。我对WPF非常陌生,所以只能猜测,但我的第一个想法是,在这种情况下,将StackPanel默认设置为ItemPresenter可能不恰当,但我只能猜测...

我不这么认为,因为如果我反转集合(所以一个不同的“多边形”首先出现),它甚至不会与之前显示的“多边形”重叠。 - Elliott Darfink
你在哪里设置“Offices”属性? - chris6523
在我的视图模型构造函数中,这个画布与之相关(Canvas位于一个与我的视图模型松散耦合的UserControl中)。 - Elliott Darfink
你的Floor集合中有哪些类型的对象? - myermian
我不认为我提到过“Floor”集合,但是“Storage”区域被描述为“Polygon”。所有物品都应该在“Storage”内呈现。如果我将“Storage”“Polygon”设置为不可见,则仍然只有一个“Office”(集合中的第一个)可见(因此“Storage”没有更高的z索引)。 - Elliott Darfink
2个回答

7

首先需要注意的是,当使用Canvas面板时,面板中的每个项目都将被放置在左上角,除非指定了相对位置。这是一个带有元素的Canvas的示例,其中一个元素靠近顶部(向下40像素,向右40像素),另一个元素位于底部(距离右侧边缘向左100像素):

<Canvas>
    <Polygon Canvas.Left="40" Canvas.Top="40" ... />
    <Ellipse Canvas.Right="100" Canvas.Bottom="0" ... />
</Canvas>

现在,请记住,CanvasPanel 的一种类型。它的主要目的不是作为某种列表,而是更进一步定义控件(或控件)的 呈现方式。如果您希望实际呈现控件的集合/列表(枚举),那么您应该使用 ItemsControl 类型。从那里,您可以指定 ItemsSource 并自定义 ItemsPanel (以及可能需要的 ItemTemplate)。

其次,经常会遇到一个问题:“如何向数据绑定的ItemsSource添加静态元素?”,对此,答案是使用CompositeCollection和随后的CollectionContainer。在您的情况下,您有两个(2)静态项(以及更多),您希望将它们添加到您的Offices集合中。我猜这些“静态形状”实际上是楼层平面图像的替代品。
这是一个示例,如果您希望绘制您的楼层平面图,您的XAML代码将如下所示:
<ItemsControl>
    <ItemsControl.Resources>
        <CollectionViewSource x:Key="cvs" Source="{Binding Floors}" />
    </ItemsControl.Resources>
    <ItemsControl.ItemsSource>
        <CompositeCollection>
            <CollectionContainer Collection="{Binding Source={StaticResource cvs}" />

            <!-- Static Items -->
        </CompositeCollection>
    </ItemsControl.ItemsSource>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas ... />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

我不确定你的Floor集合中的每个对象都是什么,但它们不应该是任何类型的形状。它们应该是一些简单陈述有关办公室位置、颜色等信息的对象。这里是一个示例,因为你没有提供项目集合的组成部分:

// This can (and should) implement INotifyPropertyChanged
public class OfficeViewModel
{
    public string EmployeeName { get; private set; }

    public ReadOnlyObservableCollection<Point> Points { get; private set; }

    ...
}

public class Point
{
    public double X { get; set; }
    public double Y { get; set; }
}

在这里,您需要使用一个 DataTemplate 来将对象(模型/视图模型)转换为视图上应该呈现的样子:

<ItemsControl>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Polygon Points="{Binding Points}" Color="AliceBlue" ... />
        <DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

当然,如果你希望从你的集合Offices中拥有每个项目的多个表示方式,则需要利用DataTemplateSelector(将设置为ItemsControl.ItemTemplateSelector属性)从一组DataTemplate中进行选择。这里有一个很好的答案/参考:https://stackoverflow.com/a/17558178/347172
最后,还有一点需要注意...保持一切按比例缩放,并将您的点作为double类型。个人建议使用0-1或0-100的比例尺。只要所有点和静态项都在这个范围内,您就可以将ItemsControl拉伸到任意高度/宽度,里面的所有内容也会相应地调整并匹配得很好。
更新: 已经过了一段时间,我忘记了CompositeCollection类不是FrameworkElement类型,因此它没有DataContext。如果您想要绑定其中一个集合的数据,请指定一个具有所需DataContext的FrameworkElement引用:
<CollectionContainer Collection="{Binding DataContext.Offices, Source={x:Reference someControl}}"/>
更新2:在网上挖掘了一段时间后,我找到了一个更好的方法来允许数据绑定与CompositeCollection一起使用。上面的答案部分已经更新以考虑使用CollectionViewSource来创建一个绑定到集合的资源。这比使用x:Reference要好得多。希望能有所帮助。

我再也无法期待更好的答案了。谢谢! - Elliott Darfink
1
我已经更新了我的被接受的答案,展示了更好地使用CompositeCollection的方法。我已经很久没有使用它了,以至于我忘记了正确的使用方式。希望更新后的答案能够帮助并使其更易理解。 - myermian
这真的救了我的命,我找不到任何关于ItemsControl的好的或深入的解释,但在这里我终于找到了。 - Tofandel

0

尝试设置

yourItemsControl.DataContext = Offices;

在代码后台。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接