0

我有一个 WPFTreeView控件,它通过绑定获取分层数据。为了控制我使用的控件中的视觉输出Hierarchical Data TemplatesDataContextof the是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我使用 aCompositeCollection将两个ObservableCollections 组合在一个层次结构中以用于 中的视觉输出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 TemplateLoaded事件中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的运行时?
  • 这是正确的方法吗,还是我有其他选择可以使它更优雅?

经过数小时尝试不同的方法并在互联网上搜索解决方案后,我现在陷入困境。如果有人能指出我正确的方向,或者甚至给我写一个小代码片段,我将不胜感激,这应该可以帮助我回到正轨。

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

提前感谢您的时间!

4

1 回答 1

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的 s 并将新样式应用于Control.

于 2016-11-07T09:34:08.277 回答