Динамически добавлять EventSetter во время выполнения к существующему шаблону иерархических данных
У меня есть WPF TreeView
контроль, который получает иерархические данные путем привязки. Для контроля визуального вывода в элементе управления я использую Hierarchical Data Templates
, DataContext
из TreeView
является ObservableCollection
пользовательского класса, который может содержать различные типы дочерних типов.
public class PaletteGroup
{
public string Name { get; set; }
public ObservableCollection<Palette> Palettes { get; set; }
public ObservableCollection<PaletteGroup> PaletteGroups { get; set; }
public IList Children
{
get
{
return new CompositeCollection()
{
new CollectionContainer() { Collection = Palettes },
new CollectionContainer() { Collection = PaletteGroups }
};
}
}
}
public class Palette
{
public string Name { get; set; }
}
Как PaletteGroup
класс может содержать детей типа Palette
а также PaletteGroup
Я использую CompositeCollection
объединить оба ObservableCollection
в одной иерархии для визуального вывода в TreeView
Из-за моих классов возможно иметь столько уровней подузлов, сколько вы хотите.
Сам визуальный вывод определен в моем файле xaml, где я использую Name
Свойство двух классов для отображения имени объекта:
<local:DragDropDecorator AllowDrop="True"
AllowPaletteItems="False"
AllowPaletteGroups="True"
AllowPalettes="True">
<TreeView Margin="10,10,10,40"
Name="PaletteStructureView"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"
MouseRightButtonUp="PalettesListBoxMouseRightButtonUp"
ItemsSource="{Binding LoadedPaletteGroups}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:PaletteGroup}"
ItemsSource="{Binding Children}">
<TextBlock Foreground="DarkGreen"
Text="{Binding Path=Name}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Palette}">
<TextBlock Foreground="DarkBlue"
Text="{Binding Path=Name}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</local:DragDropDecorator>
Как вы можете видеть, я также завернул TreeView
управление в пользовательском классе для операций перетаскивания DragDropDecorator
, где я добавляю все необходимые события для элементов управления во время выполнения. Поскольку я использую много разных элементов управления, я устал от привязки событий к элементам управления в файле xaml. Loaded
Событие этого класса выглядит так:
private void DragableItemsControl_Loaded( object sender, RoutedEventArgs e )
{
if (!(base.DecoratedUIElement is ItemsControl))
throw new InvalidCastException(string.Format("DragDragDecorator cannot have child of type {0}", Child.GetType()));
ItemsControl itemsControl = (ItemsControl)DecoratedUIElement;
itemsControl.AllowDrop = AllowDrop;
itemsControl.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(ItemsControl_PreviewMouseLeftButtonDown);
itemsControl.PreviewMouseMove += new MouseEventHandler(ItemsControl_PreviewMouseMove);
itemsControl.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(ItemsControl_PreviewMouseLeftButtonUp);
itemsControl.PreviewDrop += new DragEventHandler(ItemsControl_PreviewDrop);
itemsControl.PreviewQueryContinueDrag += new QueryContinueDragEventHandler(ItemsControl_PreviewQueryContinueDrag);
itemsControl.PreviewDragEnter += new DragEventHandler(ItemsControl_PreviewDragEnter);
itemsControl.PreviewDragOver += new DragEventHandler(ItemsControl_PreviewDragOver);
itemsControl.DragLeave += new DragEventHandler(ItemsControl_DragLeave);
}
Это абсолютно нормально работает для ListBox
контроль, который также был необходим для моего проекта. Но мне довольно тяжело с TreeView
контроль, так как события возникают только для самого верхнего узла в TreeView
даже если я попробую операции перетаскивания на некоторых дочерних элементах.
Сначала я попытался добавить все события в TreeView.ItemContainerStyle
, Это прекрасно работает для первого уровня подузлов, но игнорирует более глубокие структуры узлов, а также самые верхние узлы.
Затем я попытался добавить все события в Hierarchical Data Template
в Loaded
событие DragDropDecorator
учебный класс:
if (itemsControl.GetType() == typeof(TreeView))
{
foreach (object item in itemsControl.Resources.Keys)
{
var hdt = itemsControl.FindResource(item);
if (hdt != null & hdt.GetType() == typeof(HierarchicalDataTemplate))
{
var newHdt = (HierarchicalDataTemplate)hdt;
var test = new Style();
test.Setters.Add(new EventSetter(PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(ItemsControl_PreviewMouseLeftButtonDown)));
test.Setters.Add(new EventSetter(PreviewMouseMoveEvent, new MouseEventHandler(ItemsControl_PreviewMouseMove)));
test.Setters.Add(new EventSetter(PreviewMouseLeftButtonUpEvent, new MouseButtonEventHandler(ItemsControl_PreviewMouseLeftButtonUp)));
test.Setters.Add(new EventSetter(PreviewDropEvent, new DragEventHandler(ItemsControl_PreviewDrop)));
test.Setters.Add(new EventSetter(PreviewQueryContinueDragEvent, new QueryContinueDragEventHandler(ItemsControl_PreviewQueryContinueDrag)));
test.Setters.Add(new EventSetter(PreviewDragEnterEvent, new DragEventHandler(ItemsControl_PreviewDragEnter)));
test.Setters.Add(new EventSetter(PreviewDragOverEvent, new DragEventHandler(ItemsControl_PreviewDragOver)));
test.Setters.Add(new EventSetter(DragLeaveEvent, new DragEventHandler(ItemsControl_DragLeave)));
newHdt.ItemContainerStyle = test;
}
}
}
С этим кодом я получаю InvalidOperationException
из-за уже запечатанного объекта шаблона.
Итак, мои вопросы:
- Как я могу добавить EventSetters к уже существующему
Hierarchical Data Template
во время выполнения? - Это правильный способ сделать это, или у меня есть другие варианты, чтобы сделать это более элегантным?
После нескольких часов попыток различных методов и поиска решения в интернете я застрял сейчас. Я был бы признателен, если бы кто-нибудь указал мне верное направление или даже написал небольшой фрагмент кода, который должен помочь мне вернуться на правильный путь.
Я надеюсь, что код, который я разместил, достаточен. Если нет, просто оставьте комментарий, и я добавлю дополнительные части.
Спасибо заранее и за ваше время!
1 ответ
Я сам исправил проблему и хотел бы разместить здесь код для дальнейшего использования. Может быть, это не лучшее решение, но оно работает для меня. Я добавил следующие строки в Loaded
событие DragDropDecorator
учебный класс:
if (itemsControl.GetType() == typeof(TreeView))
{
var originalStyle = itemsControl.Style;
var newStyle = new Style();
newStyle.BasedOn = originalStyle;
newStyle.Setters.Add(new EventSetter(PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(ItemsControl_PreviewMouseLeftButtonDown)));
newStyle.Setters.Add(new EventSetter(PreviewMouseMoveEvent, new MouseEventHandler(ItemsControl_PreviewMouseMove)));
newStyle.Setters.Add(new EventSetter(PreviewMouseLeftButtonUpEvent, new MouseButtonEventHandler(ItemsControl_PreviewMouseLeftButtonUp)));
newStyle.Setters.Add(new EventSetter(PreviewDropEvent, new DragEventHandler(ItemsControl_PreviewDrop)));
newStyle.Setters.Add(new EventSetter(PreviewQueryContinueDragEvent, new QueryContinueDragEventHandler(ItemsControl_PreviewQueryContinueDrag)));
newStyle.Setters.Add(new EventSetter(PreviewDragEnterEvent, new DragEventHandler(ItemsControl_PreviewDragEnter)));
newStyle.Setters.Add(new EventSetter(PreviewDragOverEvent, new DragEventHandler(ItemsControl_PreviewDragOver)));
newStyle.Setters.Add(new EventSetter(DragLeaveEvent, new DragEventHandler(ItemsControl_DragLeave)));
itemsControl.ItemContainerStyle = newStyle;
}
Я не смог отредактировать стиль, так как он запечатан, как только он установлен. Так что я использовал BasedOn
свойство для нового объекта стиля, чтобы получить уже установленную информацию о стиле, добавьте мой EventSetter
и применить новый стиль к Control
,