• Make ObservableCollection to thread safe (Updated)


    While playing around with WPF, I tried to do some multithreading where I have a worker thread updating my ObservableCollection, while having a ListCollectionView of that ObservableCollection being shown on a ListBox.

    It was surprising to see that I get a NotSupportedException thrown, with the message saying 'This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.'.  That doesn't seem to make sense - In my mind, I understand how the thread that created the UI should be the one that handles all UI updates.  However, the data itself should be able to reside anywhere, and I should be able to update it however and whenever I want.

    Looking for a solution, I created a class deriving from ObservableCollection (the class name I chose is ObservableCollectionEx) thinking that I would just manually walk through the event's invocation list.  Well, that didn't quite work, since events are not accessible (other than for adding/removing delegates) to child classes.  Looking at the documentation, the CollectionChanged event in ObservableCollection is virtual - that means I can override it! Yeah!  I am glad someone at Microsoft decided to make that virtual.

    So here's the code that I created - the pain is I now have to rename all occurrences of ObservableCollection to this new class.  Oh well, at least making it work with threads isn't too painful.

    public class ObservableCollectionEx<T> : ObservableCollection<T>
    {
       // Override the event so this class can access it
       public override event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged;
    
       protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
       {
          // Be nice - use BlockReentrancy like MSDN said
          using (BlockReentrancy())
          {
             System.Collections.Specialized.NotifyCollectionChangedEventHandler eventHandler = CollectionChanged;
             if (eventHandler == null)
                return;
        
             Delegate[] delegates = eventHandler.GetInvocationList();
             // Walk thru invocation list
             foreach (System.Collections.Specialized.NotifyCollectionChangedEventHandler handler in delegates)
             {
                DispatcherObject dispatcherObject = handler.Target as DispatcherObject;
                // If the subscriber is a DispatcherObject and different thread
                if (dispatcherObject != null && dispatcherObject.CheckAccess() == false)
                {
                   // Invoke handler in the target dispatcher's thread
                   dispatcherObject.Dispatcher.Invoke(DispatcherPriority.DataBind, handler, this, e);
                }
                else // Execute handler as is
                   handler(this, e);
             }
          }
       }
    }

    Update:

    There is a bug with the code where subscribers in the same thread won't get called - the code above has been updated with the fix.

    It also uses CheckAccess (available, but not shown via Intellisense as mentioned here).

    做个快乐的自己。
  • 相关阅读:
    unix domain socket 浅析
    Python单元测试的Mock是怎么回事
    三招搞定你的ubuntu安全问题
    思考一次整体调整Python项目规范性的过程
    不可缺少的程序埋点
    python + unittest + request + parameterized 参数化遇到中文名称testcase不显示的问题
    【CDH】cdh搭建遇到的坑和解决过程
    [Linux系统]安装时出现Requires: libc.so.6(GLIBC_2.17)(64bit) Requires: systemd Requires: libstdc++.so时解决办法
    【Linux命令】在Linux服务器上与windows通过SCP命令互传文件时出现的问题排查过程
    【微信公众号】记一次微信活动微信公众号分享没有LOGO的解决心路历程
  • 原文地址:https://www.cnblogs.com/Jessy/p/2293070.html
Copyright © 2020-2023  润新知