wpf - RoutedEvent and EventManager.RegisterClassHandler

Updated: there was a mistake in my impl, I referenced the code in PresentationCore and modified my code based on the findings.

 

 

 

Routed Event is a special Event in WPF and Silverlight where the Event can traval up/down the tree, and you can have a handler to the parent on the visual tree to handle the event coming from this chidlren.

 

There are two phases of the Event, they are called the RoutingStrategy

 

 

  • Tunnel Phase / Or you can call it the capturing phase
  • Bubble Phase 

 

For more information about phase, please see the information RoutingStrategy.

 

 

First let's see the How to Register Routing Event and How we use use them 

 

Routing Event

Register and Setup the Event

 

To Register the routing event, you may call the EventManager.RegisterRoutedEvent; Below shows the examle. 

 

By convention, you should give the RoutedEvent static readonly field a name ended with the suffix Event.

 

 

 

 public class MyButtonSimple : Button
 {
    // namespace of RoutedEvent is System.Windows.
    public static readonly RoutedEvent TapEvent = EventManager.RegisterRoutedEvent("Tap", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyButtonSimple));

}

 

 

You may provide add-and-remove common language runtiem (CLR() event accessor; so that in CODE you can add/remove event handlers

 

 

    // CLR accessors
    // this is necessary if you want to manipulate the Event from the code
    public event RoutedEventHandler Tap
    {
      add { AddHandler(TapEvent, value); }
      remove { RemoveHandler(TapEvent, value); }
    }
 

 

You may add a method to raise the event, this can be used for example, exposing to the client...

 

 

    // and methods that raises teh TapEvent
    // this is to allow cilent to raise the events
    void RaiseTapEvent()
    {
      RaiseEvent(new RoutedEventArgs(MyButtonSimple.TapEvent));
      // you can try the overload to provide  a default Sender 
      //RaiseEvent(new RoutedEventArgs(MyButtonSimple.TapEvent, this));
    }
 

We may raise the event through, e.g. OnClick event (ihow to raise the event and how the event responds to the changes to depends on your needs)

 

 

 

    protected override void OnClick()
    {
      RaiseTapEvent();
      // suppress the base's Click Event
      //base.OnClik();
    }
 

 

Then comes how you use it ; 

 

Use the RoutedEvent 

Instance listeners are particular class instances/elements where one or more handlers have been attached for that routed event by a call to AddHandler. Existing WPF routed events make calls to AddHandler as part of the common language runtime (CLR) event wrapper add{} and remove{} implementations of the event, which is also how the simple XAML mechanism of attaching event handlers via an attribute syntax is enabled. Therefore even the simple XAML usage ultimately equates to an AddHandler call.

Instance Handlers

You can use the event handler by providing an handler to the instance of MyButtonSimple in Xaml, or in CLR. Or you can register an handler to the event in the parent node, which is capable of handling any events that is coming from MyButtonSimple instance, also in Xaml or in CLR.

 

 

To register an handler through an instance method/object is called the Routed Event's Instance Handlers.

 

 

 

Below shows you how to use  Instance Handlers to reponse to the custom Routed Event.

 

 

<Window x:Class="RoutedEventsTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        xmlns:custom="clr-namespace:RoutedEventsTest"
        >
    <Window.Resources>
        <Style TargetType="{x:Type custom:MyButtonSimple}">
            <Setter Property="Height" Value="20"/>
            <Setter Property="Width" Value="250"/>
            <Setter Property="HorizontalAlignment" Value="Left"/>
            <Setter Property="Background" Value="#808080"/>
        </Style>
    </Window.Resources>
    <StackPanel
        Background="LightGray"
        custom:MyButtonSimple.Tap="MainWindowTapHandler"
        >
        <custom:MyButtonSimple x:Name="mybtnSimple" Tap="TapHandler">Click to Tap Custom event</custom:MyButtonSimple>
        <custom:MyButtonSimpleDerived x:Name="mybtnSimpleDerived">Click to tap Custom Event on Derived Class</custom:MyButtonSimpleDerived>
    </StackPanel>
</Window>
 

 

and below is the code behind code, where the TapHandler and MainWindowTapHandler are defined.

 

 

namespace RoutedEventsTest
{
  /// <summary>
  /// Interaction logic for MainWindow.xaml
  /// </summary>
  public partial class MainWindow : Window
  {
    public MainWindow()
    {
      InitializeComponent();
    }

    private void TapHandler(object sender, RoutedEventArgs e)
    {
      Console.WriteLine("Instance Handler at child node");
      // if you set the Handler to true, then the Bubbling will stop
      //e.Handled = true;
    }

    private void MainWindowTapHandler(object sender, RoutedEventArgs e)
    {
      Console.WriteLine("Instance Handler at the parent node");
    }
  }
}
 

 

Class Handlers

Class listeners exist because types have called a particular EventManager API ,RegisterClassHandler, in their static constructor, or have overridden a class handler virtual method from an element base class

 

Class Handlers respond before instance handlers

On each given element node in an event route, class listeners have the opportunity to respond to the routed event before any instance listener on the element can.

 

class handlers are sometimes used to suppress routed events that a particular control class implementation does not wish to propagate further, or to provide special handling of that routed event that is a feature of the class. For instance, a class might raise its own class-specific event that contains more specifics about what some user input condition means in the context of that particular class. ...

 

To register a Class Handler, you can do is to call EventManager.RegisterClassHandler in its static constructor. 

 

 

    static MyButtonSimple()
    {
      EventManager.RegisterClassHandler(typeof(MyButtonSimple), TapEvent, new RoutedEventHandler(ClassOnTapEvent));
    }

    internal static void ClassOnTapEvent(object sender, RoutedEventArgs e)
    {
      Console.WriteLine("Class handler fire ealier than any instance handler");
    }
 

 

Class Handler Virtuals 

Let's first see what is the class handler virtuals...

 

Some elements, particularly the base elements such as UIElement, expose empty "On*Event" and "OnPreview*Event" virtual methods that correspond to their list of public routed events. These virtual methods can be overridden to implement a class handler for that routed event. The base element classes register these virtual methods as their class handler for each such routed event using RegisterClassHandler(Type, RoutedEvent, Delegate, Boolean) as described earlier. The On*Event virtual methods make it much simpler to implement class handling for the relevant routed events, without requiring special initialization in static constructors for each type. 

 

Below I will show you to implement the Class Handler Virtuals

 

First you might call EventManager.ReisterClassHandler on the Constructor; and you may provide an empty OnXXX handler with protected virtua lpattern

 

 

    public MyButtonSimple()
    {
      EventManager.RegisterClassHandler(typeof(MyButtonSimple), TapEvent, new RoutedEventHandler(OnTapEvent));
    }

    protected virtual void OnTapEvent(object sender, RoutedEventArgs e)
    { 
      // this is empty to allow dervied class to override it 
      Console.WriteLine("Class Handler Virtuals makes it much simpler to implement class handling for the relevant routed events, without requiring special initialization in static constructor for each type");
    }
 

Then you can create a derived class, which override the OnTap methods.

 

 

  public class MyButtonSimpleDerived : MyButtonSimple
  {
    protected override void OnTap(object sender, RoutedEventArgs e)
    {
      Console.WriteLine("You can override the base's OnXXXEvent");
      // Calling the base implementation is strongly recommended because the virtual method is on the base class. 
      //base.OnTapEvent(sender, e);
    }
  }

 

 

And last you add the Derived class to xam or in CLR. 

 

 

One tip on how to implement the Class Handlers Virtuals:

 

Calling the base implementation is strongly recommended because the virtual method is on the base class. The standard protected virtual pattern of calling the base implementations from each virtual essentially replaces and parallels a similar mechanism that is native to routed event class handling, whereby class handlers for all classes in a class hierarchy are called on any given instance, starting with the most-derived class' handler and continuing to the base class handler. You should only omit the base implementation call if your class has a deliberate requirement to change the base class handling logic. Whether you call the base implementation before or after your overriding code will depend on the nature of your implementation.

 

 

Order of the Class Handlers and Instance Handlers.

 

 

the order would be (giving that the routing event strategy is Bubbling)

 

 

 

  1. Class Handler
  2. Class handlers virtual
  3. Instance Handler where the Tap event is used (Child)
  4. Instance handler at the parent node (parent)

 

 

Give the code in this example, you will see the following output if you click on the mybuttonSimple

 

 

Class handler fire ealier than any instance handler

Class Handler Virtuals makes it much simpler to implement class handling for the relevant routed events, without requiring special initialization in static constructor for each type

You can override the base's OnXXXEvent

Instance Handler at child node

Instance Handler at the parent node

 

 

Do you see the problem?

 

The System.Window.UIElement impl on Class handler virtuals

 

the problem is that both the derived OnTap and the Base's OnTap are called. 

 

 

The reason is when mybuttonSimple create, MyButtonSimple.OnTap is added  as a class handler.  and when myButtonSimpleDerived is created, the MyButtonSimpleDerived is added as a class handler, and you end up have both the base and derived classes OnXXX method called. 

 

Below is the revised code after reading the Presenation's code. 

 

 

 

 

    static MyButtonSimple()
    {
      EventManager.RegisterClassHandler(typeof(MyButtonSimple), TapEvent, new RoutedEventHandler(OnTapThunk));
    }

    internal static void OnTapThunk(object sender, RoutedEventArgs e)
    {
      Console.WriteLine("Class handler fire ealier than any instance handler");
      MyButtonSimple buttonSimple = sender as MyButtonSimple;
      if (buttonSimple != null)
      {
        buttonSimple.OnTap(sender, e);
      }
    }
 

 

 

Now, the full code of MyButtonSimple and MyButtonSimpleDerived is as follow.

 

 

namespace RoutedEventsTest
{
  public class MyButtonSimple : Button
  {
    // namespace of RoutedEvent is System.Windows.

    public static readonly RoutedEvent TapEvent = EventManager.RegisterRoutedEvent("Tap", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyButtonSimple));

    // CLR accessors
    // this is necessary if you want to manipulate the Event from the code
    public event RoutedEventHandler Tap
    {
      add { AddHandler(TapEvent, value); }
      remove { RemoveHandler(TapEvent, value); }
    }

    // and methods that raises teh TapEvent
    // this is to allow cilent to raise the events
    void RaiseTapEvent()
    {
      RaiseEvent(new RoutedEventArgs(MyButtonSimple.TapEvent));
      // you can try the overload to provide  a default Sender 
      //RaiseEvent(new RoutedEventArgs(MyButtonSimple.TapEvent, this));
    }

    protected override void OnClick()
    {
      RaiseTapEvent();
      // suppress the base's Click Event
      //base.OnClik();
    }

    static MyButtonSimple()
    {
      EventManager.RegisterClassHandler(typeof(MyButtonSimple), TapEvent, new RoutedEventHandler(OnTapThunk));
    }

    internal static void OnTapThunk(object sender, RoutedEventArgs e)
    {
      Console.WriteLine("Class handler fire ealier than any instance handler");
      MyButtonSimple buttonSimple = sender as MyButtonSimple;
      if (buttonSimple != null)
      {
        buttonSimple.OnTap(sender, e);
      }
    }

    protected virtual void OnTap(object sender, RoutedEventArgs e)
    { 
      // this is empty to allow dervied class to override it 
      Console.WriteLine("Class Handler Virtuals makes it much simpler to implement class handling for the relevant routed events, without requiring special initialization in static constructor for each type");
    }

  }

  public class MyButtonSimpleDerived : MyButtonSimple
  {
    protected override void OnTap(object sender, RoutedEventArgs e)
    {
      Console.WriteLine("You can override the base's OnXXXEvent");
      // Calling the base implementation is strongly recommended because the virtual method is on the base class. 
      //base.OnTapEvent(sender, e);
    }
  }
}
 

 

 

References:

 

You may find this Karl's blog very intereseting.  - WPF Samples Series - EventManger RegisterClassHandler 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: "WPF-强大的图表.zip"是一个WPFA强大的图表库,可以用于创建各种类型的图表和数据可视化。这个库具有很多有用的功能和特性,使它成为开发人员首选的工具之一。 首先,它提供了丰富的图表类型,包括线条图、面积图、柱形图、饼图等等。无论是展示数据趋势、比较数据之间的关系,还是显示数据的分布,都可以使用该库来轻松创建出美观且具有交互性的图表。 其次,它支持数据的实时更新和动画效果。开发人员可以通过简单的代码,实现数据的实时更新和动画效果,使图表更加生动和动态。 此外,该库还提供了丰富的自定义选项,使开发人员能够根据自己的需求定制图表的外观和行为。可以调整图表的颜色、字体、背景等,使其与应用程序的风格一致。同时,还可以通过添加标签、工具提示等增加交互性和信息展示的功能。 最后,该库还具有良好的性能和稳定性。它采用了高效的绘图技术和数据处理算法,能够处理大量数据和复杂的图表。同时,它也经过了严格的测试和验证,保证在各种环境下都能稳定运行。 总之,"WPF-强大的图表.zip"是一个强大且易于使用的图表库,可以帮助开发人员轻松创建出各种类型的图表和数据可视化。无论是个人开发者还是企业应用开发者,都可以通过使用该库,提升应用程序的用户体验和数据展示效果。 ### 回答2: wpf-强大的图表.zip 是一个 WPF(Windows Presentation Foundation) 的图表库,提供了强大的图表功能。WPF 是一种用于创建 Windows 桌面应用程序的技术,它提供了丰富的用户界面元素和视觉效果,能够创建出漂亮、灵活和交互性强的应用程序。 这个图表库的强大之处在于它提供了多种类型的图表,包括饼图、柱状图、折线图、散点图等,可以满足不同需求的数据可视化要求。通过使用这个库,开发人员可以轻松创建出直观、美观的图表,使得数据更加易于理解和分析。 此外,这个库还提供了丰富的图表定制和交互功能。开发人员可以自定义图表的样式、颜色和标签,以适应不同的设计需求。还可以添加交互功能,比如通过鼠标点击或拖拽来实现数据筛选、数据比较等功能。这些功能使得使用者可以更加灵活地探索和分析数据。 值得一提的是,这个库还提供了一些额外的功能,如数据绑定、动画效果等。数据绑定可以方便地将图表与数据源进行绑定,实时更新图表内容。动画效果可以为图表增添一些动态效果,增强用户的体验。 总之,wpf-强大的图表.zip 是一个功能丰富、易于使用和定制的图表库,适用于需要数据可视化的 WPF 应用程序开发。无论是做数据分析、报表生成还是展示数据趋势,使用这个库可以轻松创建出漂亮、交互性强的图表,提升应用程序的用户体验。 ### 回答3: WPF-强大的图表.zip是一个压缩文件,包含了使用WPF(Windows Presentation Foundation)技术开发的强大图表控件库。WPF是微软推出的一种用于创建丰富、灵活、交互性高的应用程序的技术,它采用XAML(Extensible Application Markup Language)语言描述界面,支持数据绑定、动画效果等丰富功能。 在WPF-强大的图表.zip中,我们可以找到多种类型的图表控件,比如折线图、柱状图、饼图等。这些图表控件支持灵活的自定义配置,可以通过数据绑定将数据源与图表进行关联,实现数据的可视化展示。同时,这些图表控件还支持动画效果,能够给用户提供更加生动、直观的数据展示体验。 WPF-强大的图表.zip还包含了一些示例代码和文档,帮助开发人员快速上手使用这些图表控件。开发人员可以参考示例代码,理解如何使用这些图表控件创建各种不同类型的图表,并且通过文档来学习控件的使用方法和属性配置。 总之,WPF-强大的图表.zip提供了一套功能强大、灵活易用的图表控件库,方便开发人员在WPF应用程序中实现各种各样的数据可视化需求。无论是企业数据分析,还是个人数据展示,这些图表控件都能够帮助开发人员快速构建出美观、交互性强的图表界面。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值