我编写了一个WPF WizardFramework,它使用一些在后台执行一些操作BackgroundWorker
.在处理时,我必须更新ObservableCollection
绑定到我的UI的内容.
对于这种情况,我写了一个ThreadableObservableCollection
,它提供线程安全的方法Insert
,Remove
和RemoveAt
.虽然我使用的是.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并使用它来更改我的集合?