Introduction
Some of you may remember my love with Rx Framework began with a pet project I wrote about.
Now, I’m using Rx Framework almost every day, but this is thanks to a small little personal framework that made my life easier.
So, no big and complicated article today, it’s friday after all, just an enumeration of my best reactive framework extensions methods. This article can be considered as the suite of this old one that grouped some of my utility classes.
Best of
All methods I use such as .Subscribe will return a IDisposable that you can dispose if you want to unsubscribe.
How to subscribe to PropertyChanged
When you are interested into a property that change on some of you view model, you often need to write the following spaghetti code :
public void UpdateUser(User user)
{
user.PropertyChanged += user_PropertyChanged;
NameChanged();
}
void user_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName == "Name")
NameChanged();
}
private void NameChanged()
{
}
I replaced with
user
.ItemPropertyChanged(u=>u.Name,true)
.Subscribe((args) =>
{
});
The boolean fire the subscribe action directly a first time when you subscribe. Note that you can get the NewValue and OldValue of your property with the args argument, and do not have magic string anymore.
How to subscribe to CollectionChanged
Replace the old way of doing something on each items added to an observable collection :
public void SubscribeToCollection(ObservableCollection<User> users)
{
users.CollectionChanged += users_CollectionChanged;
foreach(var user in users)
{
NewUser(user);
}
}
void users_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if(e.NewItems != null)
foreach(User user in e.NewItems)
{
NewUser(user);
}
}
private void NewUser(User user)
{
}
Into the reactive way:
users
.ForeachItem()
.Subscribe(user =>
{
});
How to subscribe to items in a ObservableCollection
Well basically a spaghetti code combining the two previous old approach.
I replaced the following spaghetti.
public void SubscribeToCollection(ObservableCollection<User> users)
{
users.CollectionChanged += users_CollectionChanged;
foreach(var user in users)
{
NewUser(user);
}
}
void users_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if(e.NewItems != null)
foreach(User user in e.NewItems)
{
NewUser(user);
}
if(e.OldItems != null)
{
foreach(User user in e.OldItems)
{
user.PropertyChanged -= UserChanged;
}
}
}
private void NewUser(User user)
{
user.PropertyChanged += UserChanged;
}
void UserChanged(object sender, PropertyChangedEventArgs e)
{
}
Into the clean version
users
.ObserveEachItem(u => u.ItemPropertyChanged())
.Subscribe(args =>
{
User user = args.Sender;
});
Weak event listener
Have you tried to subscribe to an object weakly, so the garbage collector correctly garbage a listener that is no longer referenced anywhere ?
Well, one solution is to read this msdn page and use the WeakEventManager. Sorry, I’m too lazy to read it for you.
The other solution is to use my ObserveWeakly method :
var subscription = user.ItemPropertyChanged()
.ObserveWeakly()
.Subscribe(args =>
{
User u = args.Sender;
});
subscription = null; GC.Collect();
user.AssertSubscribers(0);
Subscribe to dependency property changes
The code to subscribe to dependency properties is not trivial as you can see in the following snippet.
TextBox box = new TextBox();
DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(TextBox.BackgroundProperty, typeof(TextBox));
if(dpd != null)
{
dpd.AddValueChanged(box, (sender, args) =>
{
});
}
But fear not, now you can do that.
TextBox box = new TextBox();
box.DependencyPropertyChanged<Brush>(TextBox.BackgroundProperty)
.Subscribe(args =>
{
Brush brush = args.NewValue;
});
Synchronize two ObservableCollection
I’m sure it happened to you more than once, for example you want an ObservableCollection<String> that list dynamically all the girls’ name of your ObservableCollection<User>. Here is the old way :
ObservableCollection<String> GirlsNames = new ObservableCollection<String>();
public void SubscribeToCollection(ObservableCollection<User> users)
{
users.CollectionChanged += users_CollectionChanged;
foreach(var user in users)
{
NewUser(user);
}
}
void users_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if(e.NewItems != null)
foreach(User user in e.NewItems)
{
NewUser(user);
}
if(e.OldItems != null)
{
foreach(User user in e.OldItems)
{
GirlsNames.Remove(user.Name);
}
}
}
private void NewUser(User user)
{
if(user.Gender = Gender.Girl)
GirlsNames.Add(user.Name);
}
Now you can turn this spaghetti into a single line: (Always returns a IDisposable if you want to stop the mapping)
users.MapToCollection(GirlsNames, u=>u.Name, u=> u.Gender == Gender.Girl);
Conclusion
I put the sources and binaries as this article, I also have a private GIT repository and internal Nuget feed to deploy my framework in my own projects. I intend to switch to public GIT repository and public nuget feed, if you beg me. ;)
Anyway the solution ships with full unit tests for everything, feel free to play with it, if there is more that what you need don’t hesitate to just copy/paste my code if you don’t want all.