实现CollectionChanged

时间:2011-01-03 21:05:27

标签: c# wpf mvvm mvvm-light

我已将CollectionChanged eventhandler(onCollectionChanged)添加到ObservableCollection属性之一。

我发现仅在添加项目或将项目移除到集合的情况下才会调用onCollectionChanged方法,但是在收集项目被编辑的情况下则不会。

我想知道如何在一个集合中发送新添加,删除和编辑的项目的列表/集合。

感谢。

8 个答案:

答案 0 :(得分:45)

您必须为每个项目(必须实现PropertyChanged)添加INotifyPropertyChanged侦听器,以获取有关在可观察列表中编辑对象的通知。

public ObservableCollection<Item> Names { get; set; }
public List<Item> ModifiedItems { get; set; }

public ViewModel()
{
   this.ModifiedItems = new List<Item>();

   this.Names = new ObservableCollection<Item>();
   this.Names.CollectionChanged += this.OnCollectionChanged;
}

void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    if (e.NewItems != null)
    {
        foreach(Item newItem in e.NewItems)
        {
            ModifiedItems.Add(newItem);

            //Add listener for each item on PropertyChanged event
            newItem.PropertyChanged += this.OnItemPropertyChanged;         
        }
    }

    if (e.OldItems != null)
    {
        foreach(Item oldItem in e.OldItems)
        {
            ModifiedItems.Add(oldItem);

            oldItem.PropertyChanged -= this.OnItemPropertyChanged;
        }
    }
}

void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    Item item = sender as Item;
    if(item != null)
       ModifiedItems.Add(item);
}

也许您必须检查一些项目是否已经存在于ModifedItems-List中(使用List的方法包含(object obj))并且只有在该方法的结果为 false时才添加新项目

班级Item必须实施INotifyPropertyChanged。请参阅此example以了解具体方法。正如罗伯特罗斯尼所说,如果你有这个要求,你也可以用IEditableObject来做到这一点。

答案 1 :(得分:10)

ItemsControl侦听CollectionChanged以管理其在屏幕上显示的项目集合的显示。 ContentControl会监听PropertyChanged以管理其在屏幕上显示的特定项目的显示。一旦你理解了这两个概念,就很容易将这两个概念分开。

跟踪某个项是否被编辑不是这些接口中的任何一个。属性更改不是编辑 - 也就是说,它们不一定表示某种用户启动的对象状态更改。例如,一个对象可能具有ElapsedTime属性,该属性由计时器不断更新;需要通知UI这些属性更改事件,但它们肯定不代表对象基础数据的更改。

跟踪对象是否被编辑的标准方法是首先使该对象实现IEditableObject。然后,您可以在对象的类内部,决定哪些更改构成编辑(即要求您调用BeginEdit),哪些更改不会。然后,您可以实现一个布尔IsDirty属性,该属性在调用BeginEdit时设置,并在调用EndEditCancelEdit时清除。 (我真的不明白为什么该属性不属于IEditableObject;我还没有实现一个不需要它的可编辑对象。)

当然,如果你不需要它,就没有必要实现第二级抽象 - 你当然可以监听PropertyChanged事件,并假设对象在被引发时已被编辑。这实际上取决于您的要求。

答案 2 :(得分:4)

我对'this answer'的修改被拒绝了! 所以我把我的编辑放在这里:

void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
    foreach(Item newItem in e.NewItems)
    {
        ModifiedItems.Add(newItem);

        //Add listener for each item on PropertyChanged event
        if (e.Action == NotifyCollectionChangedAction.Add)
            newItem.PropertyChanged += this.ListTagInfo_PropertyChanged;
        else if (e.Action == NotifyCollectionChangedAction.Remove)
            newItem.PropertyChanged -= this.ListTagInfo_PropertyChanged;
    }
}

// MSDN: OldItems:Gets the list of items affected by a Replace, Remove, or Move action.  
//if (e.OldItems != null) <--- removed
}

答案 3 :(得分:2)

INotifyCollectionChangedINotiftyPropertyChanged不同。更改基础对象的属性不会以任何方式表明集合已更改。

