Net Dots

.Net and programming in general

Making list navigation responsive using Reactive Extensions–part 1 (Throttle)

In the following series, I’ll try to show how Reactive Extensions can be used to make list UI navigation fast while also refreshing the detail view. The nice thing about this example is that it covers a lot of different concepts within RX – the end goal of this series is to make you more familiar with these concepts. You need to have some level of familiarity with the RX library to get through – the RX Hands-On Labs is a good introduction to the area. In this series, I’ll try to concentrate on the practical side of using RX.

Let’s start with the simple UI: create a WPF form that consists from a list box on the left and a text box on the right. The text box will be the “detail view” and we will pretend that it takes a half of a second to refresh this view.

     <Grid> 
     	<Grid.ColumnDefinitions> 
     	    <ColumnDefinition/> 
     	    <ColumnDefinition/> 
     	</Grid.ColumnDefinitions> 
     	<ListBox  
             ItemTemplate="{DynamicResource ItemTemplate}"  
             ItemsSource="{Binding List}" 
             SelectedItem="{Binding Current}" 
             /> 
 	<TextBox Grid.Column="1" Text="{Binding Details, Mode=OneWay}"/> 
     </Grid>

We will use Expression Blend to generate a collection of sample data and to simplify the project we will make the main window to be its own ViewModel. What actually matters to us, is that we cannot display the details instantly:

         private  object  _current;
         public  object  Current
         {
             get  { return  _current; }
             set 
             {
                 _current = value ;
                 Details = ((Item )value ).Property1;
             }
         }
 
         private  string  _details;
         public  string  Details
         {
             get  { return  _details; }
             set 
             {
                 _details = value ;
                 ReadDetailData();
                 InvokePropertyChanged("Details" );
             }
         }
 
         private  static  void  ReadDetailData()
         {
             Debug .WriteLine("Reading details on thread "  + Thread .CurrentThread.ManagedThreadId);
             Thread .Sleep(500);
         }

When we run this project and try to use arrow keys to move the selection within the list box, the experience is painful – the system is non-responsive most of the time, and the fast navigation within the list is impossible. This happens because the process of reading happens on the UI thread, blocking it from processing the furthest events.

What if we execute the reading of details on a secondary thread?:

                 Task .Factory.StartNew(() =>
                                           {
                                               ReadDetailData();
                                               InvokePropertyChanged("Details" );
                                           }); 

This works, and the UI becomes responsive, but there are two problems with the approach:

  1. In real life scenario, each read would take different time to execute, and the earlier (non last) detail can end up being displayed when user stopped navigating.
  2. The system will be overtaxed by the unnecessary reads, since each selection change would lead to a detail read.

Let’s use Reactive Extensions to save us from both problems:

         private  readonly  Subject <string > _whenItemSelected = new  Subject <string >();
         private  string  _details;
         public  string  Details
         {
             get  { return  _details; }
             set 
             {
                 _details = value ;
                 _whenItemSelected.OnNext(value );
            }
         } 

We isolated the cause (details selection change) from the effect (reading of the details) using the _whenItemSelected Subject. A Subject is a “relay” of the RX world that ties together event flows. We will use this to “filter out” unnecessary reads using the Throttle extension method:

         public  MainWindow()
         {
             DataContext = this ;
             InitializeComponent();
             _whenItemSelected
                 .Throttle(TimeSpan .FromSeconds(0.4))
                 .Do(_ => ReadDetailData())
                 .Subscribe(_ => InvokePropertyChanged("Details" ));
             Debug .WriteLine("Executing on thread "  + Thread .CurrentThread.ManagedThreadId);
         } 

Throttle works by filtering out input when it comes more frequently that is specified by the delay parameter.  Below is the “marble diagram” for Throttle:

Throttle

My mental model of Throttle is a ball you keep in the air by kicking it with a foot. While you are kicking, the events are suppressed. When you stop kicking, the ball lands (event goes through). This is what we need to do when navigating through list – skip the events while they are coming, and land the last one that came before pause in the navigation process.

Are we happy now? Almost. My biggest complain about using Throttle is that for single navigation event, reading of details is delayed versus being started immediately. Also, it would be nice to pass through some events when user navigates continuously to give some visual feedback.

Let’s try to make a better “Throttle” in the next article in the series.

Advertisements

November 3, 2010 Posted by | Uncategorized | 1 Comment

Silverlight as a “Rule them all” concept–glad to see you go…

The ZDNet article by Mary Jo Foley ignited an extensive discussion on where is the Silverlight place in the future world. Some people are predicting a swift and painful death to Silverlight, some especially ones with invested interests are arguing that Silverlight will always be a superior choice to HTML5. I liked this post by Tim Heuer which does a good summary on this little war.

My position in the referenced discussion is defined by the fact that I am a WPF developer working on the same product for the last 13 years (of course, it was Delphi before WPF). I think that in the ideal world the applications should be fewer but of much higher quality. And the concept of the “least common denominator”, even in the UI area, does not contribute to quality. I’d like to live in the world where native UI framework with all “bells and whistles” is used for each platform – WPF for Windows, Silverlight for Windows Phone, HTML5 for browser, Cocoa for Mac, IPhone and IPad.

So I am kind of glad to hear about this “strategy shift” – let us build “native” applications and leave Silverlight and Flash the role of a “bridge” technology (with the exception of Windows Phone, where Silverlight is the “native”).

November 2, 2010 Posted by | Uncategorized | Leave a comment