问题
I have a Windows 8.1 application with a parent and child viewmodel in the following relationship
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");
}
}
}
}
In the XAML there is TextBlock that is bound to the "ParentAmount" and then there is a ListView bound to the Observable collection "Children". ListView's Itemtemplate is a datatemplate with a TextBox with a two way bind to the "ChildAmount". The user can modify the value in the child TextBox
Now my requriement is to update the ParentAmount with the sum of all its child amount on the fly when the user modifies one of the child amounts. How do I achieve this?
For illustration purpose I have simplified the code example pasted above, the ChildViewModel has more functionality than what can be seen hence I can't replace that ObservableCollection of ChildViewModel with a List of double for instance.
I would be very glad if someone can point me in the right direction. Thanks in Advance.
回答1:
With a very small addition, this will do the trick. The specific changes are adding a property change handler for each child object in the ObservableCollection.
Note that this is a crude example to set you on the right track - I haven't unhooked the event handlers, and I recalculate the parent amount on any change from the child (i.e. I don't check that it was the ChildAmount that changed, this means you end up with more action than is necessary). I also haven't put in any code to handle changes to the contents of the ObservableCollection so if new items are added to it they won't have a property change event handler attached - this is simple for you to do yourself.
Note my use of a BaseViewModel - this is just good practice, it saves you from reimplementing the INotifyPropertyChanged interface on 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;
}
回答2:
A more elegant way is to use Reactive Extensions.
First you need to grab
Rx-Main
from Package Manager Console.
Then, create a static class
to host your extension method implemented using Rx. Something like this -
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);
}
}
Lastly, you call this method in your ParentViewModel
's constructor (or anywhere necessary).
// 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");
});
来源:https://stackoverflow.com/questions/27350868/how-to-update-a-value-in-parent-viewmodel-if-value-in-one-of-its-child-is-updat