Geeks With Blogs
Imran Shaik Silverlight Quintessential Rambling

I have been using Rx for over a month now and can’t even begin at telling how beautiful it really is and how much it has simplified my life especially working with UI declaratively and without having to worry about Dispatchers, threading etc., and I ended up rewriting most of the custom controls I have build over time to support Rx. And have been a big fan of Observable pattern and WeakEventListeners again had their own limitation which I always found a bit intimidating and for me Reactive Extensions became the way to go.

Enough of my expression of love for Rx and getting back to the actual blog post. Recently (before I started on Rx) in one of the projects, I was working on (WPF), I had to build a range selector, where the user can select the range by manipulating two sliders. The control was simple enough but then I encountered the problem in the application logic. I have had few very bad experiences with Slider control while creating a video player in good old days of Silverlight 2, and have learned a lesson (the hard way) of never doing long running operations (either async or sync) on any value that might change frequently and having to do sync operation and managing the threads/dispatcher. Well it was a deja vu and a trip to memory lane with long running process on value change of the slider (in this case range selector, well twice as bad). And the long running process was actually to long to turn a blind eye, and the only way out was to stabilise the value before processing long running operation.

Here is what it looked like.

private readonly DispatcherTimer timer = new DispatcherTimer();
DateTime lastTimeTicked;
bool isHandled = false;

void InitialiseValues()
{
    try 
    {
        timer.Interval = TimeSpan.FromMilliseconds(500);
        timer.Tick += new EventHandler(ReCheck);
        //Price is a model object with Start, End properties, bounded two-way to the UI
        //and the event ValuesChanged will be raised on value change of either property. 
        Price.ValuesChanged += (s, e) => StabaliseValues();
        
    }
    catch (Exception ex)
    {
        ...
    }
}

/// <summary>
/// Method to Stabilise Values
/// </summary>
private void StabaliseValues()
{
    if (!timer.IsEnabled)
    {
        timer.Start();
    }
    else
    {
        timer.Stop();
        timer.Start();
    }

    lastTimeTicked = DateTime.Now;
    isHandled = false;
}


/// <summary>
/// Event Handler for Timer.Tick 
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ReCheck(object sender, EventArgs e)
{
    //Already handled 
    if (isHandled)
        return;

    //Double check stability. 
    if (DateTime.Now - lastTimeTicked >= timer.Interval)
    {
        MyLongRunningOperationAsync(); //Run the long running operation async. 
        isHandled = true;
    }
}

Well it worked fine, apart from typical glitches which are common in any async programming scenario. But even though it worked pretty well, it was still a hack with again having to managing the events subscriptions and all that for memory leaks etc.,

When I encountered Rx I went back and the whole code was changed to a very simple extension method call, so the above scenario became,

Observable.FromEvent<EventArgs>(Price, "ValuesChanged")
          .Throttle(TimeSpan.FromMilliseconds(500))
          .Subscribe(_ => MyLongRunningOperationAsync());

This worked fine and all other async problem glitches were resolved using Rx again but this was resolved  only because the there was an event that I was listening to, on the Price; and I was asking myself can we have observable properties instead of events that I can subscribe to?

Since the application logic was in a MVVM pattern and my ViewModel/Model implemented INotifyPropertyChanged interfaces, all my properties that I would be interested in observing already had an event that I can hook up to PropertyChanged, and I can do away with ValuesChanged event so the above scenario became,

Observable.FromEvent<PropertyChangedEventArgs>(Price, "PropertyChanged")
          .Where(evt => evt.EventArgs.PropertyName == "Start" || evt.EventArgs.PropertyName == "End")
          .Throttle(TimeSpan.FromMilliseconds(500))
          .Subscribe(_ => MyLongRunningOperationAsync());

This worked perfectly but to make the process of observing a property better and generic I added some Expression pixie dust, and some reflection love and have ended up with a helper method that would do it all for you,

public static class ReactiveEx
   {

       public static IObservable<T> ObservableProperty<T>(Expression<Func<T>> propertyExtension)
       {
           if (propertyExtension == null)
               throw new ArgumentNullException("propertyExtension");

           var memberExpression = propertyExtension.Body as MemberExpression;

           if (memberExpression == null)
               throw new ArgumentException("The expression is not a member access expression.", "propertyExtension");

           var member = memberExpression.Expression;

           if (member == null)
               throw new ArgumentNullException("the member is not valid", "member");

           var property = memberExpression.Member as PropertyInfo;

           if (property == null)
               throw new ArgumentException("The member access expression does not access a property.", "property");

           var constantExpression = (ConstantExpression)member;

           if (constantExpression.Type.GetInterface("INotifyPropertyChanged") == null)
               throw new ArgumentException("The member doesn't implement INotifyPropertyChanged interface", "constantExpression");

           return Observable
                .FromEvent<PropertyChangedEventArgs>(constantExpression.Value, "PropertyChanged")
                .Where(prop => prop.EventArgs.PropertyName == property.Name)
                .Select(_ => property.GetValue(constantExpression.Value, null))
                .DistinctUntilChanged()
                .Cast<T>();
       }
   }

Using this helper method (which would be executed only once per property) the above scenario would become

ReactiveEx.ObservableProperty(() => Price.Start)
    .Merge(ReactiveEx.ObservableProperty(() => Price.End))
    .Throttle(TimeSpan.FromMilliseconds(500))
    .Subscribe(_ => MyLongRunningOperationAsync());

Now any property can be observed or subscribed to as long as the class implements INotifyPropertyChanged and can use any Rx extension methods to observe your observable property example,

var observer =
    ReactiveEx.ObservableProperty(() => Value)
    .Where(val => val > 0.5d)
    .Do(val => Debug.WriteLine(val))
    .SubscribeOnDispatcher();

IDisposable observerSubscription = 
    observer.Subscribe(
    val => Debug.WriteLine(val, "Value Changed"), 
    ex => Debug.WriteLine("Exception encountered"));

The property observer helper method would work for both WPF 4.0 and Silverlight 4.0.

Posted on Tuesday, November 30, 2010 7:29 AM Silverlight , WPF | Back to top


Comments on this post: Observe a Property in MVVM pattern in Silverlight/WPF using Rx

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © Imran Shaik | Powered by: GeeksWithBlogs.net | Join free