We have a parent class which has a collection of children. Both classes support change notification using Template10. Change notification works within each class (ie when we update a property within the same class) as below, but we are unable to trigger change notification on the parent from the child.
public class ParentViewModel : ViewModelBase
{
public string ParentString { get }
public decimal? Net { get { return Total / (1 + TaxRate / 100); } }
public decimal? Tax { get { return Total - Net; } }
decimal? _Total = default(decimal?);
public decimal? Total
{
get
{
return _Total;
}
set
{
Set(ref _Total, value);
RaisePropertyChanged(nameof(Net));
RaisePropertyChanged(nameof(Tax));
}
}
public ObservableCollection<ChildViewModel> MyChildren { get; set; }
We find we are able to use RaisePropertyChanged
in ParentViewModel
to fire
Net { get { return Total / (1 + TaxRate / 100); } }
and
Tax { get { return Total - Net; } }
On ChildViewModel
we have ChildString
. We want to notify ParentString
of changes to ChildString
.
public class ChildViewModel : ViewModelBase
{
ParentViewModel MyParent { get; set; }
string _ChildString = default(string);
public string ChildString
{
get
{
return _ChildString;
}
set
{
Set(ref _ChildString, value);
RaisePropertyChanged(nameof(this.MyParent.ParentString));
}
}
However ParentString
is not updating. How do we force ParentString
to update when ChildString
updates?
Regardless of nameof
argument, when you call ChildViewModel.RaisePropertyChanged
, you fire event on child view model.
nameof
operator can't "change" event source. You need a way to fire parent PropertyChanged
somehow. The easiest way is to make appropriate method in ParentViewModel
:
public void RaiseParentStringChanged()
{
RaisePropertyChanged(nameof(ParentString));
}
and call it from child:
public string ChildString
{
get
{
return _ChildString;
}
set
{
Set(ref _ChildString, value);
// assuming, that parent is assigned at this point
MyParent.RaiseParentStringChanged();
}
}
But it will be nice to throw away parent from child, if changing of ParentString
is the only reason to bring that dependency.
Since MyChildren
is an observable collection, you can subscribe on its CollectionChanged
event. When new child is being added, you can subscribe on its PropertyChanged
, and watch for ChildString
changes to raise appropriate parent event.
It's something like this:
// non-related code is omitted
public class ParentViewModel: ViewModelBase
{
private ObservableCollection<ChildViewModel> children;
public string ParentString { get; }
// don't do collection properties on view models
// as get-set ones, unless you know exactly what are you doing;
// usually you don't need to set them explicitly from outside
public ObservableCollection<ChildViewModel> MyChildren
{
get
{
if (children == null)
{
children = new ObservableCollection<ChildViewModel>();
children.CollectionChanged += ChildrenChanged;
}
return children;
}
}
private void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// assuming, that only two actions ("add" and "remove") are available;
// if you really need to nadle "reset" and "replace", add appropriate code;
// note, that "reset" doesn't provide acceess to removed items
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
((ChildViewModel)e.NewItems[0]).PropertyChanged += ChildPropertyChanged;
break;
case NotifyCollectionChangedAction.Remove:
((ChildViewModel)e.OldItems[0]).PropertyChanged -= ChildPropertyChanged;
break;
}
}
private void ChildPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(ChildViewModel.ChildString))
{
RaisePropertyChanged(nameof(ParentString));
}
}
}