为什么每次双击都会多次引发TreeViewItem的MouseDoubleClick事件?

Gis*_*shu 21 wpf treeview mouseevent treeviewitem

XAML

<TreeView Name="GroupView" ItemsSource="{Binding Documents}">
            <TreeView.ItemContainerStyle>
                <Style TargetType="{x:Type TreeViewItem}">
                    <EventSetter Event="MouseDoubleClick" Handler="OnTreeNodeDoubleClick"/>
                </Style>
            </TreeView.ItemContainerStyle>
            ....
</TreeView>
Run Code Online (Sandbox Code Playgroud)

代码隐藏

private void OnTreeNodeDoubleClick(object sender, MouseButtonEventArgs mouseEvtArgs)
       {
           Console.WriteLine("{3} MouseDoubleClick Clicks={0} ChangedButton={1} Source={2} Handled={4} ButtonState={5}",
               mouseEvtArgs.ClickCount, mouseEvtArgs.ChangedButton, mouseEvtArgs.OriginalSource,
               mouseEvtArgs.Timestamp, mouseEvtArgs.Handled, mouseEvtArgs.ButtonState);
       }
Run Code Online (Sandbox Code Playgroud)

我发现只需双击一次,就会多次调用事件处理程序.我试图在选项卡中打开一个文件,双击相应的树节点; 所以我需要过滤掉额外的电话.

23479156 MouseDoubleClick Clicks=1 ChangedButton=Left Source=System.Windows.Controls.TextBlock Handled=False ButtonState=Pressed
23479156 MouseDoubleClick Clicks=1 ChangedButton=Left Source=System.Windows.Controls.TextBlock Handled=False ButtonState=Pressed
Run Code Online (Sandbox Code Playgroud)

在我稍微复杂的应用程序中,每次双击它会被提升4次.在一个简单的repro-app上,每次双击它会被提升2次.此外,所有的事件参数参数也是相同的,所以我无法区分最后一个参数.

任何想法为什么这是它的方式?

Ria*_*aan 25

我知道这是一个老问题,但正如我在搜索解决方案中遇到的那样,以下是我对未来访客的调查结果!

TreeViewItems递归地包含在彼此之内. TreeViewItem是一个HeaderedContentControl(参见msdn),子节点为Content.因此,每个TreeViewItem边界都包含其所有子项.这可以通过在可视树中选择一个优秀的WPF检查器来验证TreeViewItem,这将突出显示的边界TreeViewItem.

在OP的示例中,使用样式MouseDoubleClick在每个事件上注册事件TreeViewItem.因此,将为TreeViewItem您双击的s及其每个父项分别引发事件.这可以通过在双击事件处理程序中放置断点并在事件args的Source属性上放置一个断点来验证调试器 - 您会注意到每次调用事件处理程序时它都会更改.顺便说一句,正如可以预料的那样,OriginalSource事件的内容保持不变.

为了应对这种意外行为,检查是否TreeViewItem选择了源,正如Pablo在他的回答中所建议的那样,对我来说是最好的.


小智 20

TreeViewItem双击a时,该项目被选为控制行为的一部分.根据具体情况,可以说:

...
TreeViewItem tviSender = sender as TreeViewItem;

if (tviSender.IsSelected)
    DoAction();
...
Run Code Online (Sandbox Code Playgroud)


Kir*_*kov 7

我做了一些调试,它似乎是WPF中的一个错误.已经给出的大多数答案都是正确的,解决方法是检查是否选择了树视图项.

@ ristogod的答案是最接近的根本问题-它提到,设置e.Handled = true在第一时间处理程序被调用并没有收到预期的效果和事件的不断冒泡,调用父TreeViewItemS'处理程序(这里e.Handledfalse再次).

该错误似乎出现在WPF中的代码中:http: //referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/Control.cs,5ed30e0aec6a58b2

它接收MouseLeftButtonDown事件(由子控件已经处理),但是它无法检查是否e.Handled已经设置为true.然后它继续创建一个新的MouseDoubleClick事件args(with e.Handled == false)并始终调用它.

问题也仍然是为什么在第一次事件继续泡沫之后将其设置为处理?因为在这一行中,当我们注册处理程序时Control.HandleDoubleClick:http: //referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/Control.cs,40

我们通过真正作为最后一个参数RegisterClassHandler: http://referencesource.microsoft.com/#PresentationCore/Core/CSharp/System/Windows/EventManager.cs,161 这是handledEventsToo.

因此,不幸的行为是两个因素的汇合:

  1. Control.HandleDoubleClick 总是被调用(对于处理过的事件),和
  2. Control.HandleDoubleClick 未能检查事件是否已被处理

我将通知WPF团队,但我不确定这个bug是否值得修复,因为它可能会破坏现有的应用程序(即使Handled被前一个处理程序设置为true ,依赖于事件处理程序的当前行为).


小智 6

private void TreeView_OnItemMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
    if (e.Source is TreeViewItem
        && (e.Source as TreeViewItem).IsSelected)
    {
        // your code
        e.Handled = true;
   }
}
Run Code Online (Sandbox Code Playgroud)


Joh*_*ler -1

最可能的原因是双击处理程序安装了多次,因此每次单击都会调用处理程序的每个实例一次。