2010-02-17 94 views
18

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

<TreeView Name="GroupView" ItemsSource="{Binding Documents}"> 
      <TreeView.ItemContainerStyle> 
       <Style TargetType="{x:Type TreeViewItem}"> 
        <EventSetter Event="MouseDoubleClick" Handler="OnTreeNodeDoubleClick"/> 
       </Style> 
      </TreeView.ItemContainerStyle> 
      .... 
</TreeView> 

代码隐藏

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); 
     } 

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

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 

在我这个稍微复杂的应用程序中,每双击一次就会产生4次。在一个简单的repro-app上,它每双击两次就会被提升。而且所有的事件参数参数也是一样的,所以我不能区分一组中的最后一个。

任何想法,为什么这是现在这个样子?

+0

您是否在UpdatePanel中使用了treeview? – Kangkan 2010-02-17 11:22:48

+0

@Kangkan:不,这不是一个网络应用程序;一个简单的桌面应用。 – Gishu 2010-02-17 11:58:37

+1

我曾经有过同样的问题,从来没有想过它。我在treeview(而不是treeviewitems)上安装了doubleclick事件处理程序,并使用了selecteditem属性... – 2011-04-01 23:17:07

回答

-1

最可能的原因是,双击处理程序多次安装,这样处理程序的每个实例正在为每次点击调用一次。

+0

@John - 在所有文件中找到了一个,在OnTreeNodeDoubleClick中提到的唯一位置是样式定义 – Gishu 2010-02-17 11:21:41

0

这是事件冒泡的精彩世界。该事件冒泡了TreeView的节点层次结构,并且您的处理程序对于层次结构路径中的每个节点都会调用一次。

只要使用类似

 // ... 
     if (sender != this) 
     { 
      return; 
     } 
     // Your handler code goes here ... 
     args.Handled = true; 
     // ... 

在你的处理器的代码。

+0

Bubbling会导致用户界面元素层次结构的不同处理程序被调用。它应该**不会导致每个事件多次调用相同的处理程序。 – Gishu 2010-09-23 07:56:56

+0

您的相同处理程序参数的另一件事。无论您订阅哪个事件,发件人始终都是TreeView。真正的发件人隐藏在RoutedEvent.OriginalSource参数中。根据事件有时它是TreeViewItem,但对于鼠标事件,它是在用户点击的TreeViewItem的(分层)DataTemplate中定义的控件类型。可能MS没有覆盖FrameworkElement实现。 – banzai 2010-09-23 12:02:08

4

这并不是一个真正的起泡问题。我以前见过这个。即使你告诉事件,你处理它,它仍然继续冒泡。除此之外,我不认为它实际上是在冒泡,而是将节点放在自己的双击事件之上。我可能是完全错误的。但无论如何,重要的是要知道说:

e.handled = true; 

没有什么阻止这种情况发生。为防止这种行为

一种方法是要注意,当你双击,你是第一个点击和所选择的事件应该首先开火。因此,虽然无法停止发生双击事件,但您应该能够在处理程序内部检查事件逻辑是否应该运行。这个例子利用的是:

TreeViewItem selectedNode; 

private void MouseDoubleClickEventHandler(object sender, MouseButtonEventArgs e) 
{ 
    if(selectedNode = e.Source) 
    { 
     //do event logic 
    } 
} 

private void TreeViewSelectedEventHandler(object sender, RoutedEventArgs e) 
{ 
    selectedNode = (TreeViewItem)e.Source; 
} 

但有时候你有其中的节点被其他豆类不是通过树视图SelectedItemChanged事件选择的情况。在这种情况下,你可以做这样的事情。如果你碰巧有一个声明顶级节点一个TreeView,你可以给该节点一个特定的名称,然后做这样的事情:

bool TreeViewItemDoubleClickhandled; 

private void MouseDoubleClickEventHandler(object sender, MouseButtonEventArgs e) 
{ 
    if (!TreeViewItemDoubleClickhandled) 
    { 
     //do logic here 

     TreeViewItemDoubleClickhandled = true; 
    } 

    if (e.Source == tviLoadTreeTop) 
    { 
     TreeViewItemDoubleClickhandled = false; 
    } 
    e.Handled = true; 
} 

无论使用哪种方法,重要的是要注意,无论出于什么原因与TreeViewItem双击,你不能阻止事件发射树。至少我没有找到办法。

+0

现货!将e.Handled设置为true并没有预期的效果是WPF中的一个错误。我将添加我自己的答案和更多细节。 – 2016-03-27 05:26:43

15

TreeViewItem被双击,该项目被选择作为控制行为的一部分。根据特定的情况下它可能是可以说:

... 
TreeViewItem tviSender = sender as TreeViewItem; 

if (tviSender.IsSelected) 
    DoAction(); 
