ManualResetEvent WaitOne阻止我的CollectionView的所有者Thread

 王丹__-划_402 发布于 2023-02-09 19:51

我编写了一个WPF WizardFramework,它使用一些在后台执行一些操作BackgroundWorker.在处理时,我必须更新ObservableCollection绑定到我的UI的内容.

对于这种情况,我写了一个ThreadableObservableCollection,它提供线程安全的方法Insert,RemoveRemoveAt.虽然我使用的是.NET 4.5,但如果BindingOperations.EnableCollectionSynchronization没有其他许多无效的访问异常,我就无法工作.我Collection看起来像:

  public class ThreadableObservableCollection : ObservableCollection
  {
    private readonly Dispatcher _dispatcher;
    public ThreadableObservableCollection()
    {
      _dispatcher = Dispatcher.CurrentDispatcher;
    }

    public void ThreadsafeInsert(int pos, T item, Action callback)
    {
      if (_dispatcher.CheckAccess())
      {
        Insert(pos, item);
        callback();
      }
      else
      {
        _dispatcher.Invoke(() =>
          {
            Insert(pos, item);
            callback();
          });
      }
    }

    [..]
  }

这是按预期工作,而我在我的应用程序中使用向导.现在我正在使用NUnit为应用程序编写一些集成测试.

有一个监听器等待WizardViewModel完成它的工作并寻找一些在Steps-Collection中注入的页面.完成asyncrone工作后,我可以使用Validate来检查viewmodel状态.

不幸的是我正在使用a ManualResetEvent来等待向导关闭.这看起来如下:

  public class WizardValidator : IValidator, IDisposable
  {
    private WizardViewModel _dialog;
    private readonly ManualResetEvent _dialogClosed = new ManualResetEvent(false);

    [..]

    public void ListenTo(WizardViewModel dialog)
    {
      _dialog = dialog;
      dialog.RequestClose += (sender, args) => _dialogClosed.Set();
      dialog.StepsDefaultView.CurrentChanged += StepsDefaultViewOnCurrentChanged;

      _dialogClosed.WaitOne();
    }

    [..]
 }

现在出现了一个问题:当应用程序运行时,UI线程未被阻止,可以毫无问题地更新Collection.但是在我的测试用例中,我初始化ViewModel的"主"线程(以及因为它的集合)是一个被测试代码阻止的AppDomainThread.现在我ThreadsafeInsert想更新集合但不能使用AppDomain线程.

但是我必须等待向导完成,我该如何解决这种僵局?或者是否有更优雅的解决方案?

编辑: 我解决了这个问题,检查是否有用户界面,然后我才在Application-Thread上调用,否则我会故意在另一个线程上更改集合.这不会阻止异常,但是从测试中无法识别...但是仍然插入了项目,只NotifyCollectionChanged调用了-Handler(它只在UI中使用).

  if (Application.Current != null)
  {
    Application.Current.Dispatcher.Invoke(() =>
      {
        Steps.Insert(pos, step);
        stepsView.MoveCurrentTo(step);
      });
  }
  else
  {
    new Action(() => Steps.Insert(pos, step)).BeginInvoke(ar => stepsView.MoveCurrentToPosition(pos), null);  
  }

这是一个丑陋的解决方法,我仍然对一个干净的解决方案感兴趣.

有没有办法使用备用Dispatcher来创建(例如)整个ViewModel并使用它来更改我的集合?

撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有