Есть ли способ вызвать какое-то событие OnPropertyChanged для вычисляемого свойства, которое использует свойство "дочерней сущности" в коллекции?

Есть ли способ вызвать какой-то OnPropertyChanged событие для вычисляемого свойства, которое использует свойство "дочерней сущности" в коллекции?

Небольшой пример:

У меня есть простое приложение WPF с DataGrid, показывающим свойства клиента. Я использую Entity Framework 5, подход CodeFirst, поэтому я написал свои классы вручную с моей собственной реализацией INotifyPropertyChanged.

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; }

    ...
}

Теперь в этом же классе я создал 3 вычисляемых свойства:

    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;
        }
    }

FullName автоматически обновляется в DataGrid, потому что я звоню OnPropertyChanged("FullName");

Я нашел пример реализации INotifyCollectionChanged, которую я, вероятно, могу использовать для автоматического обновления TotalCars когда что-то добавляется или удаляется из коллекции ICollection: http://www.dotnetfunda.com/articles/article886-change-notification-for-objects-and-collections.aspx

Но что является лучшим подходом для запуска OnPropertyChange("TotalOpenInvoices") когда собственность (PayedInFullвнутри коллекции ICollection (Invoices) изменения?

Делать что-то вроде OnPropertyChanged("Customer.TotalOpenInvoices"); в классе Invoice, кажется, не добиваются цели...:)

2 ответа

Решение

У вас есть контроль над тем, что происходит внутри ваших коллекций? Если вы это сделаете, вы можете создать родительское свойство для своего объекта Invoice, и когда оно будет добавлено в коллекцию, установите родительское значение для вашего Customer. Затем, когда PaidInFull будет установлен, запустите ваш Customer.OnPropertyChanged("TotalOpenInvoices") или вызовите метод объекта Customer для пересчета ваших счетов.

Обеспечение родительско-дочерних отношений в C# и.Net

Это предполагает, что Invoice и Customer оба реализуют IPropertyChanged. Просто измените свою коллекцию на ObservableCollection и просмотрите свойство CollectionChanged.

При добавлении новых счетов-фактур подключите обработчик событий к событию PropertyChanged этого счета-фактуры. Когда элемент удаляется из коллекции, удалите этот обработчик событий.

Затем просто вызовите функцию NotifyPropertyChanged в свойстве TotalOpenInvoices.

Например (не полностью проверено, но должно быть близко):

Счет-фактура

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
    }

Покупатель

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
}

Обратите внимание, что каждый класс может быть абстрагирован до одного класса, который реализует INotifyPropertyChanged, такого как класс ViewModelBase, но это не обязательно требуется для этого примера.

Другие вопросы по тегам