具有不同数量对象的 WPF MVVM 视图.如何? [英] WPF MVVM View with varying number of objects. How to?

查看:45
本文介绍了具有不同数量对象的 WPF MVVM 视图.如何?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

嗨!我想设计包含不同位置的多个对象的视图.例如 - 如果 viewmodel 可以有像对象列表(矩形)这样的字段,并且当我更改/添加成员到列表时,新的矩形会出现在指定位置的视图上,那就太好了.我如何创建这样的视图/视图模型?

HI! I want to design view which will contain multiple objects in different locations. For example - it would be great if viewmodel could have field like list of objects (rectangles) and when i change/add members to list, new rectangles appear on view in specified positions. How do i create such view/viewmodel?

推荐答案

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

You could have an ICollectionView or ObservableCollection<T> property in your ViewModel and bind the ItemsSource property of an ItemsControl to this property. Then this will display all the items in your collection(view). However, it will typically display them in a StackPanel as this is the default item container for an ItemsControl. As far as I understood your question, you want the items to be placed anywhere on your screen. This could be done by using a Canvas as the ItemsControl's ItemsPanel, and then binding the Canvas.Left and Canvas.Top properties to properties in your ViewModels. Of course, every item would need a Left and Top property then (and maybe also a Width and Height property).

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>

在最后一步中,您可能希望 LeftTop 属性相对于 Canvas 的大小,以便如果 Canvas 的大小发生变化,项目将保持在相对位置.这是更多的工作:

In a final step, you might want the Left and Top properties to be relative to the size of the Canvas, so that the items remain at the relative positions if the size of the Canvas changes. This is some more work:

<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 注释中.但是,我应该注意到我使用了 Kent Boogart 的转换器集合中的 ExpressionConverter.我从我的一个应用程序中复制并粘贴了上述代码,因此由于根据您的场景快速调整属性,其中可能存在一些不一致之处.但是,我认为原则应该是明确的.祝你好运!

The description of the code is already in the XAML comments. However, I should note that I have used the ExpressionConverter from Kent Boogart's Converter collection. I copied and pasted the above code from one of my applications, so there might be some inconsistencies in there because of quickly adjusting the properties to your scenario. However, the principle should be clear, I think. Good luck!

这篇关于具有不同数量对象的 WPF MVVM 视图.如何?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