Reputation: 817
Say I have a collection of order line objects...
public class OrderLine {
public decimal Qty { get; set; }
public decimal Cost { get; set; }
public decimal CostExt {
get {
return Qty * Cost;
}
}
}
I can implement INotifyPropertyChanged
in Qty
and Cost
when those values are modified, and my view will be notified of the update. However, CostExt
is an aggregate value, it relies on both Qty
and Cost
, meaning that I am forced to invoke PropertyChanged
on it as well, in each property that it relates to! In a large view-model with potentially hundreds of properties and various aggregates sprinkled throughout, this would become a nightmare to maintain. Is there a way to coerce the view to re-examine the aggregate without any undue suffering?
Edit: OK, just to be clear, the design pattern I'm looking for will allow me to simply tell the aggregate(CostExt
, in this case) what properties it should consider integral to its binding. Qty
, Cost
, and any other property involved should need to know nothing about the fact that they are being read from by the aggregate. The end result will be a much cleaner view-model without the need to rewrite existing properties because a new aggregate requires them. Simple, eh?
Upvotes: 2
Views: 444
Reputation: 3258
If you don't want to manually implement INotifyPropertyChanged, use Fody PropertyChanged.
Your code:
[ImplementPropertyChanged]
public class Person
{
public string GivenNames { get; set; }
public string FamilyName { get; set; }
public string FullName
{
get
{
return string.Format("{0} {1}", GivenNames, FamilyName);
}
}
}
When compiled:
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
string givenNames;
public string GivenNames
{
get { return givenNames; }
set
{
if (value != givenNames)
{
givenNames = value;
OnPropertyChanged("GivenNames");
OnPropertyChanged("FullName");
}
}
}
string familyName;
public string FamilyName
{
get { return familyName; }
set
{
if (value != familyName)
{
familyName = value;
OnPropertyChanged("FamilyName");
OnPropertyChanged("FullName");
}
}
}
public string FullName
{
get
{
return string.Format("{0} {1}", GivenNames, FamilyName);
}
}
public virtual void OnPropertyChanged(string propertyName)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Notice that properties that depends on other properties will get picked up.
Upvotes: 1
Reputation: 11
How about something like this:
public class OrderLine : INotifyPropertyChanged {
decimal qty;
decimal cost;
public decimal Qty {
get { return qty; }
set { SetNotify(ref qty, value, "Qty", "CostExt"); }
}
public decimal Cost {
get { return cost; }
set { SetNotify(ref cost, value, "Cost", "CostExt"); }
}
public decimal CostExt {
get {
return Qty * Cost;
}
}
protected void SetNotify<T>(ref T property, T value, params string[] notificationProperties) {
var method = PropertyChanged;
if (method != null) {
foreach (var p in notificationProperties) {
method(this, new PropertyChangedEventArgs(p));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Upvotes: 0
Reputation: 16966
Very simple, Raise ProperttyChanged
event on CostExt
when ever other dependent properties are changed.
public decimal Qty
{
get { return _qty; }
set
{
_qty = value;
OnPropertyChanged("Qty");
OnPropertyChanged("CostExt");
}
}
Your (complete)implementation should be like
public class OrderLine : INotifyPropertyChanged
{
private decimal _qty;
private decimal _cost;
public decimal Qty
{
get { return _qty; }
set
{
_qty = value;
OnPropertyChanged("Qty");
OnPropertyChanged("CostExt");
}
}
public decimal Cost
{
get { return _cost; }
set
{
_cost = value;
OnPropertyChanged("Cost");
OnPropertyChanged("CostExt");
}
}
public decimal CostExt
{
get
{
return Qty * Cost;
}
}
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Upvotes: 1