WPF MVVM使用不同数量的对象查看.如何?

0xD*_*EEF 2 c# wpf mvvm

HI!我想设计一个包含不同位置的多个对象的视图.例如 - 如果viewmodel可以包含像对象列表(矩形)这样的字段,当我将成员更改/添加到列表时,新的矩形会出现在指定位置的视图中,这将是很棒的.我如何创建这样的视图/ viewmodel?

geh*_*hho 6

您可以在ViewModel中拥有一个ICollectionViewObservableCollection<T>属性,并将ItemsSourcean 的属性绑定ItemsControl到此属性.然后,这将显示集合中的所有项目(视图).但是,它通常会在a中显示它们,StackPanel因为这是一个的默认项容器ItemsControl.据我了解您的问题,您希望将项目放在屏幕上的任何位置.这可以通过使用a Canvas作为ItemsControl's ItemsPanel,然后将Canvas.LeftCanvas.Top属性绑定到ViewModel中的属性来完成.当然,每一个项目都需要一个LeftTop财产,然后(也许还WidthHeight财产).

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
}
Run Code Online (Sandbox Code Playgroud)

而你的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>
Run Code Online (Sandbox Code Playgroud)

在最后一步中,您可能希望LeftTop属性相对于其大小Canvas,以便在Canvas更改大小时项目保持在相对位置.这是一些更多的工作:

<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>
Run Code Online (Sandbox Code Playgroud)

代码的描述已经在XAML注释中.但是,我应该注意到我使用了ExpressionConverter来自Kent Boogart的Converter系列.我从我的一个应用程序复制并粘贴了上面的代码,因此可能会出现一些不一致的原因,因为我们会根据您的场景快速调整属性.但是,我认为原则应该是明确的.祝好运!