在运行时将 EventSetter 动态添加到现有分层数据模板 [英] Add EventSetter dynamically on runtime to existing Hierarchical Data Template

查看:25
本文介绍了在运行时将 EventSetter 动态添加到现有分层数据模板的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 WPF TreeView 控件,它通过绑定获取分层数据.为了控制控件中的视觉输出,我使用 Hierarchical Data Templates.TreeViewDataContext 是一个自定义类的ObservableCollection,可以容纳不同类型的子类型.

I have a WPF TreeView control, which gets hierarchical data by binding. To control the visual output in the control I use Hierarchical Data Templates. The DataContext of the TreeView is an ObservableCollection of a custom class which can hold different kind of children types.

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 类可以容纳 PalettePaletteGroup 类型的孩子,我使用 CompositeCollection 来组合ObservableCollectionTreeView 中的视觉输出的一个层次结构中,因为我的类,可以有你想要的多个子节点级别.

As the PaletteGroup class can hold childrens of type Palette and PaletteGroup, I use a CompositeCollection to combine both ObservableCollections in one hierarchy for the visual output in the TreeView, because of my classes it is possible to have as many subnode levels you want.

视觉输出本身是在我的xaml文件中定义的,这里我使用了两个类的Name属性来显示对象的名称:

The visual output itself is defined in my xaml file, where I use the Name property of the two classes to show the name of the object:

<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 事件如下所示:

As you can see, I also wrapped the TreeView control in a custom class for Drag&Drop operations called DragDropDecorator, where I add all the necessary events for the controls on runtime. As I use a lot of different controls, I got tired of always binding the events to the controls in the xaml file. The Loaded event of this class looks like this:

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 中最上面的节点引发在一些孩子身上.

This is absolutely working fine for ListBox controls, which was also a need for my project. But I have quite a hard time with the TreeView control, as the events are only raised for the upper most node in the TreeView, even if I try the Drag&Drop operations on some children.

首先,我尝试将所有事件添加到 TreeView.ItemContainerStyle.这适用于第一级子节点,但忽略更深的节点结构以及最上层的节点.

First I tried to add all the events to the TreeView.ItemContainerStyle. This works fine for the first level of subnodes, but ignores deeper node structures and also the upper most nodes.

然后我尝试将所有事件添加到DragDropDecorator类的Loaded事件中的Hierarchical Data Template:

Then I tried to add all the events to the Hierarchical Data Template in the Loaded event of the DragDropDecorator class:

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 因为一个已经密封的模板对象.

With this code I get an InvalidOperationException because of an already sealed template object.

所以我的问题是:

  • 如何将 EventSetter 添加到已经存在的 分层数据运行时模板?
  • 这是正确的做法,还是我有还有其他选项可以让这更优雅吗?

经过数小时尝试不同的方法并在互联网上寻找解决方案后,我现在被困住了.如果有人能指出我正确的方向,甚至给我写一个小代码片段来帮助我回到正轨,我将不胜感激.

After hours of trying different methods and searching for a solution on the internet I am stuck now. I would appreciate it if someone can point me in the right direction or even write me a little code snippet which should help me to get back on track.

我希望我发布的代码足够了.如果没有,请发表评论,我会添加其他部分.

I hope the code I posted is sufficient. If not, just leave a comment and I will add additional parts of it.

提前感谢您的宝贵时间!

Thanks in advance and for your time!

推荐答案

我自己解决了这个问题,我想在这里发布代码以供将来参考.也许这不是最好的解决方案,但它对我有用.我在 DragDropDecorator 类的 Loaded 事件中添加了以下几行:

I fixed the problem by myself and I would like to post the code here for future reference. Maybe this is not the best solution, but it works for me. I added the following lines to the Loaded event of the DragDropDecorator class:

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.

I wasn't able to edit the style, as it gets sealed once it is set. So I used the BasedOn property on a new style object, to get the already set style information, add my EventSetters and apply the new style to the Control.

这篇关于在运行时将 EventSetter 动态添加到现有分层数据模板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