WPF: добавление UIElements в ListBox, у которого ItemsPanelTemplate является холстом?
Я работаю над 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 ответа
Я думаю, что у вас есть все в вашем вопросе, чтобы добавить элементы в Canvas
вашей ListBox
, Я просто собрал простое тестовое приложение WPF, используя код в вашем вопросе, и это просто сработало. Всякий раз, когда я добавил предметы в ObservableCollection
это связано с ItemsSource
из ListBox
WPF создаст ListBoxItem
для этого объекта, и в зависимости от стиля ListBoxItem
элемент будет нарисован в правильном месте Canvas
,
Вы не смогли заставить ваш код работать выше? Если это так, какую ошибку вы получили или что не сработало, как вы ожидали?
Изменить: После прочтения вопроса снова, я думаю, что путаница из-за неправильного понимания того, как работает привязка WPF. Когда вы связываете коллекцию с ListBox
"s ItemsSource
все, что вам нужно сделать, это добавить элементы в эту коллекцию (при условии, что коллекция реализует 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
,
Если вы хотите стать модным, вы даже можете сделать это методом расширения.
Вы определяете способ отображения ваших предметов, устанавливая ItemTemplate
, лайк:
<ListBox.ItemTemplate>
<DataTemplate>
<Rectangle Width="{Binding Width}" Height="{Binding Height}" />
</DataTemplate>
</ListBox.ItemTemplate>
Если вы хотите, чтобы ваш ItemTemplate
быть измененным в зависимости от вашего элемента, то вы можете установить ItemTemplateSelector
и решить, какой ItemTemplate
использоваться программно.