代码之家  ›  专栏  ›  技术社区  ›  0xDEAD BEEF

具有不同数量对象的WPF MVVM视图。如何?

  •  2
  • 0xDEAD BEEF  · 技术社区  · 14 年前

    你好! 我想设计在不同位置包含多个对象的视图。例如,如果ViewModel可以有类似字段的对象列表(矩形),那么它将非常好,当我更改/添加成员到列表时,新的矩形将显示在视图的指定位置。 如何创建这样的视图/视图模型?

    2 回复  |  直到 14 年前
        1
  •  6
  •   gehho    14 年前

    你可以有一个 ICollectionView ObservableCollection<T> 属性并绑定 ItemsSource AN的性质 ItemsControl 对这个财产。然后将显示集合中的所有项(视图)。但是,它通常将它们显示在 StackPanel 因为这是 项目控制 . 据我所知,您希望项目放在屏幕上的任何位置。这可以通过使用 Canvas 作为 项目控制 ItemsPanel ,然后绑定 Canvas.Left Canvas.Top 属性到视图模型中的属性。当然,每个项目都需要 Left Top 当时的财产(也可能是 Width Height 财产)。

    public class ItemViewModel
    {
        public double Left { get; set; }
        public double Top { get; set; }
        public double Width { get; set; }
        public double Height { get; set; }
    
        // whatever you need...
    }
    
    public class CollectionViewModel
    {
        public ObservableCollection<ItemViewModel> Collection { get; }
    
        // some code which fills the Collection with items
    }
    

    你的XAML:

    <ItemsControl ItemsSource="{Binding Collection}">
    
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    
        <ItemsControl.ItemTemplate>
            <DataTemplate DataType="{x:Type local:ItemViewModel}">
                <Rectangle Width="{Binding Width}" Height="{Binding Height}"
                           Canvas.Left="{Binding Left}" Canvas.Top="{Binding Top}"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    
    </ItemsControl>
    

    最后一步,您可能需要 顶部 属性相对于 帆布 使项目保持在相对位置,如果 帆布 变化。这是更多的工作:

    <DataTemplate DataType="{x:Type local:ItemViewModel}">
        <Rectangle Width="{Binding Width}" Height="{Binding Height}">
    
            <!-- Make the left position of the item depend on the ActualWidth of the Canvas,
                 the relative Left position (between 0 and 1) from the ItemViewModel, and the ActualWidth
                 of the item itself. This is needed because the Canvas.Left property defines the
                 position of the left side, not the center. Therefore, we calculate the position of
                 the center this way:
                      (Canvas.ActualWidth * ItemViewModel.Left) - (Item.ActualWidth / 2)
            -->
            <Canvas.Left>
                <MultiBinding>
                    <MultiBinding.Converter>
                        <converters:ExpressionConverter Expression="{}({0} * {1}) - ({2} / 2)"/>
                    </MultiBinding.Converter>
                    <Binding Path="ActualWidth" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Canvas}}"/>
                    <Binding Path="Left"/>
                    <Binding Path="ActualWidth" RelativeSource="{RelativeSource Self}"/>
                </MultiBinding>
            </Canvas.Left>
    
            <!-- the top position of the items is determined the same way as the left position
                 which is described above -->
            <Canvas.Top>
                <MultiBinding>
                    <MultiBinding.Converter>
                        <converters:ExpressionConverter Expression="{}({0} * {1}) - ({2} / 2)"/>
                    </MultiBinding.Converter>
                    <Binding Path="ActualHeight" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Canvas}}"/>
                    <Binding Path="Top"/>
                    <Binding Path="ActualHeight" RelativeSource="{RelativeSource Self}"/>
                </MultiBinding>
            </Canvas.Top>
    
        </Rectangle>
    </DataTemplate>
    

    代码的描述已在XAML注释中。但是,我应该注意,我已经使用了 ExpressionConverter Kent Boogart's Converter collection . 我从我的一个应用程序中复制并粘贴了上面的代码,因此其中可能存在一些不一致之处,因为可以根据您的场景快速调整属性。不过,我认为,这一原则应该是明确的。祝你好运!

        2
  •  2
  •   Community c0D3l0g1c    7 年前

    接受的答案有一个问题,即canvas.left/top绑定不起作用,因为itemscontrol将矩形包装在容器控件中。另一个问题解决了这个问题: Setting Canvas properties in an ItemsControl DataTemplate

    希望这对其他人有帮助,因为我在屏幕上敲打我的头,想知道为什么这不起作用。