【问题标题】:Static ObservableCollection Event is not firing静态 ObservableCollection 事件未触发
【发布时间】:2017-03-23 13:28:29
【问题描述】:

我有以下 static ObservableCollection 使用 linq 更新。为什么事件没有触发?

public static class myViewModel
{

    private static ObservableCollection<ObjA> CollectionA = new ObservableCollection<ObjA>();
    private static ObservableCollection<ObjB> CollectionB = new ObservableCollection<ObjB>();

    static myViewModel()
    {
        CollectionA.CollectionChanged += new NotifyCollectionChangedEventHandler(myHandler);
        CollectionA = new ObservableCollection(CollectionB.Select(abc=> new ObjA(abc, True));
    }

    private static void myHandler(object sender, NotifyCollectionChangedEventArgs e)
    {  
        //To do
        throw new NotImplementedException();
    }

    private static void updateCollection()
    {

        foreach (var x in CollectionA)
        {
            CollectionA.field=5;
        }
    }
}

【问题讨论】:

  • 它没有触发,因为你没有改变集合,你甚至没有替换它

标签: c# linq events observablecollection


【解决方案1】:

第一步:给CollectionA 一个事件处理程序。

CollectionA.CollectionChanged += new NotifyCollectionChangedEventHandler(myHandler);

第二步:丢弃 CollectionA 并将其替换为没有处理程序的不同集合。

CollectionA = new ObservableCollection(CollectionB.Select(abc=> new ObjA(abc, true));

看看你在那里做了什么?

CollectionA 返回对集合对象的引用。您没有向该集合对象添加项目。您正在用不同的集合对象替换该集合对象。

将项目添加到现有集合中:

CollectionA.CollectionChanged += new NotifyCollectionChangedEventHandler(myHandler);

foreach (var x in CollectionB.Select(abc=> new ObjA(abc, true)))
{
    CollectionA.Add(x);
}

如果你真的想一次全部替换集合,你需要将处理程序添加到 new 集合中:

CollectionA = new ObservableCollection(CollectionB.Select(abc=> new ObjA(abc, true));

CollectionA.CollectionChanged += myHandler;

如果myHandler 具有正确的参数和返回类型,则不需要new NotifyCollectionChangedEventHandler

处理此类事情的通常方法是使CollectionA 成为添加处理程序本身的属性:

private static ObservableCollection<ObjA> _collectionA;
public static ObservableCollection<ObjA> CollectionA {
    get { return _collectionA; }
    set {
        if (_collectionA != value)
        {
            //  Remove handler from old collection, if any
            if (_collectionA != null)
            {
                _collectionA.CollectionChanged -= myHandler;
            }

            _collectionA = value;

            if (_collectionA != null)
            {
                _collectionA.CollectionChanged += myHandler;

                //  Whatever myHandler does on new items, you probably want to do 
                //  that here for each item in the new collection. 
            }
        }
    }
}

static myViewModel()
{
    //  Now, whenever you replace CollectionA, the setter will add the changed 
    //  handler automatically and you don't have to think about it. 
    CollectionA = new ObservableCollection(CollectionB.Select(abc=> new(abc, True));
}

更新

现在,我们对这些物品做什么?也许我们想知道它们的属性何时发生变化。 ObservableCollection 不会为我们这样做,但我们可以自己连接。

考虑以更方便的可重用方式重构此代码的方法很有趣。

private static ObservableCollection<ObjA> _collectionA;
public static ObservableCollection<ObjA> CollectionA
{
    get { return _collectionA; }
    set
    {
        if (_collectionA != value)
        {
            //  Remove handler from old collection, if any
            if (_collectionA != null)
            {
                _collectionA.CollectionChanged -= myHandler;
            }

            //  1. Remove property changed handlers from old collection items (if old collection not null) 
            //  2. Add property changed to new collection items (if new collection not null) 
            AddAndRemovePropertyChangedHandlers(_collectionA, value, ObjA_PropertyChanged);

            _collectionA = value;

            if (_collectionA != null)
            {
                _collectionA.CollectionChanged += myHandler;
            }
        }
    }
}

//  NotifyCollectionChangedEventArgs gives us non-generic IList rather than IEnumerable
//  but all we're doing is foreach, so make it as general as possible. 
protected static void AddAndRemovePropertyChangedHandlers(
    System.Collections.IEnumerable oldItems, 
    System.Collections.IEnumerable newItems, 
    PropertyChangedEventHandler handler)
{
    if (oldItems != null)
    {
        //  Some items may not implement INotifyPropertyChanged. 
        foreach (INotifyPropertyChanged oldItem in oldItems.Cast<Object>()
            .Where(item => item is INotifyPropertyChanged))
        {
            oldItem.PropertyChanged -= handler;
        }
    }

    if (newItems != null)
    {
        foreach (INotifyPropertyChanged newItem in newItems.Cast<Object>()
            .Where(item => item is INotifyPropertyChanged))
        {
            newItem.PropertyChanged += handler;
        }
    }
}

private static void ObjA_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
}

private static void myHandler(object sender, 
    System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
    //  If e.Action is Reset, you don't get the items that were removed. Oh well. 
    AddAndRemovePropertyChangedHandlers(e.OldItems, e.NewItems, ObjA_PropertyChanged);
}

【讨论】:

  • 是的,我的意思是新的 ObjA(abc, true)
  • 在替换 _collectionA 之前,您可能应该执行 _collectionA.CollectionChanged -= myHandler;
  • @Default 我不想把它弄乱,但是......是的,你是对的。
  • @Ed Plunkett 是否应该在可观察集合中的项目被修改时触发该事件?
  • @John 不。不能也不会。如果您希望在更新中发生这种情况,我将展示该怎么做。
猜你喜欢
  • 1970-01-01
  • 2017-05-07
  • 1970-01-01
  • 1970-01-01
  • 2015-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多