【问题标题】: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?有没有办法为使用集合中“子实体”属性的计算属性触发某种“OnPropertyChanged”事件?
【发布时间】:2012-11-06 19:21:38
【问题描述】:

有没有办法为使用集合中“子实体”属性的计算属性触发某种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 实现的示例,当向 ICollection 添加或删除某些内容时,我可能可以使用它来自动更新 TotalCarshttp://www.dotnetfunda.com/articles/article886-change-notification-for-objects-and-collections.aspx

但是当 ICollection (Invoices) 中的属性 (PayedInFull) 发生更改时,触发 OnPropertyChange("TotalOpenInvoices") 的最佳方法是什么?

在 Invoice 类中执行类似 OnPropertyChanged("Customer.TotalOpenInvoices"); 的操作似乎并不能解决问题... :)

【问题讨论】:

  • 您可以调用客户的属性更改。
  • 这个特殊的问题导致我最终创建了自己的 MVVM 框架
  • 您可以使用手动方法.. 或开始使用表达式树来找出您必须在哪个实例中引发 PropertyChanged 事件。

标签: c# wpf datagrid entity inotifycollectionchanged


【解决方案1】:

您可以控制收藏中的内容吗?如果这样做,您可以在 Invoice 对象上创建 Parent 属性,然后将其添加到集合中,将父级设置为 Customer。然后,当 PaidInFull 被设置时,运行您的 Customer.OnPropertyChanged("TotalOpenInvoices") 或调用 Customer 对象上的方法来重新计算您的发票。

Enforcing parent-child relationship in C# and .Net

【讨论】:

  • @HighCore +1 给你。我不太确定你指的是什么。
  • 谢谢,无论如何我总是建议让这些东西尽可能通用和可重用,这就是我告诉你表达式树的原因。
  • @HighCore 请回答。它将帮助 ivanv 和其他任何可能看到这个问题的人。我没有使用过表达式树,所以我无法评论它。
  • 感谢您的回复!在我的 Invoice 类中,我已经有一个 Customer 导航属性,Entity Framework 使用该属性将这些类连接在一起。所以这实际上是我的 Parent 财产。我没有在我的 Invoice 类中调用 OnPropertyChanged("Customer.TotalOpenInvoices"),而是将其更改为 Customer.OnPropertyChanged("TotalOpenInvoices")... 我确实必须将 OnPropertyChanged 从“受保护”更改为“内部”。但现在它就像一个魅力!
  • 感谢关于表达式树的提示。我也会检查一下:)
【解决方案2】:

这假设 Invoice 和 Customer 都实现了 IPropertyChanged。只需将您的集合更改为 ObservableCollection,然后观察 CollectionChanged 属性。

添加新发票时,将事件处理程序连接到该发票的 PropertyChanged 事件。当从集合中移除一个项目时,移除该事件处理程序。

然后,只需在 TotalOpenInvoices 属性上调用 NotifyPropertyChanged 函数。

例如(未完全测试,但应该很接近):

发票

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 类,但这不是本示例所必需的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-02-27
    • 2017-04-27
    • 1970-01-01
    • 2018-05-14
    • 1970-01-01
    • 2021-12-17
    • 2011-06-02
    • 1970-01-01
    相关资源
    最近更新 更多