WPF MVVM Просмотр с различным количеством объектов. Как? - PullRequest
2 голосов
/ 02 июня 2010

HI! Я хочу создать вид, который будет содержать несколько объектов в разных местах. Например, было бы замечательно, если бы viewmodel мог иметь поле, подобное списку объектов (прямоугольники), и когда я меняю / добавляю членов в список, новые прямоугольники появляются в представлении в указанных позициях. Как мне создать такой view / viewmodel?

Ответы [ 2 ]

6 голосов
/ 02 июня 2010

Вы можете иметь свойство ICollectionView или ObservableCollection<T> в вашей ViewModel и привязать свойство ItemsSource ItemsControl к этому свойству. Затем отобразятся все элементы вашей коллекции (просмотр). Тем не менее, он обычно отображает их в StackPanel, так как это контейнер по умолчанию для ItemsControl. Насколько я понял ваш вопрос, вы хотите, чтобы элементы были размещены в любом месте на вашем экране. Это можно сделать, используя Canvas в качестве ItemsPanel *1009*, а затем связав свойства Canvas.Left и Canvas.Top со свойствами в ваших ViewModels. Разумеется, тогда каждому предмету потребуется свойство 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>

На последнем шаге вы можете захотеть, чтобы свойства Left и Top были относительно размера 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>

Описание кода уже есть в комментариях к XAML. Тем не менее, я должен отметить, что я использовал ExpressionConverter из коллекции конвертера Кента Бугарта . Я скопировал и вставил приведенный выше код из одного из моих приложений, поэтому там могут быть некоторые несоответствия из-за быстрой настройки свойств в соответствии с вашим сценарием. Однако принцип должен быть ясен, я думаю. Удачи!

2 голосов
/ 24 декабря 2010

Одна проблема с принятым ответом состоит в том, что привязки Canvas.Left / Top не работают, поскольку прямоугольники заключены в элементы управления контейнера с помощью ItemsControl. Этот другой вопрос решает эту проблему: Установка свойств Canvas в шаблоне данных ItemsControl

Надеюсь, это поможет другим, так как я бился головой об экран, удивляясь, почему это не работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...