Do you have control of what goes inside your collections? If you do, you can create a Parent property on your Invoice object and when it is added to the collection, set the parent to your Customer. Then when PaidInFull gets set, run your Customer.OnPropertyChanged("TotalOpenInvoices") or call a method on the Customer object to recalculate your invoices.
Is there a way to trigger some kind of `OnPropertyChanged` event for a computed property that uses a property of a "child entity" in a collection?
-
27-11-2021 - |
Question
Is there a way to trigger some kind of OnPropertyChanged
event for a computed property that uses a property of a "child entity" in a collection?
A small example:
I have a simple WPF application with a DataGrid showing Customer properties. I am using Entity Framework 5, CodeFirst approach, so I wrote my classes manually with my own INotifyPropertyChanged implementation.
public partial class Customer : INotifyPropertyChanged
{
private string _firstName;
public virtual string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
OnPropertyChanged("FirstName");
OnPropertyChanged("FullName");
}
}
private string _lastName;
public virtual string LastName
{
get { return _lastName; }
set
{
_lastName = value;
OnPropertyChanged("LastName");
OnPropertyChanged("FullName");
}
}
public virtual ICollection<Car> Cars { get; set; }
public virtual ICollection<Invoice> Invoices { get; set; }
...
}
Now in that same class I created 3 computed properties:
public string FullName
{
get { return (FirstName + " " + LastName).Trim(); }
}
public int TotalCars
{
get
{
return Cars.Count();
}
}
public int TotalOpenInvoices
{
get
{
if (Invoices != null)
return (from i in Invoices
where i.PayedInFull == false
select i).Count();
else return 0;
}
}
The FullName
is automatically updated in the DataGrid because I'm calling OnPropertyChanged("FullName");
I found an example of the INotifyCollectionChanged implementation that I can probably use to auto update the TotalCars
when something is added to or removed from the ICollection:
http://www.dotnetfunda.com/articles/article886-change-notification-for-objects-and-collections.aspx
But what is the best approach to trigger the OnPropertyChange("TotalOpenInvoices")
when a property (PayedInFull
) inside the ICollection (Invoices
) changes?
Doing something like OnPropertyChanged("Customer.TotalOpenInvoices");
in the Invoice class doesn't seem to do the trick... :)
Solution
OTHER TIPS
This assumes that Invoice and Customer both implement IPropertyChanged. Simply change your collection to an ObservableCollection, and watch the CollectionChanged property.
When new Invoices are added, hook up an event handler to the PropertyChanged event of that Invoice. When an item is removed from the collection, remove that event handler.
Then, just call your NotifyPropertyChanged function on your TotalOpenInvoices property.
For example (not completely tested, but it should be close):
Invoice
public class Invoice : INotifyPropertyChanged
{
private bool payedInFull = false;
public bool PayedInFull
{
get { return payedInFull; }
set
{
payedInFull = value;
NotifyPropertyChanged("PayedInFull");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Customer
public class Customer : INotifyPropertyChanged
{
public Customer()
{
this.Invoices.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Invoices_CollectionChanged);
}
ObservableCollection<Invoice> Invoices
{
get;
set;
}
public int TotalOpenInvoices
{
get
{
if (Invoices != null)
return (from i in Invoices
where i.PayedInFull == false
select i).Count();
else return 0;
}
}
void Invoices_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
foreach (Invoice invoice in e.NewItems)
{
invoice.PropertyChanged += new PropertyChangedEventHandler(invoice_PropertyChanged);
NotifyPropertyChanged("TotalOpenInvoices");
}
foreach (Invoice invoice in e.OldItems)
{
invoice.PropertyChanged -= new PropertyChangedEventHandler(invoice_PropertyChanged);
NotifyPropertyChanged("TotalOpenInvoices");
}
}
void invoice_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "PayedInFull")
NotifyPropertyChanged("TotalOpenInvoices");
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Note that each class could be abstracted to a single class that implements INotifyPropertyChanged, such as a ViewModelBase class, but that is not necessarily required for this example.