我有一个自定义的并发可观察的集合,正在WPF桌面应用程序中用作ItemsSource
。
为了使集合“可观察”,我实现了INotifyCollectionChanged
。由于它是“并发的”,即可以从多个线程进行修改,因此我正在使用CollectionChanged
调用System.Windows.Threading.Dispatcher
事件(如文档所建议)。
因为我希望UI元素能够实时更新,例如当属性更改时(也称为“实时成形”)对列表进行重新排序,我还实现了ICollectionViewFactory
来使用其设置创建所需的视图,例如SortDescriptions
。
考虑以下代码流-全部在UI /调度程序线程上:
CollectionChanged
事件。Window
加载了ListBox
并将其绑定到集合。我有一个函数的三个版本,每当(我的自定义集合)的内部列表更改时,该函数就会被调用:
版本1 (带有CheckAccess
和InvokeAsync
)
private void _notify(NotifyCollectionChangedEventArgs args)
{
if (_dispatcher.CheckAccess())
{
CollectionChanged?.Invoke(this, args);
}
else
{
_dispatcher.InvokeAsync(() => CollectionChanged?.Invoke(this, args));
}
}
版本2 (没有CheckAccess
和InvokeAsync
)
private void _notify(NotifyCollectionChangedEventArgs args)
{
_dispatcher.InvokeAsync(() => CollectionChanged?.Invoke(this, args));
}
版本3 (没有CheckAccess
和Invoke
)
private void _notify(NotifyCollectionChangedEventArgs args)
{
_dispatcher.Invoke(() => CollectionChanged?.Invoke(this, args));
}
版本1和3可以正常使用,但是在版本2中,所有项目在“列表框”中显示两次。
似乎是这样的:
Dispatcher.InvokeAsync
,则该调用将添加到“ UI消息泵的末尾”,而无需线程等待结果。CollectionView
将项目添加到其源中,从而创建重复的条目。我(认为我)理解在版本1中,事件是在UI元素存在之前触发(并等待)的,因此与CollectionView
无关。
但是为什么/ 如何第3版(带有Invoke
)起作用?代码的行为方式不同于使用InvokeAsync
时的行为方式,使我认为它应该死锁,因为它等待应该在“进一步的消息泵”下处理的调用,但显然不是。 Invoke
是否在内部进行某种CheckAccess
?
答案 0 :(得分:1)
Dispatcher.Invoke是否在内部调用CheckAccess吗?
是的,您可以找到详细信息here
public void Invoke(Action callback, DispatcherPriority priority, CancellationToken cancellationToken, TimeSpan timeout)
{
if(callback == null)
{
throw new ArgumentNullException("callback");
}
ValidatePriority(priority, "priority");
if( timeout.TotalMilliseconds < 0 &&
timeout != TimeSpan.FromMilliseconds(-1))
{
throw new ArgumentOutOfRangeException("timeout");
}
// Fast-Path: if on the same thread, and invoking at Send priority,
// and the cancellation token is not already canceled, then just
// call the callback directly.
if(!cancellationToken.IsCancellationRequested && priority == DispatcherPriority.Send && CheckAccess())