C# - 如何使用“INotifyPropertyChanged”更新集合类中的计算字段?

时间:2013-12-25 00:08:22

标签: c# collections inotifypropertychanged

我有一个Item类,其中包含两个属性“quantity”和“price”,并实现了INotifyPropertyChanged

public class Item:INotifyPropertyChanged
{
    private event PropertyChangedEventHandler _propertyChanged;
    public event PropertyChangedEventHandler PropertyChanged
    {
        add { _propertyChanged += value; }
        remove { _propertyChanged -= value; }
    }

    void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (_propertyChanged != null)
        {
            _propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public int QuantityOnHand
    {
        get
        {
            return this._quantityOnHand;
        }
        set
        {
            if (value > 0)
            {
                this._quantityOnHand = value;
                NotifyPropertyChanged();
            }
            else
            {
                throw new System.ArgumentException("Quantity must be a positive value!");
            }
        }
    }
    .....

}

我有一个名为“Inventory”的集合类,其属性为TotalRetailPrice:

public class Inventory {
private List<Item> _inventoryList = new LinkedList<Item>();
public decimal TotalRetailPrice
    {
        get 
        {
            decimal totalRetailPrice = 0M;
            foreach (var item in _inventoryList)
            {
                totalRetailPrice += item.QuantityOnHand * item.RetailPrice;
            }
            return totalRetailPrice;
        }
    }

我试图找到一种方法来自动更新此属性TotalRetailPrice,每当我更改列表中任何项目的数量或价格时。我怎样才能做到这一点?现在使用我的代码,每当我尝试获取此totalRetailPrice属性时,我将不得不浏览列表并重新计算它。

谢谢!

1 个答案:

答案 0 :(得分:3)

由于接口INotifyPropertyChanged公开了一个名为PropertyChanged的事件,您可以在“库存”类中订阅该事件。

您还需要侦听列表中已更改的事件,因为您需要知道何时添加/删除项目,以便您可以根据需要添加/删除事件处理程序。我建议使用ObservableCollection<T>,因为它支持一些'收集已更改'事件。你有没有理由使用LinkedList<T>

e.g。

public class Inventory 
{
    private ObservableCollection<Item> _inventoryList = new ObservableCollection<Item>();

    public decimal _total;
    // You probably want INPC for the property here too so it can update any UI elements bound to it
    public decimal Total { get { return _total; } set { _total = value; } }

    // Constructor     
    public Inventory() 
    {
        WireUpCollection();
    }

    private void WireUpCollection()
    {
        // Listen for change events
        _inventoryList.CollectionChanged += CollectionChanged;
    }

    private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        // Check what was added - I'll leave this to you, the e.NewItems are the items that
        // have been added, e.OldItems are those that have been removed

        // Here's a contrived example for when an item is added. 
        // Don't forget to also remove event handlers using inpc.PropertyChanged -= Collection_PropertyChanged;
        var inpc = e.NewItems[0] as INotifyPropertyChanged;

        if(inpc != null)
          inpc.PropertyChanged += Collection_PropertyChanged;
    }

    private void Collection_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        RecalculateTotal();
    }

    private void RecalculateTotal()
    { 
        // Your original code here which should feed the backing field
    }
}

在此处查看MSDN文档:

http://msdn.microsoft.com/en-us/library/ms668604(v=vs.110).aspx

有关ObservableCollection<T>的信息。事件部分就是你所追求的。另请注意,如果您喜欢语法或想要捕获某个范围内的变量等,您可以使用匿名函数来处理事件。这有助于完全理解它们(不确定Java可用的内容,因为我没有真正触及它除了几个Android乱七八糟的项目)所以它可能值得一读,因为在捕获时需要注意一些小注意事项,但这是另一个故事!

e.g。

_inventoryList.CollectionChanged += (o,e) => 
                                    { 
                                        // Anonymous method body here 
                                        // o = the first param (object sender), e = args (NotifyCollectionChangedEventArgs e)
                                    };