ObservableCollection<T> - Listen for changes in child elements?
-
10 มีนาคม 2553 20:52I've seen several threads about this, but none that I could find which helped much.
I have an ObservableCollection<T> which holds a number of different child elements. I get the expected notifications for adding, removing etc. What I really could use is a collection which additionally raises notifications when a child element (implenting INotifyPropertyChanged) has a property change.
I'd prefer to not have the Collection have to listen to specific events, but rather listen for "any" event raised by a child.
I was thinking that maybe I could specialize the ObservableCollection<T> class somehow, maybe by using a binding object or something that via reflection could look for properties to bind to as a child is added, and raise a generic event ("ChildElementPropertyChanged") when one of the child properties raises an event.
Does something like this already exist? Any thoughts about nice ways to implement this?
Thanks,
Hedley
ตอบทั้งหมด
-
10 มีนาคม 2553 21:07ObservableCollection doesn't work that way already? I thought change events bubbled up.
If my response answers your question, please mark it as the "Answer" by clicking that button above my post.
My blog: http://www.RyanVice.net/ -
10 มีนาคม 2553 21:08ผู้ดูแลHedley,I wrote something that does this, and it works for any INotifyCollectionChanged or IBindingList implementation holding INotifyPropertyChanged instances.I initially wrote this for my Master-detail aggregation sample, which is posted to the Expression Blend Gallery under Collection Aggregator for Master-Detail Windows.You should be able to just use (or adapt) that class for your purposes.-Reed
Reed Copsey, Jr. - http://reedcopsey.com -
10 มีนาคม 2553 21:17The changes that bubble up are adds, removes etc as the list itself is concerned. Individual properties on the elements of the list aren't listened to by the parent.
Thanks,
Hedley -
10 มีนาคม 2553 21:43ผู้ดูแล
The changes that bubble up are adds, removes etc as the list itself is concerned. Individual properties on the elements of the list aren't listened to by the parent.
Nope. If you want that to happen, you need to explicitly subscribe to them. It's a bit of a chore, but mostly bookkeeping.
Thanks,
HedleySee my previous link - I have a working example of this in ObservableCollectionAggregator.cs, in that download.
Reed Copsey, Jr. - http://reedcopsey.com -
10 มีนาคม 2553 21:48Bummer...
If my response answers your question, please mark it as the "Answer" by clicking that button above my post.
My blog: http://www.RyanVice.net/ -
10 มีนาคม 2553 23:36Reed,
Your sample was very interesting, and elegantly implemented.
I'm going to study it a bit more, to see if it's the right fit for my problem.
The problem I'm running up against is, as always, time.
I have a solution that "works" but is not ideal, and I'm searching for alternatives.
Thanks,
Hedley -
11 มีนาคม 2553 10:07ผู้ดูแล
Hi Hedley,
The following is a complete sample to do what you want.
public class MyObservableCollection<T>: ObservableCollection<T>
{
public class ChildElementPropertyChangedEventArgs : EventArgs
{
public object ChildElement { get; set; }
public ChildElementPropertyChangedEventArgs(object item)
{
ChildElement = item;
}
}
public delegate void ChildElementPropertyChangedEventHandler(ChildElementPropertyChangedEventArgs e);
public event ChildElementPropertyChangedEventHandler ChildElementPropertyChanged;
private void OnChildElementPropertyChanged(object childelement)
{
if (ChildElementPropertyChanged != null)
{
ChildElementPropertyChanged(new ChildElementPropertyChangedEventArgs(childelement));
}
}protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
base.OnCollectionChanged(e);
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
foreach (T item in e.NewItems)
{
INotifyPropertyChanged convertedItem = item as INotifyPropertyChanged;
convertedItem.PropertyChanged += new PropertyChangedEventHandler(convertedItem_PropertyChanged);
}
}
else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
{
foreach (T item in e.OldItems)
{
INotifyPropertyChanged convertedItem = item as INotifyPropertyChanged;
convertedItem.PropertyChanged -= new PropertyChangedEventHandler(convertedItem_PropertyChanged);
}
}
}void convertedItem_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
OnChildElementPropertyChanged(sender);
}
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
MyObservableCollection<Person> people = new MyObservableCollection<Person>();
people.Add(new Person() { Name = "Kate", Age = 23 });
people.Add(new Person() { Name = "John", Age = 24 });
people.ChildElementPropertyChanged += new MyObservableCollection<Person>.ChildElementPropertyChangedEventHandler(people_ChildElementPropertyChanged);
people[0].Name = "new name";
}void people_ChildElementPropertyChanged(MyObservableCollection<Person>.ChildElementPropertyChangedEventArgs e)
MSDN Subscriber Support in Forum
{
Console.WriteLine((e.ChildElement as Person).Name);
}
Hope this helps.
If you have any question, please feel free to let me know.
Sincerely,
Linda Liu
If you have any feedback on our support, please contact msdnmg@microsoft.com
Please remember to mark the replies as answers if they help and unmark them if they provide no help.
Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.- ทำเครื่องหมายเป็นคำตอบโดย TCLMKR 11 มีนาคม 2553 15:25
-
11 มีนาคม 2553 15:26I think this will work. I didn't want to have to subscribe to the individual property changes, I just need to know if something changed.
Thanks Linda!
Hedley -
16 มีนาคม 2553 16:06
I'd add one more thing: an override for ClearItems to remove the attached handlers if Clear() is called on the collection. Something like this...
protected override void ClearItems()
{
foreach (INotifyPropertyChanged item in Items)
{
item.PropertyChanged -= PropertyChangedEventHandler;
}
base.ClearItems();
} -
16 มีนาคม 2553 20:40Yep. Good point, thanks!
Hedley -
16 เมษายน 2555 12:59
This example seems to work only for object implementing INotifyPropertyChanged; otherwise the following conversion yields to null:
INotifyPropertyChanged convertedItem = item as INotifyPropertyChanged;
convertedItem.PropertyChanged += new PropertyChangedEventHandler(convertedItem_PropertyChanged); //exception here
If this is intended, why don't bind T to be a type that implements INotifyPropertyChanged as follows?
public class MyObservableCollection<T>: ObservableCollection<T> where T : INotifyPropertyChanged{ ... }
Is there a way to make this work for classes not implementing INotifyPropertyChanged too?
Thank you.
- แก้ไขโดย Heartbreaka 16 เมษายน 2555 13:00
-
16 เมษายน 2555 15:15
Is there a way to make this work for classes not implementing INotifyPropertyChanged too?
You need some kind of notification of the property change.
Implementing INotifyPropertyChanged is the most common way to do so.
The are other APIs using different interfaces (e.g. BindingList
fires an ListChanged event with an ItemChanged flag)
Yet another mechanism are depedency properties.
If you want to listen to depedency property changes from outside the dependency object
you may want to use a DependenyPropertyDescriptor.
As for POCOs aka simple CLR objects there is afaics no
(ordinary) way to get notified on a property change.
Chris