WPF类具有线程关联性。这意味着对这些对象的所有更改必须位于创建它们的同一个线程中。创建一个线程安全的用户界面API确实很难,所以微软选择保持它的单线程并强制运行时检查以确保它。
也就是说,有几个选项需要在后台线程中执行排序,然后将其应用于UI线程中。第一个选项是将您的SortableCollection复制到一个普通的旧List或Array中,并在后台执行排序。一旦后台线程完成,您就可以使用Dispatcher在UI线程中执行代码。 WPF中的每个UI元素都延伸System.Windows.Threading.DispatcherObject
,大部分延伸System.Windows.Freezable
。 DispatcherObject是让Dispatcher在UI线程中执行代码的地方。
从逻辑上讲,执行将是这样的:
public void BackgroundSort()
{
List<T> items = new List<T>(this.ToArray());
BackgroundSortDelegate del = Sort;
del.BeginInvoke(SortCompleted, del);
}
private void SortCompleted(IAsyncResult result)
{
BackgroundSortDelegate del = result.AsyncState as BackgroundSortDelegate;
List<T> items = del.EndInvoke(result);
this.Dispatcher.Invoke(()=>{this.Collection = items;});
}
所发生的短的解释是,后台工作/委托在此列表中使用项目的副本。排序完成后,我们调用Dispatcher对象并调用一个操作。在这个动作中,我们将新的排序列表分配给我们的对象。
在UI线程内分配任何后台工作结果的关键是使用UI的Dispatcher对象。实际上可能有六种方法在C#中调用后台工作程序,但将后台线程放到UI线程中的方法是相同的。
感谢Berin的解释, 但是,我必须把这种方法? 在SortableCollection中或者在处理排序方法的事件处理程序中? – Khaldoun 2010-12-21 14:21:05
无论它对你来说最合理的逻辑。在SortableCollection中做这件事的好处是你可以将SortableCollection传递给不同的方法,并且它们可以自动与它进行交互。请注意警告:调度程序是一项昂贵的操作。偶尔可以调用它,但是在一个紧密的循环中它会减慢速度。 – 2010-12-21 14:38:47