Reputation: 602
I've been trying to figure out how to do this for a couple of days now.
It's a fairly common problem so I'll explain the situation as generically as possible so maybe others can get a bit of use out of it.
I have a list view on my WPF (using MVVM) dialog, it's bound to an observable collection of items with, say, five properties which are displayed in seperate columns.
I call a function which iterates over all the items and changes one of their properties. This function takes a while to get through all the items so I want it to update each item as it goes.
What are the options to do this so the UI remains responsive, and which is the simplest to implement?
Upvotes: 3
Views: 1069
Reputation: 4760
A simple way, and also for people who are working on Netframework 3.5, could be work on a background thread and synchronize using the synchronization contest. Try this:
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();
Upvotes: 0
Reputation: 2323
I would use an ObservableCollection Extender that allows you to update the collection in another thread. This is what I use in my applications when dealing with collections:
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;
}
Upvotes: 2
Reputation: 62246
If you are using C# 4.0
, use
Task.Factory.StartNew(new Action(() =>
{
.....
//CALL YOUR UPDATE METHOD
})).ContinueWith( { //something to execute after, if need}..)
and when set ModelView
object from other thread, use
Application.Current.Dispatcher.Invoke(new Action(() =>
{
//set ModelView object properties here
}));
Upvotes: 4