Joshua Mee
Joshua Mee

Reputation: 602

Maintaining a responsive WPF UI

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

Answers (3)

Raúl Otaño
Raúl Otaño

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

Xcalibur37
Xcalibur37

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

Tigran
Tigran

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

Related Questions