views:

614

answers:

5

I have a control that has its data bound to a standard ObservableCollection, and I have a background task that calls a service to get more data.

I want to, then, update my backing data behind my control, while displaying a "please wait" dialog, but when I add the new items to the collection, the UI thread locks up while it re-binds and updates my controls.

Can I get around this so that my animations and stuff keep running on my "please wait" dialog?

Or at least give the "appearance" to the user that its not locked up?

A: 

use BackgroundWorker to accomplish this task. update the obsrvablecollection in the DoWork method

Kishore Kumar
How does that update the UI thread? Maybe im not understanding this, but arent all my controls and the rendering behind them within the UI thread, so updating the collection in a background thread will just cause the UI thread to do the normal update?
Mark
A: 

use the dispatcher to avoid cross threading issues

Veer
A: 

Use this:


Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Render, new Action(UpdateData), value);

private void UpdateData(int value)
{
  BindingSourceProperty = value;
}


Rakesh Gunijan
what does it do? Why is it good? How will it help me?
Mark
lets say you have a WPF UI form having ProgressBar control as <ProgressBar Maximum="100" Value="{Binding Path=PercentValue}" /> where PercentValue is property defined in your DataContext attached to form. Assuming a scenario where you are getting callback data on some other thread indicating progress of operation and you want to display it on UI. If you set that value directly to your dependency source property 'PercentValue', you will get updated ui only when background thread operation is finished. So in order to get real time progress of percent value, set this value as above
Rakesh Gunijan
+5  A: 

If i understand correctly, you already use a BackgroundWorker to retrieve the data, and that simply assigning this data to the ObservableCollection is locking up the UI.

One way to avoid locking up the UI is to assign the data to the ObservableCollection in smaller chunks by queuing multiple dispatcher methods. Between each method call, UI events can be handled.

the following would add one item on at a time, that's a bit extreme, but it illustrates the concept.

void UpdateItems()
{
    //retrievedItems is the data you received from the service
    foreach(object item in retrievedItems)
        Dispatcher.BeginInvoke(DispatcherPriority.Background, new ParameterizedThreadStart(AddItem), item);    
}

void AddItem(object item)
{
    observableCollection.Add(item);
}
Bubblewrap
So by adding them to the items collection in a background thread will update the controls on the UI thread? How does that work?
Mark
No, you add them in the dispatcher thread, but with a lower priority and in smaller chucnks so the UI remains responsive
Bubblewrap
I see, thanks I'll give that a try
Mark
it worked very well, thanks a lot for the help!
Mark
Exactly what i was looking for. Made my day. Thanks.
Adrian Faciu
+4  A: 

ObservableCollection will raise CollectionChanged events that will force UI to rebind data, measure, arrange and redraw. This might take a lot of time if you have many updates coming.

It is possible to make user think that UI is alive by splitting the job in small packages. Use Dispatcher from UI thread (any control has reference to it) to schedule collection update actions with 10-100 items (determine number by experiment, these just to support the idea).

Your background code might looks like this:

void WorkInBackground()
{
    var results = new List<object>();

    //get results...

    // feed UI in packages no more than 100 items
    while (results.Count > 0)
    {
        Application.Current.MainWindow.Dispatcher.BeginInvoke(
            new Action<List<object>>(FeedUI),
            DispatcherPriority.Background,
            results.GetRange(0, Math.Min(results.Count, 100)));
        results.RemoveRange(0, Math.Min(results.Count, 100));
    }
}
void FeedUI(List<object> items)
{
    // items.Count must be small enough to keep UI looks alive
    foreach (var item in items)
    {
        MyCollection.Add(item);
    }
}
Sergey Galich