使用TrulyObservableCollection从CollectionChanged事件获取更改的项目

时间:2012-11-15 16:01:45

标签: c# wpf class events observablecollection

我在WPF DataGrid中使用TrulyObservableCollection作为数据源。我的类正确地实现了PropertyChange事件(当属性发生变化时我收到通知)。 CollectionChanged事件也会被触发。但是,我的问题在于PropertyChanged事件和CollectionChanged事件之间的关联。我可以在PropertyChanged事件中看到哪个项目正在被更改(在这种情况下是sender对象),但我似乎无法找到一种方法来查看哪个项目已从CollectionChanged事件。 sender对象是整个集合。在CollectionChanged事件中查看哪个项目发生了变化的最佳方法是什么?相关的代码片段如下。感谢您的帮助,如果需要澄清,请告诉我。

设置集合的代码:

    private void populateBret()
    {
        bretList = new TrulyObservableCollection<BestServiceLibrary.bretItem>(BestClass.BestService.getBretList().ToList());
        bretList.CollectionChanged += bretList_CollectionChanged;
        dgBretList.ItemsSource = bretList;
        dgBretList.Items.Refresh();
    }

    void bretList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        //Do stuff here with the specific item that has changed
    }

集合中使用的类:

public class bretItem : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private int _blID;
    public string _blGroup;

    [DataMember]
    public int blID
    {
        get { return _blID; }
        set
        {
            _blID = value;
            OnPropertyChanged("blID");
        }
    }

    [DataMember]
    public string blGroup
    {
        get { return _blGroup; }
        set
        {
            _blGroup = value;
            OnPropertyChanged("blGroup");
        }
    }

    protected void OnPropertyChanged (String name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
}

TrulyObservableCollection类

public class TrulyObservableCollection<T> : ObservableCollection<T> where T : INotifyPropertyChanged
    {
        public TrulyObservableCollection()
            : base()
        {
            CollectionChanged += new NotifyCollectionChangedEventHandler(TrulyObservableCollection_CollectionChanged);
        }
        public TrulyObservableCollection(List<T> list)
            : base(list)
        {
            foreach (var item in list)
            {
                item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
            }
            CollectionChanged += new NotifyCollectionChangedEventHandler(TrulyObservableCollection_CollectionChanged);
        }

        void TrulyObservableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                foreach (Object item in e.NewItems)
                {
                    (item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
                }
            }
            if (e.OldItems != null)
            {
                foreach (Object item in e.OldItems)
                {
                    (item as INotifyPropertyChanged).PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
                }
            }
        }

        void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            NotifyCollectionChangedEventArgs a = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
            OnCollectionChanged(a);
        }
    }

修改

item_PropertyChanged事件中,NotifyCollectionChangedEventArgs设置为NotifyCollectionChangedAction.Reset。这会导致OldItemsNewItems为空,因此在这种情况下我无法获取更改的项目。我无法使用.Add,因为Datagrid会使用其他项目进行更新。我无法让.Replace工作以获取更改的项目。

2 个答案:

答案 0 :(得分:3)

这个怎么样:

在包含ViewModel ObservableCollection的{​​{1}}中,ViewModel订阅bretItem的{​​{1}}事件。

这样可以防止需要从CollectionChanged派生的新类ObservableCollection与其集合中的项目相关联。

TrulyObservableCollection的处理程序中,您可以像现在一样添加和删除ObservableCollection事件处理程序。由于现在您的ViewModel被告知集合中对象的更改,您可以采取适当的措施。

PropertyChanged

注意事项:

顺便说一下,看起来你打破了基于这个片段的ViewModel模式:

public class BretListViewModel
{

    private void populateBret()
    {
        bretList = new ObservableCollection<BestServiceLibrary.bretItem>(BestClass.BestService.getBretList().ToList());
        bretList.CollectionChanged += bretList_CollectionChanged;              
    }

    void bretList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
         if (e.NewItems != null)
        {
            foreach (Object item in e.NewItems)
            {
                (item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
            }
        }
        if (e.OldItems != null)
        {
            foreach (Object item in e.OldItems)
            {
                (item as INotifyPropertyChanged).PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
            }
        }
    }


    void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        var bret = sender as bretItem;

        //Update the database now!

        //One note:
        //The ObservableCollection raises its change event as each item changes.
        //You should consider a method of batching the changes (probably using an ICommand)
    }

}

您可能应考虑加载MVVM并将dgBretList.ItemsSource = bretList; dgBretList.Items.Refresh(); 绑定到ViewModel,而不是在View的代码隐藏中编码逻辑。

答案 1 :(得分:2)

以这种方式使用集合更改事件是不合适的,因为它只是在从集合中添加/删除项目时才会被触发。这就是你撞墙的原因。你也有用这种方法打破Liskov替代原则的危险。

最好在集合类上实现INotifyPropertyChanged接口,并在其中一个项触发其属性更改事件时触发该事件。