Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

Introduction to the Reactive Framework Part III

In my previous post in the Introduction to the Reactive Framework series, I discussed the duality between the IEnumerable/IEnumerator and IObservable/IObserver and how that enables us to generalize the ideas from LINQ to Objects to LINQ to Observables/Events.  After the last post, I was asked how we go from events to Observables, so let’s tackle that in this post.

Before we get started, let’s get caught up to where we are today:

From Events…

Let’s start out by looking at the way we’d usually do event-based programming in .NET.  First, if we have the need to pass data as arguments to our event, we’ll need to create a class which inherits from EventArgs, and in this case we have a class called SomeEventArgs.  This class holds nothing more than just a simple object value.

public class SomeEventArgs : EventArgs {
    public object Value { get; private set; }

    public SomeEventArgs(object value) {
        Value = value
    }
}

Then we’d have a class which exposes an event with an EventHandler of our SomeEventArgs.  At some point along the way we’d have a method which invokes our event, keeping in mind that we need to check for nulls before invoking.

public class SomeClass
{
    public event EventHandler<SomeEventArgs> SomeEvent;
    public event EventHandler<SomeEventArgs> OtherEvent;
    
    public void InvokeEvent(object value) {
        var handler = SomeEvent;
        if(handler != null)
            handler(this, new SomeEventArgs(value));
    }
    // Other event invoking code here
}

Now to do something useful with it, you can subscribe and unsubscribe by using the += and –= operators.  In this case, I actually declared a method called SomeHandler which will react to the event arguments that I give it. 

var someClass = new SomeClass();

// Add handler
someClass.SomeEvent += SomeHandler;

// Remove handler
someClass.SomeEvent -= SomeHandler;

Our SomeHandler method must follow the signature with the sender as an object and the EventArgs derived class, whatever it might be.  At this point we react to anything that we’re subscribed to in this method. 

private void SomeHandler(object sender, SomeEventArgs e) {
    // Some handler code
}

The problem with this solution is that it isn’t quite compositional.  What do I mean by that?  What if I want to combine this SomeEvent firing with another event?  Or if I want to ensure that an event gets posted on a certain synchronization context?  Or if I want to throttle the firing of an event to a certain threshold?  All of those are a tad more difficult using standard out of the box .NET events, but, we can with Observables.  So, how do we get there?

To Observables

Unlike F#, we don’t get Observables for free when we use events, so we have a little work ahead of us.  In order for us to go from events to Observables , we simply need to use the Observable.FromEvent method.  There are several flavors that we can use.  The first option is to use a little “magic string” juice to wrap our event in an IObservable.  This method takes the object to bind for the event and the event name as a string.

