How to update value in parent ViewModel if value in one of its children is updated in Windows 8.1 app
I have a Windows 8.1 app with parent and child view modes in the following ratio
ParentViewModel
class ParentViewModel {
private double _parentAmount;
public double parentAmount
{
get { return _parentAmount; }
set
{
if (value != _parentAmount)
{
_parentAmount = value;
NotifyPropertyChanged("parentAmount");
}
}
}
private ObservableCollection<ChildViewModel> _children;
public ObservableCollection<ChildViewModel> children
{
get { return _children; }
set
{
if (value != _children)
{
_children = value;
NotifyPropertyChanged("children");
}
}
}
}
ChildViewModel
class ChildViewModel {
private double _ChildAmount;
public double ChildAmount
{
get { return _ChildAmount; }
set
{
if (value != _ChildAmount)
{
_ChildAmount = value;
NotifyPropertyChanged("ChildAmount");
}
}
}
}
The XAML has a TextBlock bound to "ParentAmount" and then the ListView is bound to the Children Observable collection. ListView Itemtemplate is dataset with TextBox with two way binding with "ChildAmount". User can change value in child TextBox
Now my requirement is to update the ParentAmount with the sum of its entire child sum on the fly when the user changes one of the child sums. How do you achieve this?
For purposes of illustration, I've simplified the above code example, the ChildViewModel has more options than what you can see, so I can't replace this ObservableCollection of the ChildViewModel with a list with a double instance.
I would be very happy if someone could point me in the right direction. Thanks in Advance.
source to share
With very little addition, this does the trick. Specific changes include adding a property change handler for each child object in the ObservableCollection.
Please note that this is a crude example to set you on the correct track - I have not unhooked the event handlers, and I recalculate the parent amount on any change from the child (i.e. I do not verify that it was the ChildAmount that changed, this means that you get more action than necessary). I also haven't inserted any code to handle changes to the ObservableCollection, so if new items are added to them, they won't have a property change event handler, it's easy for you to do it yourself.
Please note my use of BaseViewModel is just good practice, it saves you the trouble of overriding the INotifyPropertyChanged interface for every class that needs it.
class ParentViewModel : BaseViewModel
{
private double _parentAmount;
public double parentAmount
{
get { return _parentAmount; }
set
{
if (value != _parentAmount)
{
_parentAmount = value;
NotifyPropertyChanged("parentAmount");
}
}
}
private ObservableCollection<ChildViewModel> _children;
public ObservableCollection<ChildViewModel> children
{
get { return _children; }
set
{
if (value != _children)
{
_children = value;
foreach (var child in _children)
child.PropertyChanged += ChildOnPropertyChanged;
NotifyPropertyChanged("children");
}
}
}
private void ChildOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
parentAmount = children.Sum(p => p.ChildAmount);
}
}
class ChildViewModel : BaseViewModel
{
private double _ChildAmount;
public double ChildAmount
{
get { return _ChildAmount; }
set
{
if (value != _ChildAmount)
{
_ChildAmount = value;
NotifyPropertyChanged("ChildAmount");
}
}
}
}
public class BaseViewModel : INotifyPropertyChanged
{
protected void NotifyPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
source to share
A more elegant way is to use Reactive Extensions .
First you need to grab
Rx-Main
from Package Manager Console .
Then create static class
to host your extension method implemented using Rx. Something like that -
public static class Extensions
{
public static IObservable<T> OnPropertyChanges<T>(this T source, string propertyName)
where T : INotifyPropertyChanged
{
return Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(
handler => handler.Invoke,
h => source.PropertyChanged += h,
h => source.PropertyChanged -= h)
.Where(p => p.EventArgs.PropertyName == propertyName)
.Select(_ => source);
}
}
Finally, you call this method in your constructor ParentViewModel
(or anywhere).
// whenever the ChildAmount property of any ChildViewModel has changed, do something
Observable.Merge(children.Select(c => c.OnPropertyChanges("ChildAmount")))
.Subscribe((c) =>
{
// update your parent amount here
NotifyPropertyChanged("parentAmount");
});
source to share