2014-02-20 25 views
2

我想确定我明白事件是如何传播的。以下是否正确?事件如何在wpf中传播?

例如,让我们来看看在按钮内点击鼠标左键时如何调用按钮的Click事件。

按钮注册Click事件:

public class Button : ButtonBase 
{ 
    public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent("Click", 
     RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Button)); 

    public event RoutedEventHandler Click 
    { 
    add { AddHandler(ClickEvent, value); } 
    remove { RemoveHandler(ClickEvent, value); } 
    } 

    protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) 
    { 
    ...  
    RaiseEvent(new RoutedEventArgs(ClickEvent, this)); 
    ... 
    } 
    ... 
} 
  • EventManager.RegisterRoutedEvent创建一个名为Click路由事件并将其添加到按钮的事件处理程序的集合称为EventHandlersStore。我相信这个系列(我们称之为_routedEvents)的类型与Dictionary<RoutedEvent, RoutedEventHandler>类似。所以,RegisterRoutedEvent确实_routedEvents.Add(ClickEvent, null)
  • AddHandler增加处理程序到ClickEvent条目EventHandlersStore。如果没有人订阅Click事件,则ClickEvent的处理程序仍为null

现在,当RaiseEvent被调用,在OnMouseLeftButtonDown,这是发生了什么,以及如何将事件根据我的理解路由:

void RaiseEvent(RoutedEventArgs e) 
{   
    DependencyObject current = this;  
    do 
    { 
     // check if the element has handler for routed event 
     var handler = current.GetHandlerFromEventHandlersStore(e.RoutedEvent); 
     if (handler != null) 
     { 
      handler(e); 
     } 

     // the event was NOT handled -> route the event to the parent 
     // OR 
     // the event was handled but wasn't marked as handled -> route the event further to parent 
     if (e.Handled == false) 
     { 
      // assuming that RoutingStrategy is Bubble 
      current = VisualTreeHelper.GetParent(current); 
     } 

    // continue until either it has been handled or it reaches the root element 
    } while (e.Handled == false && current != null); 
} 

,我会很感激,如果,如果我有人可以指正米错了,也告诉我如何调用OnMouseLeftButtonDown(我无法在resharper中找到它)

回答

2

RoutedEvents工作有点像这样。你给我们的例子很好的演示了如何基本routedevent路由。

然而还有更多的内幕。它并不那么简单。我会举几个例子。

当触发事件时,可能会在LogicalTree和VisualTree之间切换以查找其路径,因为路径可能是通过未从FrameworkElement继承的数据定义的,或者甚至不是Visual。在这种情况下,您可以使用隧道策略来颠倒路径,否则路由事件默认会冒泡。即使事件处理状态为真,也可以访问路由节点。事件的参数不仅仅包含e.Source,e.OriginalSource和e.Handled。该参数包含有关上一个和下一个节点的信息。另外,args可能包含另一个routedevent的列表,一旦某个节点到达,它将被触发。

没有属于某个路由事件的所有可用处理程序的大表。查找处理程序也是一段时间内发生的逻辑,因为您可以动态更改模板或布局面板。

正如你可以看到有像10十亿别人的东西在内部和相当复杂的:)

你举的例子是一个很好的演示发生的事情,不要担心你有routedevents权的基本知识。无论如何,你不需要知道所有这些内部事物。只要它有效...

1

逻辑实际上有点不同于你的建议。例如,即使e.Handled已设置为true,您仍将继续进行隧道或冒泡。但是,您只会调用注册为handledEventsToo = true的处理程序。

每个节点还有多个处理程序。可能有类处理程序,用EventManager.RegisterClassHandler()注册,它们实际上是由给定元素类型的所有实例共享的静态处理程序。然后是常规实例处理程序,使用元素的AddHandler()方法注册。

事件传播逻辑看起来是这样的(伪代码):

void RaiseEvent(RoutedEventArgs e) { 
    EventRoute route = BuildEventRoute(e); 
    RouteNode currentNode = route.Head; 

    while (currentNode != null) { 
     DependencyObject current = currentNode.Element; 

     // Update the Source of the RoutedEventArgs (OriginalSource remains the same). 
     e.Source = current; 

     // Invoke class handlers for current node, if any exist. 
     foreach (var handler in GetClassHandlers(e)) { 
      if (!e.Handled || handler.HandledEventsToo) { 
       handler.Invoke(e); 
      } 
     } 

     // Invoke instance handlers for current node, if any exist. 
     foreach (var handler in GetInstanceHandlers(e)) { 
      if (!e.Handled || handler.HandledEventsToo) { 
       handler.Invoke(e); 
      } 
     } 

     currentNode = currentNode.Next; 
     // Continue until we reach the end of the route. 
    } 
} 

途径取决于该事件策略路由是否BubbleTunnel,或Direct。实质上,路由是通过从源元素开始并确定其父元素,然后填充两个元素之间的任何中间节点并继续向上构建的。隧道事件的路线简单颠倒。

正如@devhedgehog在他的回答中指出的那样,该过程并不严格遵循视觉或逻辑树;概念父母可以是视觉父母,逻辑父母,内容父母等等。它可以在2D和3D空间之间交叉,从文档可视树等等。