ReactiveUI goodies – ReactiveList

In the world of XAML, DataBinding and ViewModels – the ObservableCollection is the standard utility to light up any list of items. The ObservableCollection is part of the Framework Class Library (FCL) and implements the INotifyCollectionChanged interface. Basically the equivalent of INotifyPropertyChanged for collections. Any class implementing INotifyCollectionChanged needs to inform subscribers about items being added, moved, deleted, replaced or a whole collection reset. This not only goes for individual items but also for ranges of items. However, in case you want to roll your own INotifyCollectionChanged implementation, beware that most UI controls do not support range operations and will throw an exception. The only option in range operations is to raise a reset event. That’s, by the way, the reason why ObservableCollection does not provide AddRange or RemoveRange methods. But enough about ObservableCollection. Let’s talk reactive. Specifically, ReactiveList. ReactiveList is the ObservableCollection on steroids and comes as part of ReactiveUI.  So let’s get started. If you need help installing ReactiveUI then please check my previous post https://janhannemann.wordpress.com/2016/10/03/reactiveui-goodies-observing-properties

Once you have ReactiveUI at your disposal, you can declare a ReactiveList in your ViewModel just the same way you would with ObservableCollection. When you bind your View to that property you will see items coming in and out just as expected.

public ReactiveList<TodoItem> Items
{ 
    get { return _items; }
    set { this.RaiseAndSetIfChanged(ref _items, value); }
}

For the demo App I wrote a little fake DataService that provides us with items to feed our ReactiveList. The code below shows how you can listen to an IObservable data list and also shows how to invoke the observation on to the UI thread.

_dataService.Listen()
    .ObserveOn(RxApp.MainThreadScheduler)
    .Subscribe(x =>
    {
        Items.Add(x);
    });

The real advantage of ReactiveList shows when you need to do a bit more than just displaying a few items in a list. So let’s not just display the items, but also add a counter. We declare the counter property and can feed it directly from an IObservable of the ReactiveList like this:

public int Count
{
    get { return _count; }
    set { this.RaiseAndSetIfChanged(ref _count, value); }
}
.
.
Items.CountChanged.Subscribe(x => Count = x);

CounterChanged isn’t the only useful IObservable event provided. Here a list of all things you can listen to during the life time of ReactiveList:

  • BeforeItemsAdded
  • ItemsAdded
  • BeforeItemsRemoved
  • ItemsRemoved
  • BeforeItemsMoved
  • ItemsMoved
  • CountChanging
  • CountChanged
  • Changed
  • ShouldReset

In the past years I worked on a lot e-mail apps and chat apps. They all had one common challenge; displaying hundreds or thousands of items in a list. No matter whether it was WPF, WinRT or Xamarin Forms. Performance was a big challenge. In all cases ObservableCollection wasn’t cutting it and we had to roll our own implementations. ReactiveList gives much better control for those scenarios. So let’s change up our data subscription a bit to accommodate a scenario where we expect a lot of traffic.  We can use one of the many powerful IObservable operations to manipulate the data stream. In this case I would like to get the items in packages instead of one by one. So we simply put in a Buffer() operation and specify the size of the packages. But what happens if we do not get enough items to fill up the buffer? That’s where the TimeSpan parameter comes in handy. Either way, if the buffer is full or time is up, a notification will be triggered.

_dataService.Listen()
    .Buffer(TimeSpan.FromSeconds(10), 5)
    .ObserveOn(RxApp.MainThreadScheduler)
    .Subscribe(x =>
    {
        Items.AddRange(x);
    });

I already mentioned that range operations are available. But I also said that most UI frameworks do not support them even though INotifyCollectionChanged allows them. If the platform does not support range operations then the items are added one by one as you used to with Add(). So what can we do if we notice UI lag or other performance issues that are caused by rapidly modifying the content of ReactiveList. One thing that will help us is already built-in; an automatic collection Reset if a significant change happened to the list. So what is significant you ask? Well its defined by what percentage of the collection was changed and you can control that value either in the constructor or on the fly via the property ResetChangeThreshold. But you can take it into your own hands as well. If you do a lot of inserts or deletions but do not want to throw away the whole collection and neither want to overwhelm the UI then you can simply pause and resume all notifications.

_dataService.Listen()
    .Buffer(TimeSpan.FromSeconds(3), 500)
    .ObserveOn(RxApp.MainThreadScheduler)
    .Subscribe(x =>
      {
         using (Items.SuppressChangeNotifications())
         {
             foreach (var item in x)
             {
                 Items.Add(item);
             }
         }
         if (x.Any())
         {
            Items.Reset();
         }
      });

I hope this post was helpful and puts one more tool in your utility belt to bring performant Apps to life. There are more things to ReactiveList I haven’t talked about yet but I will get to that in a future post.

All code for my practical code series will be available on GitHub https://github.com/bitdisaster/practicalcode

Happy Coding!

2 thoughts on “ReactiveUI goodies – ReactiveList

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