Handling a routed event in any class

When you handle an element’s event in XAML, the event handling method you specify must exist in the class contained in the associated code-behind file.  That’s fine for most situations, but what about when you want that element’s event(s) to be handled by another class?  In the first version of WPF there is no support for declaratively handling events with methods in arbitrary classes.  This blog post shows a way around that limitation.

I created a utility class named RoutedEventManager which exposes an attached property named HandlerInfo.  That property is of type RoutedEventHandlerInfo; a class which stores the information needed for this technique to work.  Here is an example of those classes in use.  The XAML seen below shows a Button in a Window.  The Button’s Loaded event is handled by a utility class named ButtonHelper.

RoutedEventManager (XAML)

As seen in that XAML snippet, the Button’s Loaded event is not directly assigned a handler.  The HandlerInfo attached property is set to an instance of RoutedEventHandlerInfo, specifying the information needed to eventually assign a handler to the Loaded event.  Here is the method which handles the Button’s Loaded event, in the ButtonHelper class:

RoutedEventManager (ButtonHelper)

The implementation details of ButtonHelper are unimportant, but it is worth pointing out why it handles the Button’s Loaded event.  An attached property can only be set on an element once.  In the demo app we need to handle three of the Button’s events (Click, MouseEnter, and MouseLeave), so we assign handlers to those events in the method which handles Loaded.  If you only need to handle one event “externally” on the element, say the Click event, this extra step is unnecessary.  In that case you could just establish a handler for the Click event with RoutedEventManager in XAML.

We will not delve too deep into how this works, but free feel to explore the code in the demo app at the end of this post.  For those of you with insatiable curiosity and no time to poke around in a demo project, here is the basic gist.  When the HandlerInfo attached property is set on an element, eventually this method in RoutedEventManager is invoked:

RoutedEventManager (setting new handlerinfo)

That method depends on the CreateHandler method of the RoutedEventHandlerInfo class, which is implemented like this:

RoutedEventManager (creating the event handler)

As of this writing you can only specify a static method as the event handling method.  I’m considering whether it would be helpful to be able to specify an instance method on a particular object.  If you have a compelling reason for that feature to exist, please let me know.

These classes are not “done yet.”  I’m still pondering what other functionality might be useful for them, so feel free to drop me a comment if you have a neat idea. 

Download the demo app here: RoutedEventManager (demo project)  Be sure to change the file extension from .DOC to .ZIP and then decompress it.

10 Responses to Handling a routed event in any class

  1. Alex Nesterov says:

    Why not just get an element by name and subscribe to its events from whereever you need? We don’t even need “statics” that way…

  2. Josh Smith says:

    Alex,

    Suppose you have a template which contains an element whose events should be handled by some helper class. If that template is used in several places in an application, it makes sense to consolidate the event subscribing logic to the template itself, not various places in the code-behind.

    Josh

  3. Alex Nesterov says:

    Good point…

  4. Karl Shifflett says:

    Josh,

    Have been giving this topic a lot of thought, considering it essential to a very loosly coupled design.

    Below is the contstructor for my Window class. It sets up the necessary plumbing to listen for any MenuItem that gets clicked in the entire visual tree, all without any event handler delegates.

    Is this the same thing you are looking to accomplish.

    This design in it’s current form may not be deal, since a page object may have a context meme that it was to be the handler for without outside interference.

    So to solve this, I’m going to subclass MenuItem controls that I want to participate in the global handling and have them Implement an Interface. The in the code below, just change the Type from MenuItem to ICustomMenuItem or something.

    The below privided a method for a window or popup to be added to the application using only a single event handler. The MenuItems that be service here, all have a complex parameter type that has the required information to display the window or popup.

    Love WPF!

    Public Sub New()
    MyBase.New()

    Me.InitializeComponent()

    ‘ Insert code required on object creation below this point.

    EventManager.RegisterClassHandler(GetType(MenuItem), MenuItem.ClickEvent, New RoutedEventHandler(AddressOf MenuItemClickHandler))

    End Sub

  5. Josh Smith says:

    Karl,

    Thanks for the feedback. Using RegisterClassHandler is different from what my technique allows you to do for a few reasons. My little hack can be used in XAML, and can be applied to just one instance of a class, not all instances.

    Thanks,
    Josh

  6. Seref Arikan says:

    Josh,
    This code might be the key to what I’ve been thinking about for a while. The following is a copy of the message I’ve sent to MSDN forums, and my current idea is that there is a great potential for loose xaml + some binding class/mechanism + scripting.
    Basicly I’m after completely seperating GUI from business logic, and also supporting scripting in business layer. I’m pasting my post from the forums, and I’d love to hear your opinions on this one.

    All the best
    Seref

    Hi, I have posted the following question to channel 9 forums also, and I hope I’m not cross posting by asking the same thing here.

    This might be a little bit long, but I hope I can get some feedback. Ok, the question simply put is: is it possible to build a dynamic framework using WPF, which will let users develop parts of a UI (or all of it) and then deploy it to a remote execution environment by means of web services etc..
    I have seen similar questions here, with negative answers, but I wanted to make my scenario clear. A client of mine has a large .NET application which covers quite a bit of functionality. They want this to be extended by third parties, since customizing the software for each customer is becoming a high cost process. The first idea that comes to mind is to develop an interface based framework, that’ll allow third parties to write components. I’d like to do something better if possible.
    This large .NET app has key functionality that includes access to hardware like card readers, thermal printers etc, and the idea is to build a layer of abstraction on top of it, so that 3rd parties enjoy dealing with business requirements only. For quite a time, I did research to see if I can do this using scripting. Actually, “technically” it seems possible, but the experience won’t be so nice for the third party. They need to write both UI and business code, and there is no “development environment” for building a .net app that is going to be scripted. I can build a simple IDE for them using windows designer hosting etc, but it will still make things more complicated.
    WPF and vs.net 2008 seems like an interesting candidate for this purpose. My imaginary scenario is this: a 3rd party is provided with a set of apis that includes various functionality, they fire up vs.net 2008, design a WPF form, attach their “scripted” business logic that uses the apis, and deploy this to the actual execution environment. If something goes wrong, or an update is developed, updating the actual code that is working is a breeze since it is scripted. Just provide some remote mechanism to 3rd party, and bug fixes or updates can be reflected very easily.
    Silverlight almost has all of this, If you consider IIS to be the hosting environment for components. You have xaml, a great designer, and javascript. The problem is, the execution in the browser plugin has its own .net framework, and it is a sandbox, so access to provided hardware abstracting api is not possible Sad
    For WPF, loading XAML is possible, but XAML with code behind code that contains business logic can not be loaded dynamically (right?)

    Quite long story in short: I want to “script” XAML + code that uses a set of .NET assemblies as libraries. Do you think that there is any hope for me?
    A little note: it appears that MyXaml can do this, but it does not have the designer or development environment that I’d like to provide to 3rd parties.

  7. Kevin says:

    Hi Josh,
    Nice article … Is it possible to have the Handler Methods not be static ?

  8. Josh Smith says:

    I don’t see why not. You’d have to have some way of specifying an object instance on which to invoke the handler in XAML, or have the markup extension use Activator.CreateInstance() to create one via a default ctor. Something like that…

  9. Kevin says:

    So i would need to substitute
    MethodInfo methodInfo = this.TargetType.GetMethod(this.MethodName, flags); from RoutedEventHandlerInfo to use Activator.CreateInstance ? why is it that having flags not static work ?

  10. Josh Smith says:

    All I am saying is, if you want this thing to call an instance method, it needs an instance of the class on which to call the method. How and where you choose to create the object is up to you.🙂

%d bloggers like this: