I have a Class, MyClass
that implements INotifyPropertyChanged
and has some properties that implement PropertyChanged
. When MyClass.MyProperty
changes, PropertyChanged
fires as expected. Another class contains a SortedList<MyClass>
.I've tried merging the events into a single observable in the class that contains the SortedSet<MyClass>
and subscribing to it, but it doesn't seem to ever have any events. Here's what I'm trying:
Observable.Merge(MySortedList.ToObservable())
.Subscribe(evt => Console.WriteLine("{0} changed", evt.MyProperty));
What I'm trying to get is a single observable that contains all of the events from every item in my SortedList<MyClass>
. I've tried using ObservableCollection instead, but that doesn't change anything, nor would it be expected to, really, since it doesn't fire collectionchanged when a property of a contained item changes, anyway. I can listen to individual elements in SortedList<MyClass>
and see the PropertyChanged event fire, but what I want is a single Observable that contains a stream of ALL of the PropertyChanged events from all of the elements in SortedList<MyClass>
.
It seems like this should be something fairly easy to do using Rx, but I can't seem to figure out how.
I have produced an article for the RxCookBook on this subject that you can find here https://github.com/LeeCampbell/RxCookbook/blob/master/Model/CollectionChange.md Further article on PropertyChange notification is here https://github.com/LeeCampbell/RxCookbook/blob/master/Model/PropertyChange.md
It solves what you need by aggregating up the changes from an ObservableCollection<T>
. By using the ObservableCollection<T>
you also get notifications when items are added or removed from the collection.
If you dont want to use the ObservableCollection<T>
(i.e. you only want to track properties at a given snapshot of the collection) then you will need to do something else. First I assume you have an INoftifyPropertyChanged
to IObservable<T>
extension method or you are just going to use the standard event to IObservable<T>
methods.
Next you can project the List of values into a list of change sequences i.e. IEnumerable<T>
to IEumerable<IObserable<T>>
. This allows you to use Observable.Merge
to flatten the list of changes in to a single stream of changes.
Here is a sample if you dont want to use the link above:
void Main()
{
var myList = new List<MyThing>{
new MyThing{Name="Lee", Age=31},
new MyThing{Name="Dave", Age=37},
new MyThing{Name="Erik", Age=44},
new MyThing{Name="Bart", Age=24},
new MyThing{Name="James", Age=32},
};
var subscription = Observable.Merge(myList.Select(t=>t.OnAnyPropertyChanges()))
.Subscribe(x=>Console.WriteLine("{0} is {1}", x.Name, x.Age));
myList[0].Age = 33;
myList[3].Name = "Bob";
subscription.Dispose();
}
// Define other methods and classes here
public class MyThing : INotifyPropertyChanged
{
private string _name;
private int _age;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name");
}
}
public int Age
{
get { return _age; }
set
{
_age = value;
OnPropertyChanged("Age");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public static class NotificationExtensions
{
/// <summary>
/// Returns an observable sequence of the source any time the <c>PropertyChanged</c> event is raised.
/// </summary>
/// <typeparam name="T">The type of the source object. Type must implement <seealso cref="INotifyPropertyChanged"/>.</typeparam>
/// <param name="source">The object to observe property changes on.</param>
/// <returns>Returns an observable sequence of the value of the source when ever the <c>PropertyChanged</c> event is raised.</returns>
public static IObservable<T> OnAnyPropertyChanges<T>(this T source)
where T : INotifyPropertyChanged
{
return Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(
handler => handler.Invoke,
h => source.PropertyChanged += h,
h => source.PropertyChanged -= h)
.Select(_=>source);
}
}
Which will output:
Lee is 33
Bob is 24
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With