This is an example code:
public class MyParent : INotifyPropertyChanged
{
List MyChildren;
public bool IsChanged
{
get
Doing this kind of communication can be tricky, especially if you want to avoid memory leaks due to the event handlers that you hook up. There is also the case of handling items that are added / removed from the collection.
I've really enjoyed the power and simplicity of the Continuous LINQ project on codeplex. It has some very rich features for setting up "Reactive Objects", "Continuous Values", and "Continuous Collections". These let you define your criteria as a Linq expression and then let the CLINQ library keep the underlying values up to date in real time.
In your case, you could set up the parent with a ContinuousFirstOrDefault() linq query that watched for any child where "IsChanged == true". As soon as a child sets the value to true and raises PropertyChanged, the continuous value will detect the change and raise a corresponding PropertyChanged in the parent.
The benefits:
Here's what the code might look like:
public class MyParent : INotifyPropertyChanged
{
private ObservableCollection _MyChildren;
private ContinuousValue _ContinuousIsChanged = null;
public MyParent()
{
_MyChildren = new ObservableCollection();
// Creat the ContinuousFirstOrDefault to watch the MyChildren collection.
// This will monitor for newly added instances,
// as well as changes to the "IsChanged" property on
// instances already in the collection.
_ContinuousIsChanged = MyChildren.ContinuousFirstOrDefault(child => child.IsChanged);
_ContinuousIsChanged.PropertyChanged += (s, e) => RaiseChanged("IsChanged");
}
public ObservableCollection MyChildren
{
get { return _MyChildren; }
}
public bool IsChanged
{
get
{
// If there is at least one child that matches the
// above expression, then something has changed.
if (_ContinuousIsChanged.Value != null)
return true;
return false;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaiseChanged(string propName)
{
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
public class MyChild : INotifyPropertyChanged
{
private int _Value;
public int Value
{
get
{
return _Value;
}
set
{
if (_Value == value)
return;
_Value = value;
RaiseChanged("Value");
RaiseChanged("IsChanged");
}
}
private int _DefaultValue;
public int DefaultValue
{
get
{
return _DefaultValue;
}
set
{
if (_DefaultValue == value)
return;
_DefaultValue = value;
RaiseChanged("DefaultValue");
RaiseChanged("IsChanged");
}
}
public bool IsChanged
{
get
{
return (Value != DefaultValue);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaiseChanged(string propName)
{
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
The above code sets up the ContinuousFirstOrDefault in the constructor so that it is always monitoring. However, in some cases you can optimize this by lazily instantiating the ContinuousFirstOrDefault only when the getter for "IsChanged" is called. That way you don't start monitoring for changes until you know that some other piece of code actually cares.