向ObservableCollection中添加项时的线程问题

7

我正在更新一个WPF ViewModel的ObservableCollection,在WCF数据服务异步查询回调方法中:

ObservableCollection<Ent2> mymodcoll = new ObservableCollection<Ent2>();
 ...
query.BeginExecute(OnMyQueryComplete, query);
 ...
private void OnMyQueryComplete(IAsyncResult result)
    {
        ...
        var repcoll = query.EndExecute(result);

        if (mymodcoll.Any())
        {
            foreach (Ent c in repcoll)
            {
                var myItem = mymodcoll.Where(p => p.EntID == c.EntID).FirstOrDefault();
                if (myItem != null) 
                {
                    myItem.DateAndTime = c.DateAndTime; // here no problems
                    myItem.Description = c.Description;
                     ...
                }
                else
                {
                    mymodcoll.Add(new Ent2 //here I get a runtime error
                    {
                        EntID = c.EntID,
                        Description = c.Description,
                        DateAndTime = c.DateAndTime,
                        ...
                    });
                }
            }
        }
        else
        {
            foreach (Ent c in repcoll)
            {
                mymodcoll.Add(new Ent2 //here, on initial filling, there's no error
                {
                    EntID = c.EntID,
                    Description = c.Description,
                    DateAndTime = c.DateAndTime,
                    ...
                });
            }
        }
    }  

问题是,当一个查询结果集合包含一个目标集合中不存在的项目并且我需要添加此项目时,我会收到运行时错误:调用线程无法访问此对象,因为不同的线程拥有它。(我通过注释指出了这行代码)
然而,如果目标集合为空(在初始填充时),所有项目都已成功添加而没有任何问题。(这部分代码我也通过注释指出)。当一个项目只需要更新它的一些字段时,也没有问题,该项目可以成功更新。
我该如何解决这个问题?
1个回答

3

第一种情况:您在修改集合中的对象,而不是集合本身 - 因此不会触发 CollectionChanged 事件。

第二种情况:您正在从不同的线程向集合添加新元素,因此触发了 CollectionChanged 事件。由于数据绑定,此事件需要在UI线程中执行。

我已经遇到过这个问题多次了,解决方法并不完美(如果有更好的解决方法,请告诉我!)。您将需要从 ObservableCollection<T> 派生,并将其传递给GUI线程调度程序上的 BeginInvokeInvoke 方法的委托。

示例:

public class SmartObservableCollection<T> : ObservableCollection<T>
{
    [DebuggerStepThrough]
    public SmartObservableCollection(Action<Action> dispatchingAction = null)
        : base()
    {
        iSuspendCollectionChangeNotification = false;
        if (dispatchingAction != null)
            iDispatchingAction = dispatchingAction;
        else
            iDispatchingAction = a => a();
    }

    private bool iSuspendCollectionChangeNotification;
    private Action<Action> iDispatchingAction;

    [DebuggerStepThrough]
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (!iSuspendCollectionChangeNotification)
        {
            using (IDisposable disposeable = this.BlockReentrancy())
            {
                iDispatchingAction(() =>
                {
                    base.OnCollectionChanged(e);
                });
            }
        }
    }
    [DebuggerStepThrough]
    public void SuspendCollectionChangeNotification()
    {
        iSuspendCollectionChangeNotification = true;
    }
    [DebuggerStepThrough]
    public void ResumeCollectionChangeNotification()
    {
        iSuspendCollectionChangeNotification = false;
    }


    [DebuggerStepThrough]
    public void AddRange(IEnumerable<T> items)
    {
        this.SuspendCollectionChangeNotification();
        try
        {
            foreach (var i in items)
            {
                base.InsertItem(base.Count, i);
            }
        }
        finally
        {
            this.ResumeCollectionChangeNotification();
            var arg = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
            this.OnCollectionChanged(arg);
        }
    }


}

谢谢!我尝试了您的方法,将我的目标集合制作成 SmartObservableCollection 类型。但是现在我在您的基类中遇到了相同的错误,在 OnCollectionChanged(NotifyCollectionChangedEventArgs e) 方法的 base.OnCollectionChanged(e); 代码行中。我可能错过了什么? - rem
дҪ йңҖиҰҒдёәдёҺUIзәҝзЁӢзӣёе…іиҒ”зҡ„Dispatcherзҡ„InvokeжҲ–BeginInvokeж–№жі•жҸҗдҫӣдёҖдёӘ委жүҳгҖӮ - Femaref
“this.BlockReentrancy()” 方法是什么?这是你写的还是WPF中的东西?在WP7 / Silverlight中不存在。 - Frode Lillerud
BlockReentrancy。基本上,它可以防止在使用块期间对集合进行更改。不知道为什么它不在wp7 / silverlight中。 - Femaref

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接