Saturday, August 15, 2009

Generating observable events with T4 template for Reactive Framework

[New version (2011.05.06)]

Reactive Framework (Rx) is what you definitely need to play with. Firstly watch this, this and this, then download Silverlight Toolkit where you can find System.Reactive.dll. If you don't want to use the Silverlight's version of Rx, convert it to the full .Net framework's version this way. Next read this, this, this and this.

Now you are ready to write you own LINQ queries over events :) Lets see a very simple query listening to 3 event of the System.Windows.Forms.Form class.

var form = new Form();

IObservable<Event<KeyEventArgs>> keyDown =
    Observable.FromEvent<KeyEventArgs>(form, "KeyDown");
IObservable<Event<KeyEventArgs>> keyUp =
    Observable.FromEvent<KeyEventArgs>(form, "KeyUp");
IObservable<Event<MouseEventArgs>> mouseMove =
    Observable.FromEvent<MouseEventArgs>(form, "MouseMove");

var downMoveUp =
    from key in keyDown
    from mouse in mouseMove.Until(keyUp)
    select " X = " + mouse.EventArgs.X + " Y = " + mouse.EventArgs.Y;

downMoveUp.Subscribe(x => Console.WriteLine(x));

Console.WriteLine("Press any key and move mouse until key up ... ");

form.ShowDialog();

When you create an event observer you need to put a hardcoded event name and appropriate event arguments type. This is quite uncomfortable, especially when you want to use this event in many different places so lets wrap this code into an extension methods.

public static class FormExtenions
{
    public static IObservable<Event<KeyEventArgs>> GetObservableKeyDown(this Form form)
    {
        return Observable.FromEvent<KeyEventArgs>(form, "KeyDown");
    }
    public static IObservable<Event<KeyEventArgs>> GetObservableKeyUp(this Form form)
    {
        return Observable.FromEvent<KeyEventArgs>(form, "KeyUp");
    }
    public static IObservable<Event<MouseEventArgs>> GetObservableMouseMove(this Form form)
    {
        return Observable.FromEvent<MouseEventArgs>(form, "MouseMove");
    }
}

var keyDown = form.GetObservableKeyDown();
var keyUp = form.GetObservableKeyUp();
var mouseMove = form.GetObservableMouseMove();

Writing extension methods for all events of all controls is obviously a very boring and error-prone task so lets use T4 templates to generate that code.

ObservableEventsT4

This produces:

using System.Linq;
using System.Collections.Generic;

namespace ObservableEvents
{
    public static class ExtensionMethods
    {
        public static IObservable<Event<System.Windows.Forms.ControlEventArgs>> GetObservableControlAdded(this System.Windows.Forms.Control source)
        {
            return Observable.FromEvent<System.Windows.Forms.ControlEventArgs>(source,"ControlAdded" );
        }
        public static IObservable<Event<System.Windows.Forms.ControlEventArgs>> GetObservableControlRemoved(this System.Windows.Forms.Control source)
        {
            return Observable.FromEvent<System.Windows.Forms.ControlEventArgs>(source,"ControlRemoved" );
        }
        public static IObservable<Event<System.Windows.Forms.DragEventArgs>> GetObservableDragDrop(this System.Windows.Forms.Control source)
        {
            return Observable.FromEvent<System.Windows.Forms.DragEventArgs>(source,"DragDrop" );
        }
        public static IObservable<Event<System.Windows.Forms.DragEventArgs>> GetObservableDragEnter(this System.Windows.Forms.Control source)
        {
            return Observable.FromEvent<System.Windows.Forms.DragEventArgs>(source,"DragEnter" );
        }
        public static IObservable<Event<System.Windows.Forms.DragEventArgs>> GetObservableDragOver(this System.Windows.Forms.Control source)
        {
            return Observable.FromEvent<System.Windows.Forms.DragEventArgs>(source,"DragOver" );
        }

Templates work also in Silverlight's projects. Get source code for this post from here and enjoy the Rx!.

1 comment:

jean said...

Great post, and very useful T4 template. Thanks! :-) Looking forward an example of a basic app implementation that leverages RX and parallel framework together....