在运行时将 EventSetter 动态添加到现有分层数据模板 [英] Add EventSetter dynamically on runtime to existing Hierarchical Data Template
问题描述
我有一个 WPF TreeView
控件,它通过绑定获取分层数据.为了控制控件中的视觉输出,我使用 Hierarchical Data Templates
.TreeView
的DataContext
是一个自定义类的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
类可以容纳 Palette
和 PaletteGroup
类型的孩子,我使用 CompositeCollection
来组合ObservableCollection
在 TreeView
中的视觉输出的一个层次结构中,因为我的类,可以有你想要的多个子节点级别.
As the PaletteGroup
class can hold childrens of type Palette
and PaletteGroup
, I use a CompositeCollection
to combine both ObservableCollection
s 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 EventSetter
s and apply the new style to the Control
.
这篇关于在运行时将 EventSetter 动态添加到现有分层数据模板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!