... 
20

我知道这是一个老问题,但正如我在我的搜索碰到它的解决方案,这里是我的发现为今后的任何访问者!

TreeViewItem s被递归地包含在彼此之内。 TreeViewItemHeaderedContentControl(参见msdn),其子节点为Content。因此,每个TreeViewItem的边界都包含其所有子项。这可以通过在视觉树中选择TreeViewItem使用优秀的WPF Inspector来验证,这将突出显示TreeViewItem的界限。

在OP的示例中,MouseDoubleClick事件使用样式在每个TreeViewItem上注册。因此,该事件将针对您双击的TreeViewItem以及其每个父项分别进行。这可以在调试器中验证,方法是在双击事件处理程序中放置一个断点并在事件参数的Source属性中添加一个监视 - 您会注意到每次调用事件处理函数时它都会更改。顺便提一下,可以预料,该活动的OriginalSource保持不变。

为了应对这种意外的行为,按照Pablo在他的回答中所述,检查源TreeViewItem是否被选中,对我来说是最好的。

6
private void TreeView_OnItemMouseDoubleClick(object sender, MouseButtonEventArgs e) 
{ 
    if (e.Source is TreeViewItem 
     && (e.Source as TreeViewItem).IsSelected) 
    { 
     // your code 
     e.Handled = true; 
    } 
} 
1

我比检查选择或创建国旗多一点优雅的解决方案:

的helper方法:

public static object GetParent(this DependencyObject obj, Type expectedType) { 

    var parent = VisualTreeHelper.GetParent(obj); 
    while (parent != null && parent.GetType() != expectedType) 
     parent = VisualTreeHelper.GetParent(parent); 

    return parent; 
} 

然后你的处理程序:

+0

您应该指定'OriginalSource'是纯粹的命中测试确定的原始报告源,所以如果事件发送者与父节点'TreeViewItem'相同,我们就直接点击节点。 – Maxence 2017-04-07 08:11:22

7

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

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

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

它接收MouseLeftButtonDown事件(这是由子控件已处理),但它没有检查是否e.Handled已被设置为true。然后继续创建一个新的MouseDoubleClick事件参数(使用e.Handled == false)并始终调用该参数。

这个问题仍然是为什么在设置它处理第一次事件继续冒泡之后呢?因为在这一行,当我们注册的处理程序Control.HandleDoubleClickhttp://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/Control.cs,40

我们作为最后一个参数传递真实的RegisterClassHandlerhttp://referencesource.microsoft.com/#PresentationCore/Core/CSharp/System/Windows/EventManager.cs,161 这是handledEventsToo

所以不幸的行为是两个因素汇合:

  1. Control.HandleDoubleClick总是被调用(用于处理事件来说),以及
  2. Control.HandleDoubleClick未能检查事件已经处理

我会通知WPF团队,但我不确定这个bug是否值得修复,因为它可能会破坏现有的应用程序(即使Handled是se依赖于当前正在调用的事件处理程序的行为t由以前的处理程序为真)。

0

该解决方案存在一些相当主要的问题,但它可以在需要在多个地方解决此问题的情况下工作,并且我确实发现了接受的解决方案无法工作的情况(双击切换按钮打开一个弹出窗口,其中切换按钮是处理双击另一元素中)

public class DoubleClickEventHandlingTool 

{ 私人常量字符串DoubleClickEventHandled =“DoubleClickEventHandled”。

public static void HandleDoubleClickEvent() 
{ 
    Application.Current.Properties[DoubleClickEventHandled] = DateTime.Now.AddSeconds(1); 
} 

public static bool IsDoubleClickEventHandled() 
{ 
    var doubleClickWasHandled = Application.Current.Properties[DoubleClickEventHandled] as DateTime?; 

    return doubleClickWasHandled.HasValue && !IsDateTimeExpired(doubleClickWasHandled.Value); 
} 

private static bool IsDateTimeExpired(DateTime value) 
{ 
    return value < DateTime.Now; 
} 

public static void EnableDoubleClickHandling() 
{ 
    Application.Current.Properties[DoubleClickEventHandled] = null; 
} 

public static bool IsDoubleClickEventHandledAndEnableHandling() 
{ 
    var handled = IsDoubleClickEventHandled(); 
    EnableDoubleClickHandling(); 

    return handled; 
} 

}

使用DoubleClickEventHandlingTool.HandleDoubleClickEvent() 内/低级别的元素如内:

private void OnPreviewMouseDown(object sender, MouseButtonEventArgs e) 
{if (e.ClickCount == 2) DoubleClickEventHandlingTool.HandleDoubleClickEvent();} 
然后

高层双击事件只执行它的操作时:

if (!DoubleClickEventHandlingTool.IsDoubleClickEventHandledAndEnableHandling()) 
相关问题