实现此行为的一种方法是创建一个自定义集合,该集合将在添加后询问对象并注册INotifyPropertyChanged.PropertyChanged事件;然后,当项目被移除时,它需要适当地取消注册。

这种方法的一个警告是当你的对象嵌套N级深度时。要解决这个问题,您需要使用反射来查询每个属性,以确定它是否是另一个实现INotifyCollectionChanged或其他需要遍历的容器的集合。

这是一个未经测试的基本示例...

    public class ObservableCollectionExt<T> : ObservableCollection<T>
    {
        public override event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged;

        protected override void SetItem(int index, T item)
        {
            base.SetItem(index, item);

            if(item is INotifyPropertyChanged)
                (item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(OnPropertyChanged);
        }

        protected override void ClearItems()
        {
            for (int i = 0; i < this.Items.Count; i++)
                DeRegisterINotifyPropertyChanged(this.IndexOf(this.Items[i]));

            base.ClearItems();
        }

        protected override void InsertItem(int index, T item)
        {
            base.InsertItem(index, item);
            RegisterINotifyPropertyChanged(item);
        }

        protected override void RemoveItem(int index)
        {
            base.RemoveItem(index);
            DeRegisterINotifyPropertyChanged(index);
        }

        private void RegisterINotifyPropertyChanged(T item)
        {
            if (item is INotifyPropertyChanged)
                (item as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(OnPropertyChanged);
        }

        private void DeRegisterINotifyPropertyChanged(int index)
        {
            if (this.Items[index] is INotifyPropertyChanged)
                (this.Items[index] as INotifyPropertyChanged).PropertyChanged -= OnPropertyChanged;
        }

        private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            T item = (T)sender;
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset, item)); 
        }
    }

答案 4 :(得分:1)

认为使用ObservableCollection项填充INotifyPropertyChanged会导致CollectionChanged事件在项目提升PropertyChanged时触发通知。

第二个想法,我认为您需要使用BindingList<T>来使单个项目更改以这种方式传播开箱即用。

否则,您需要手动订阅每个项目的更改通知并引发CollectionChanged事件。请注意,如果您要创建自己的派生ObservableCollection<T>,则必须在实例化和Add()Insert()订阅,并取消订阅Remove(),{{ 1}}和RemoveAt()。否则,您可以订阅Clear()事件并使用事件参数中添加和删除的项目来订阅/取消订阅。

答案 5 :(得分:0)

在winforms中,BindingList是标准做法。在WPF&amp; Silverlight,您通常无法使用ObservableCollection并且需要在每个项目上收听PropertyChanged

答案 6 :(得分:0)

使用以下代码:

-my型号:

 public class IceCream: INotifyPropertyChanged
{
    private int liczba;

    public int Liczba
    {
        get { return liczba; }
        set { liczba = value;
        Zmiana("Liczba");
        }
    }

    public IceCream(){}

//in the same class implement the below-it will be responsible for track a changes

    public event PropertyChangedEventHandler PropertyChanged;

    private void Zmiana(string propertyName) 
    {
        if (PropertyChanged != null) 
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

在我的类PersonList实现方法中负责在AppBarControl中单击按钮后激活一个值

async private void Add_Click(object sender, RoutedEventArgs e)
    {
        List<IceCream> items = new List<IceCream>();
        foreach (IceCream item in IceCreamList.SelectedItems)
        {
            int i=Flavors.IndexOf(item);
            Flavors[i].Liczba =item.Liczba+ 1;
            //Flavors.Remove(item);

            //item.Liczba += 1;

           // items.Add(item);
           // Flavors.Add(item);
        }

        MessageDialog d = new MessageDialog("Zwiększono liczbę o jeden");
        d.Content = "Zwiększono liczbę o jeden";
        await d.ShowAsync();


        IceCreamList.SelectedIndex = -1;
    }
}

我希望对某人有用 请注意:

private ObservableCollection<IceCream> Flavors;

-

答案 7 :(得分:0)

我发现此限制的最简单的解决方案是使用RemoveAt(index)删除项目,然后使用InsertAt(index)在同一索引上添加修改的项目,因此ObservableCollection将通知视图。