Reactive Extensions–Observable Sequences are First Class Objects

Windows Phone From Scratch #28Reactive

In yesterdays’ posting on Asynchronous programming with Reactive Extensions I  created a browser application that reacted to user input into a TextBox.

You will remember that the observable was created from the TextChanged event as demonstrated in the following bit of code (the throttle was used to avoid picking up any changes in the text until the user paused in entering new text),

var keys = Observable.FromEvent<KeyEventArgs>(
   search, "KeyUp" )
   .Throttle( TimeSpan.FromSeconds( .5 ) );

Then we subscribed to the observable collection and had to reach into the text box to obtain the changed text:

keys.ObserveOn( Deployment.Current.Dispatcher ).
         Subscribe( evt =>
{
  txtSearching.Text =
     "Searching for... " + search.Text;
  txtLoading.Visibility =
      System.Windows.Visibility.Visible;
  webBrowser1.Navigate( new Uri(
      "http://en.wikipedia.org/wiki/" + search.Text ) );
} );

This is fine as far as it goes, but we can go further.  Because observable sequences are first class objects we can provide operators over them using generic extension methods; which is exactly what was done by the creators of the Rx framework.


Thus, we can revamp the above code to use a much more Linq-like select statement to obtain the text as it changes:

 var keys = (from evt in Observable.FromEvent<KeyEventArgs>(
            search, "KeyUp" )
            select ( ( TextBox ) evt.Sender ).Text)
            .Throttle( TimeSpan.FromSeconds( .5 ) );

To paraphrase the excellent white paper DEVHOL202, we use a query expression to project away the IEvent<EventArgs> data type in favor of a string, and as a result the input sequences now are observable sequences of a meaningful data type.

This allows us to rewrite our  observe statement  as

 keys.ObserveOnDispatcher().Subscribe( evt =>
    {
       txtSearching.Text = "Searching for... " + evt;
       txtLoading.Visibility =
              System.Windows.Visibility.Visible;
       webBrowser1.Navigate( new Uri(
          "http://en.wikipedia.org/wiki/" + evt ) );
    } );

Much cleaner and opens the door to using even more query operators.

Here is the complete code behind file:

using System;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Reactive;
using System.Windows.Input;
using System.Linq;
using System.Windows.Controls;
using System.Windows;

namespace ReactiveExtensionsAsync
{
  public partial class MainPage : PhoneApplicationPage
  {
    // Constructor
    public MainPage()
    {
      InitializeComponent();

      var keys = (from evt in Observable.FromEvent<
                       KeyEventArgs>(
                 search, "KeyUp" )
                 select ( ( TextBox ) evt.Sender ).Text)
                 .Throttle( TimeSpan.FromSeconds( .5 ) );

      keys.ObserveOnDispatcher().Subscribe( evt =>
      {
          txtSearching.Text =
             "Searching for... " + evt;
          txtLoading.Visibility =
             System.Windows.Visibility.Visible;
          webBrowser1.Navigate( new Uri(
             "http://en.wikipedia.org/wiki/" + evt ) );
       } );

      var browser = Observable.FromEvent<
          System.Windows.Navigation.NavigationEventArgs>(
         webBrowser1, "Navigated");
      browser.ObserveOn(
         Deployment.Current.Dispatcher).Subscribe( evt =>
         txtLoading.Visibility =
         System.Windows.Visibility.Collapsed);
    }
 }
}

About Jesse Liberty

Jesse Liberty has three decades of experience writing and delivering software projects and is the author of 2 dozen books and a couple dozen online courses. His latest book, Building APIs with .NET will be released early in 2025. Liberty is a Senior SW Engineer for CNH and he was a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Microsoft MVP.
This entry was posted in Data, Mini-Tutorial, Patterns & Skills, Reactive and tagged . Bookmark the permalink.