Derived Lists

I've decided to start re-publishing the ReactiveUI documentation in blog post format over the next few weeks, so that more people get eyes on it. To get a sneak preview for what's coming next, head to the docs PR on GitHub

ReactiveList.CreateDerivedCollection is an extremely powerful method in MVVM programming, which allows you to create a projection of a ReactiveList as another list. CreateDerivedCollection allows you to do projection, ordering, and filtering of a source ReactiveList.

Once a derived list has been created, this list will update dynamically based on the source collection - as the source list has items added and removed, the derived list will also update its contents. If ChangeTrackingEnabled is enabled on the source list, changes to the source list will also cause updates to the derived list.

For example, if you have a source list of MenuItems and you have a filter method of x => x.IsEnabled, changing IsEnabled on any of the menu items will hide or show the items in the list. Note that the derived list is read-only - you cannot manually modify the derived list.

A canonical example - creating ViewModels from Models

public class TweetsListViewModel : ReactiveObject  
{
    ReactiveList<Tweet> Tweets = new ReactiveList<Tweet>();
    IReactiveDerivedList<TweetTileViewModel> TweetTiles;

    public TweetsListViewModel()
    {
        TweetTiles = Tweets.CreateDerivedCollection(
            x => new TweetTileViewModel() { Model = x });

        // Adding a new item to Tweets, results in a new ViewModel showing
        // up in TweetTiles
        Tweets.Add(new Tweet() { Title = "Hello!", });
    }
}

A more motivating example - filtering and ordering

CreateDerivedCollection can do some more interesting things, but to illustrate it, we need a more interesting set of classes.

public class Tweet  
{
    public DateTime CreatedAt { get; set; }
}

public class TweetTileViewModel : ReactiveObject  
{
    bool isHidden;
    public bool IsHidden {
        get { return isHidden; }
        set { this.RaiseAndSetIfChanged(ref isHidden, value); }
    }

    public Tweet Model { get; set; }
}

Now, let's connect up the filtering and ordering. It's important to note, that since want to refilter the list based on the IsHidden value, it must be a ReactiveObject and fire change notifications, or else CreateDerivedList has no way to know when it should refilter.

Because we had to put IsHidden on the ViewModel, we need to set up two levels of filtering. Models that are ReactiveObject would make this easier, but is often not possible.

Let's take a look:

public class TweetsListViewModel : ReactiveObject  
{
    ReactiveList<Tweet> Tweets = new ReactiveList<Tweet>();

    IReactiveDerivedList<TweetTileViewModel> TweetTiles;
    IReactiveDerivedList<TweetTileViewModel> VisibleTiles;

    public TweetsListViewModel()
    {
        TweetTiles = Tweets.CreateDerivedCollection(
            x => new TweetTileViewModel() { Model = x },
            x => true,
            x => x.CreatedAt);

        VisibleTiles = TweetTiles.CreateDerivedCollection(
            x => x,
            x => x.IsVisible);
    }
}