几天来,我一直在试图弄清楚如何做到这一点。
这是一个相当普遍的问题,所以我将尽可能笼统地解释这种情况,以便其他人可以从中获得一些用处。
我的 WPF(使用 MVVM)对话框上有一个列表视图,它绑定到一个可观察的项目集合,例如,五个属性显示在单独的列中。
我调用了一个函数,它遍历所有项目并更改它们的一个属性。此功能需要一段时间才能完成所有项目,因此我希望它随时更新每个项目。
有哪些选项可以做到这一点,以使 UI 保持响应,哪个最容易实现?
如果您正在使用C# 4.0
,请使用
Task.Factory.StartNew(new Action(() =>
{
.....
//CALL YOUR UPDATE METHOD
})).ContinueWith( { //something to execute after, if need}..)
ModelView
当从其他线程设置对象时,使用
Application.Current.Dispatcher.Invoke(new Action(() =>
{
//set ModelView object properties here
}));
我会使用一个 ObservableCollection Extender,它允许您在另一个线程中更新集合。这是我在处理集合时在我的应用程序中使用的:
public class ObservableCollectionExtender<T> : ObservableCollection<T>
{
/// <summary>
/// Source: New Things I Learned
/// Title: Have worker thread update ObservableCollection that is bound to a ListCollectionView
/// http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/have-worker-thread-update-observablecollection-that-is-bound-to-a.aspx
/// Note: Improved for clarity and the following of proper coding standards.
/// </summary>
/// <param name="e"></param>
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
// Use BlockReentrancy
using (BlockReentrancy())
{
var eventHandler = CollectionChanged;
// Only proceed if handler exists.
if (eventHandler != null)
{
Delegate[] delegates = eventHandler.GetInvocationList();
// Walk thru invocation list
foreach (NotifyCollectionChangedEventHandler handler in delegates)
{
var currentDispatcher = handler.Target as DispatcherObject;
// If the subscriber is a DispatcherObject and different thread
if ((currentDispatcher != null) &&
(currentDispatcher.CheckAccess() == false))
{
// Invoke handler in the target dispatcher's thread
currentDispatcher.Dispatcher.Invoke(
DispatcherPriority.DataBind, handler, this, e);
}
else
{
handler(this, e);
}
}
}
}
}
/// <summary>
/// Overridden NotifyCollectionChangedEventHandler event.
/// </summary>
public override event NotifyCollectionChangedEventHandler CollectionChanged;
}
一种简单的方法,也适用于使用 Netframework 3.5 的人,可以在后台线程上工作并使用同步竞赛进行同步。试试这个:
var sync = SynchronizationContext.Current;
BackgroundWorker w = new BackgroundWorker();
w.DoWork+=(_, __)=>
{
foreach (var item in collection)
{
//calculate other things
sync.Post(p => { ...Actualize UI code... }, null);
}
}, null);
};
w.RunWorkerAsync();