开发者

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

HI! I want to design view which will contain multiple objects i开发者_StackOverflow中文版n 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?


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
}

And your 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. 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. 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!


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. This other question addresses this issue: Setting Canvas properties in an ItemsControl DataTemplate

Hopefully that helps others, since I was banging my head on the screen wondering why this wasn't working.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