public static class EventExtensions {
    public static IObservable<Event<SomeEventArgs>> GetSomeEvent(
        this SomeClass someClass) {
    
    return Observable.FromEvent<SomeEventArgs>(
        someClass, "SomeEvent");
}

This method then returns to us an IObservable<Event<SomeEventArgs>> which allows us to get access to the EventArgs and the Sender through their respective properties on the Event class.  Personally, I’m not a fan of magic strings at all and if I can get away with not using them, all the better.  Luckily, there are other options.  The next option is to use Action<EventHandler<EventArgs>> arguments for both the subscribe and unsubscribe.  Keep in mind that this only works for those events which are declared to use EventHandler<TEventArgs>. 

public static class EventExtensions {
    public static IObservable<Event<SomeEventArgs>> GetSomeEvent(
        this SomeClass someClass) {
    
    return Observable.FromEvent<SomeEventArgs>(
        h => someClass.SomeEvent += h,
        h => someClass.SomeEvent -= h);
}

If your given event doesn’t have the signature of EventHandler<TEventArgs>, you can still take advantage of the Reactive Framework as well.  In this instance, let’s extend the WPF IInputElement to wrap the MouseMove event as an IObservable<Event<MouseEventArgs>>.

public static IObservable<Event<MouseEventArgs>> GetMouseMove(
    this IInputElement inputElement)
{
    return Observable.FromEvent(
        (EventHandler<MouseEventArgs> h) => new MouseEventHandler(h),
        h => inputElement.MouseMove += h,
        h => inputElement.MouseMove -= h);
}

What this does for us behind the scenes is create an IObservable/IObserver to handle the events as they happen.  Each invocation of the event causes the OnNext to be fired with the Event<TEventArgs> argument.  In this case, the OnComplete and OnException are not implemented as part of the IObserver instance.

An Example

Let’s create a little example of drawing on a canvas in WPF.  In order to do that, we’ll need not only the MouseMove extension as we have above, but also MouseLeftButtonDown and MouseLeftButtonUp.  Each of those extension methods should follow the same pattern as above in terms of needing the extra parameter to convert the EventHandler<MouseButtonEventArgs> to a MouseButtonEventHandler. 

First, we need to capture position of the mouse during our mouse moves.  After that, we’ll need to zip together the mouse move and another mouse move that is one ahead in order to capture a delta.  Finally, we’ll capture the mouse downs and the mouse deltas until the mouse ups happen.  Listed below is code to make that happen.

var mouseMoves = from mm in mainCanvas.GetMouseMove()
                 let location = mm.EventArgs.GetPosition(mainCanvas)
                 select new { location.X, location.Y};
                 
var mouseDiffs = mouseMoves
    .Skip(1)
    .Zip(mouseMoves, (l, r) => new {X1 = l.X, Y1 = l.Y, X2 = r.X, Y2 = r.Y});

var mouseDrag = from _  in mainCanvas.GetMouseLeftButtonDown()
                from md in mouseDiffs.Until(
                    mainCanvas.GetMouseLeftButtonUp())
                select md;

The code should read rather straight forward as we attach to the mainCanvas object in order to draw.  The interesting functions are the mouseDiffs and the mouseDrag.  The mouseDiffs takes the mouseMoves and then skips 1 ahead and zips it with another mouseMoves, and projects to hold both left and right values.  The mouseDrag function takes events from left mouse button down and then from mouseDiffs until the left mouse button up Observable happens. 

Now that we have our mouseDrag, what can we do with it?  Well, let’s go ahead and draw on our canvas:

var mouseSub = mouseDrag.Subscribe(item =>
{
    var line = new Line
                  {
                      Stroke = Brushes.LightSteelBlue,
                      X1 = item.X1,
                      X2 = item.X2,
                      Y1 = item.Y1,
                      Y2 = item.Y2,
                      StrokeThickness = 5
                  };
    mainCanvas.Children.Add(line);
});

Our mouseSub subscription creates a new line with our coordinates from zipped mouseDrag Observable and then adds it to our canvas.  Consider how little code was required to make this happen, and try doing that with standard .NET events.  Quite frankly, this kind of solution makes event-based programming using .NET events expressive enough to be usable.

Conclusion

As I’ve stated before, the LiveLabs Reactive Framework gives us the ability to harness reactive programming and treat events as the first class citizens they should have been using LINQ expressions and other standard LINQ combinators.  Still, there is much to cover in this series with the standard LINQ combinators, the monadic heritage of this solution and more.

This entry was posted in C#, Event-based Porgramming, Reactive Framework. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://codebetter.com/members/Matthew.Podwysocki/default.aspx Matthew.Podwysocki

    @Robert,

    Yes, this is old code as Event became IEvent and Until became TakeUntil.

    Matt

  • Robert Taylor

    I actually built a WPF app using .NET events just to see what it took. I was planning to maybe show this to others along with the Rx Version. However, there seem to be a few errors in what you have written (which may be related to a version change).

    IObservable> should be
    IObservable>

    and mouseDiffs.Until doesn’t compile. It seems as if this should be mouseDiffs.TakeUntil.

    Do you have a working project that you could make available?

  • http://codebetter.com/members/Matthew.Podwysocki/default.aspx Matthew.Podwysocki

    @Matthias,

    It means that it returns the Observable that reacts first and I’ll cover that in a later post.

    Matt

  • http://www.minddriven.de Matthias

    Does anybody know what “Amb” means?

    Thanks, Matthias

  • http://codebetter.com/members/Matthew.Podwysocki/default.aspx Matthew.Podwysocki

    @Martinho,

    Just as I answered Ivan, it’s not my place to say, but hopefully soon. There are only so many things you can do with the Silverlight bits, I totally understand.

    Yes, I hope to cover those combinators in the upcoming series.

    Matt

  • http://codebetter.com/members/Matthew.Podwysocki/default.aspx Matthew.Podwysocki

    @Ivan,

    Hopefully soon, but I’m not the one to answer the question. Looking forward to the Rx talk at PDC which should go a bit further than my posts have.

    Matt

  • http://www.dump-split.com Ivan Maček

    I really love Reactive Framework’s approach!
    When can we expect CTP? :)

  • http://devnonsense.blogspot.com Martinho Fernandes

    Great example, Matt.

    Now, any idea when these goodies will be out here in the wild? I have been toying with the assembly from the Silverlight Toolkit (after hacking it so it depends on the “regular” framework, not the Silverlight one) and I’m starting to love it.

    Looking forward to the monadic heritage post, and something about the combinators that don’t exist in standard LINQ (Spawn, Reify, Amb, Dispatch…)