WPF TreeViewItem鼠标双击

4
我有困难识别选中的TreeViewItem名称;我在TreeView中有以下结构:
 + Module 1 (name k1)
   - Sub Module 1 (name s1)
 + Module 2 (name k2)
   - Sub Module 2 (name s2)
 + Module 3 Name (k3)
   - Sub Module 3 (name s3)
   - Sub Module 4 (name s4)

我的 XAML 代码如下:

<TreeView x:Name="treview_Menu">
   <TreeView.ItemContainerStyle>
      <Style TargetType="{x:Type TreeViewItem}">
         <EventSetter Event="MouseDoubleClick" Handler="OnItemMouseDoubleClick" />
      </Style>
   </TreeView.ItemContainerStyle>
</TreeView>

在代码中我有这个:
Private Sub OnItemMouseDoubleClick(sender As Object, args As MouseButtonEventArgs)
   MsgBox(sender.name)
End Sub

在上述代码中,始终返回顶部节点的名称。例如,如果我双击子模块3或子模块4,则始终返回Module_3的名称,对于子模块1和子模块2也是如此...我该如何解决这个问题?
1个回答

5
我认为问题在于ItemContainerStyle仅适用于第一级节点(我认为这是由于TreeViewItemsControl继承的方式以及ItemContainerStyle从那里继承 - 它只适用于ItemsControl的基本Items)。无论如何,我离题了...

您可以通过将样式分配给Resources而不是ItemContainerStyle来解决它,像这样:

<TreeView.Resources>
    <Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource {x:Type TreeViewItem}}">
        <EventSetter Event="MouseDoubleClick" Handler="OnItemMouseDoubleClick"/>
    </Style>
</TreeView.Resources>

根据您的需求,这可能不会按照您的意图工作...

因为这将向每个TreeViewItem添加一个触发器 - 不幸的是,它们像这样嵌套:

        <TreeViewItem Header="Module 1">
            <TreeViewItem Header="Sub Module 1"/>
        </TreeViewItem>
        <TreeViewItem Header="Module 2">
            <TreeViewItem Header="Sub Module 1"/>
            <TreeViewItem Header="Sub Module 2"/>
        </TreeViewItem>

因此,根据您实际使用代码的目的,事件在外部和内部模块上触发可能是一个问题。

ClickEvent中处理这种情况的常见方法是在代码中将args.Handled设置为True,这会阻止事件向更高级别冒泡。但不幸的是,由于它们被触发的方式,这在MouseDoubleClick事件上不起作用。(微软真是个好东西...xD)

一个可能的解决方案在这里:https://dev59.com/ym015IYBdhLWcg3w_Qw_#6326181

基本上,我们放弃MouseDoubleClick事件,而是使用PreviewMouseLeftButtonDown事件(显然Microsoft使用它来触发MouseDoubleClick事件)。无论如何,我们可以像这样使其触发一次:

(请原谅我的C#,我试图从wpf角度回答这个问题,但我不知道等效的VB代码)

OnItemPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)   {
    if (e.ClickCount == 2) {
        e.Handled = true;
        var treeViewItem = sender as TreeViewItem;
        MessageBox.Show(treeViewItem.Header.ToString());
    }
}

然而,由于预览事件向下隧道传输,它不幸地在包含项上触发一次,而不是包含的项,所以我们回到了原点。我们可以在这里查看args.OriginalSource,但我们基本上已经到了需要另一种解决方案的阶段:
分离式备选方案
另一个选择是仅在双击整个TreeView时添加事件监听器,然后简单地找到其OriginalSource位于哪个TreeViewItem中。(如果您想处理它并阻止其传播/导致其他影响,则需要像上面那样挂钩PreviewMouseLeftButtonDown事件)。
为此,您可以获取MouseButtonEventArgs args并考虑args.OriginalSource(这是您单击的可视树上的最上层元素),然后有几种情况:它根本不在任何TreeViewItem内,或者它本身就是您需要的TreeViewItem,第三种可能性是OriginalSource是您需要的TreeViewItem内的某个元素(这是最可能的情况-从测试中,您通常单击包含在TreeViewItem内的),这样您就可以递归地查找父级,直到命中第一个类型为TreeViewItem的父级。(您可以使用VisualTreeHelper GetParent方法实现此目的。)
基本上,以上算法可以总结为只需递归检查当前项是否为TreeViewItemTreeView类型即可,否则将当前项设置为其父项并递归...如果您停在TreeViewItem上,则拥有所需的项,否则如果您单击了项目外部,则最终会停留在TreeView上。

PS: 由于我不会写VB,所以我认为解释上述算法可能比尝试编写它更好 - 但我希望上述解释对您仍然有用 :)


你好,感谢帮助,它起作用了,但是因为我的控件样式被改变了(样式元素树视图丢失)? - aprendiz123456789
不太明白你的意思?(例如(样式元素树视图丢失)?) 如果您在上面发布您的样式和所需效果,我可能能够帮助 :) - David E
我目前正在使用MahApps.Metro,这里有一张没有单击事件的图片和另一张带有更改产品样式的单击事件图片:图片1(http://i.imgur.com/pedXtEI.jpg)和图片2(http://i.imgur.com/hvwP7uV.jpg)。 - aprendiz123456789
啊,好的,抱歉!问题在于我们无意中在本地覆盖了您的自定义样式,但我们希望在其基础上进行构建。因此,请尝试将定义样式的行更改为:<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource {x:Type TreeViewItem}}">,并告诉我结果如何。 - David E
工作正常!谢谢你们所有人 =D 我会为你们点一支蜡烛祈祷的 =D - aprendiz123456789
真的不可思议,微软竟然让这么受欢迎的需求变得如此困难。 - cheny

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接