WPF: добавление UIElements в ListBox, у которого ItemsPanelTemplate является холстом? - PullRequest
1 голос
/ 28 мая 2009

Я работаю над ListBox, который переопределяет ItemsPanelTemplate, чтобы использовать Canvas вместо StackPanel. У ListBoxItems есть DataTemplate, который использует конвертер для определения вида и положения каждого ListBoxItem на холсте. Когда я добавляю элемент в коллекцию, с которой связан ListBox, я хотел бы иметь возможность добавлять другие UIElement на холст. Могу ли я сделать это за пределами конвертера ListBoxItem?

Мой раздел ресурсов выглядит так:

    <Style TargetType="ListBox">
        <Setter Property="ItemsPanel">
            <Setter.Value>
                <ItemsPanelTemplate>
                    <Canvas x:Key="cnvAwesome">

                    </Canvas>
                </ItemsPanelTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style TargetType="ListBoxItem">
        <Setter Property="Canvas.Left" Value="{Binding Path=Position.X}" />
        <Setter Property="Canvas.Top" Value="{Binding Path=Position.Y}" />
    </Style>

И ListBox:

    <ListBox x:Name="lstAwesome" ItemsSource="{Binding Source={StaticResource someCollection}}"/>

И коллекция, к которой привязан ListBox:

public ObservableCollection<SomeType> someCollection {get; set; }

Итак, если у меня есть метод для добавления элемента в коллекцию someCollection, с которой связан ListBox lstAwesome:

private void AddItemToDataboundCollection(SomeType someItem)
{
    someCollection.Add(someItem)
    // I'd like to add a UIElement to the canvas that the ListBox uses to layout items here.
}

Итак, можно ли добавить элементы UIE на холст, используемый для ItemsPanel в списке данных с привязкой к данным? Программно, когда я добавляю элементы в коллекцию, с которой связан этот ListBox?

Буду очень признателен за любой вклад!

Ответы [ 2 ]

2 голосов
/ 28 мая 2009

Я думаю, что у вас есть все в вашем вопросе для добавления пунктов в Canvas вашего ListBox. Я просто собрал простое тестовое приложение WPF, используя код в вашем вопросе, и это просто сработало. Всякий раз, когда я добавлял элементы к ObservableCollection, который привязан к ItemsSource из ListBox, WPF создавал ListBoxItem для этого объекта, и на основе стиля ListBoxItem элемент отображался в правильном месте по Canvas.

Вы не смогли заставить работать свой код выше? Если это так, какую ошибку вы получили или что не сработало, как вы ожидали?

Редактировать : После прочтения вопроса я думаю, что путаница связана с неправильным пониманием того, как работает привязка WPF. Когда вы связываете коллекцию с ItemsSource *1016*, все, что вам нужно сделать, это добавить элементы в эту коллекцию (при условии, что коллекция реализует INotifyCollectionChanged, что ObservableCollection делает). WPF подписывается на события, предоставляемые этим интерфейсом, и создает / уничтожает ListBoxItems на основе изменений, внесенных в список.

Редактировать 2 : хотя для этого лучше использовать DataTemplate, чтобы строка автоматически добавлялась при добавлении элемента в коллекцию, я не уверен, как вы может получить позицию ранее добавленного элемента, чтобы строка начиналась в нужном месте.

Один из возможных вариантов работы - подкласс Canvas и значение ItemsPanelTemplate для ListBox. Я считаю, что у него есть функция, которую вы можете переопределить, которая вызывается всякий раз, когда к ней добавляется элемент управления. В этой функции вы можете получить позицию ранее добавленного элемента управления (которая будет кэшироваться в вашем подклассе Canvas), а затем добавить элемент управления строкой к элементам управления Canvas напрямую. Это предполагает, что положение элементов управления в Canvas не может измениться. Если это возможно, вам, вероятно, придется подписаться на событие PropertyChanged объекта и соответствующим образом обновить строки.

Редактировать 3 : Поскольку привязка не выглядит как вариант из-за ваших требований, наименее плохой вариант - просто добавить дочерние элементы напрямую. Это можно сделать, выполнив поиск по визуальному дереву с помощью VisualTreeHelper.GetChild () :

private T FindChild<T>(FrameworkElement parent, string name)
    where T : FrameworkElement
{
    FrameworkElement curObject = null;
    T obj = default(T);
    int count = VisualTreeHelper.GetChildrenCount(parent);
    for(int i = 0; i < count; i++)
    {
        curObject = VisualTreeHelper.GetChild(parent, i) as FrameworkElement;
        obj = curObject as T;
        if(null != obj && obj.Name.Equals(name))
        {
            break;
        }

        obj = FindChild<T>(curObject, name);
        if(null != obj)
        {
            break;
        }
    }

    return obj;
}

Используйте это так:

Canvas canvas = FindChild<Canvas>(lstAwesome, "cnvAwesome");

Этот код рекурсивно ищет в визуальном дереве parent элемент управления указанного типа и имени. Здесь было бы лучше использовать DependencyObject сверх FrameworkElement (поскольку именно это возвращает VisualTreeHelper.GetChild()), однако Name определяется только для FrameworkElement.

Если вы хотите получить фантазию, вы даже можете сделать это методом расширения.

1 голос
/ 28 мая 2009

Вы определяете способ отображения ваших предметов, устанавливая ItemTemplate, например:

<ListBox.ItemTemplate>
    <DataTemplate>
        <Rectangle Width="{Binding Width}" Height="{Binding Height}" />
    </DataTemplate>
</ListBox.ItemTemplate>

Если вы хотите, чтобы ваш ItemTemplate был изменен в зависимости от вашего элемента, вы можете установить ItemTemplateSelector и решить, какой ItemTemplate будет использоваться программно.

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