简体   繁体   English

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

[英]WPF MVVM View with varying number of objects. How to?

HI! 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. 例如 - 如果viewmodel可以包含像对象列表(矩形)这样的字段,当我将成员更改/添加到列表时,新的矩形会出现在指定位置的视图中,这将是很棒的。 How do i create such view/viewmodel? 我如何创建这样的视图/ viewmodel?

You could have an ICollectionView or ObservableCollection<T> property in your ViewModel and bind the ItemsSource property of an ItemsControl to this property. 您可以在ViewModel中拥有ICollectionViewObservableCollection<T>属性,并将ItemsControlItemsSource属性绑定到此属性。 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 . 但是,它通常会在StackPanel显示它们,因为这是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. 这可以通过使用Canvas作为ItemsControlItemsPanel ,然后将Canvas.LeftCanvas.Top属性绑定到ViewModel中的属性来完成。 Of course, every item would need a Left and Top property then (and maybe also a Width and Height property). 当然,每个项目都需要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
}

And your XAML: 而你的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>

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. 在最后一步中,您可能希望LeftTop属性相对于Canvas的大小,以便在Canvas大小更改时项目保持在相对位置。 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>

The description of the code is already in the XAML comments. 代码的描述已经在XAML注释中。 However, I should note that I have used the ExpressionConverter from Kent Boogart's Converter collection . 但是,我应该注意到我使用了Kent Boogart的Converter集合中ExpressionConverter 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! 祝好运!

One problem with the accepted answer is that the Canvas.Left/Top bindings do not work since the Rectangles are in wrapped in container controls by the ItemsControl. 接受的答案的一个问题是Canvas.Left / Top绑定不起作用,因为Rectangle被ItemsControl包装在容器控件中。 This other question addresses this issue: Setting Canvas properties in an ItemsControl DataTemplate 另一个问题解决了这个问题: 在ItemsControl DataTemplate中设置Canvas属性

Hopefully that helps others, since I was banging my head on the screen wondering why this wasn't working. 希望这有助于其他人,因为我在屏幕上敲打着想知道为什么这不起作用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM