CreateDerivedCollection and derived lists
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, y) => x.CreatedAt.CompareTo(y.CreatedAt));
VisibleTiles = TweetTiles.CreateDerivedCollection(
x => x,
x => !x.IsHidden);
}
}
Wrap it all up
CreateDerivedCollection allows us to declare how a list should be transformed and keep up with the original list.