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.

About these ads

November 3, 2010 - Posted by | Uncategorized

1 Comment »

  1. […] list navigation responsive using Reactive Extensions-part 2 (SampleResponsive) In the previous article in this series we used the standard RX operator, Throttle to reduce the frequency of details view update while […]

    Pingback by Making list navigation responsive using Reactive Extensions-part 2 (SampleResponsive) « Net Dots | December 18, 2010 | Reply


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: