Динамически добавлять 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,

Другие вопросы по тегам