2

I'm writing an app in C#/WPF (.Net framework 4.0). The one thing I am having a problem with is how to update controls, if possible, while the app is very busy. I think I'm just flailing about trying things I find on the web to make it work (and on rare occasions it does). The app has four classes, a main window class, a Worker1 class and a Worker2 class and a static ExtensionMethods class. Worker2 is called from Worker1 and does most of the work. When it runs the CPU is at 100% (core at 100%, system has four cores). (All are in the same namespace.)

public static class ExtensionMethods
{
    private static Action EmptyDelegate = delegate() { };

    public static void Refresh(this UIElement uiElement)
    {

        uiElement.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Render, EmptyDelegate);
    }
}

The main window calls a top level function in Worker1, which goes through some setup and then starts the long process, which goes something like this:

MainWindow mainWindow = App.Current.MainWindow as MainWindow;
mainWindow.dispatcher_UpdateProgressBarMaximum(maxValue);
int count=0;
Worker2 worker2 = new Worker2();
while (bytesRead =(readData() !=0) {
    count++;
    worker2.doWork();
    mainWindow.dispatcher_UpdateProgressBar(count);

}

In the mainWindow class is:

dispatcher_UpdateProgressBar(int Value)
{
Dispatcher.BeginInvoke(new Action(() => myProgressBar.Value = value), null);
        myProgressBar.Refresh();
}

(I have also tried it with: myProgressBar.Dispatcher.Begin...etc.

but neither matter too much. With small files where there are 2 or 3 reads I can see the progress bar getting updated. But with large files, no update until the end. This happens also for some label controls in which I display some status information. After the entire work is completed I optionally display a messagebox of what was done. For the labels, when I display the messagebox, the labels are updated. The app may work on many files one after the other.

I've thought about using a background thread but not sure how to do it. That is, the worker2.doWork() section is very intensive but if that's on it's own thread the the mainWindow.dispatcher_Update...will be called immediately after the thread begins and there's nothing to stop the next read from happening, right?

Perhaps it's impossible the way I've coded it. IF I used a Backgroundworker, would the class be instantiated in the Main Window or Worker1? Even a pointer or two would be great.

John Saunders
  • 160,644
  • 26
  • 247
  • 397
Ron
  • 2,435
  • 3
  • 25
  • 34
  • 1
    I have edited your title. Please see, "[Should questions include “tags” in their titles?](http://meta.stackexchange.com/questions/19190/)", where the consensus is "no, they should not". – John Saunders Dec 17 '13 at 20:10

2 Answers2

3

In .NET 4.5 they added the IProgress<T> interface and the Progress<T> class that lets you report progress from another thread and it will automatically run the callback on the UI thread if the Progress object was created there.

Being that you are on .NET 4.0 you are not totally out of luck, Microsoft released those classes as a Out of band update to the framework via the NuGet Package Microsoft.BCL which back-ports those two classes in to .NET 4.0.

Once you have Progress<T> it is fairly easy to use.

void StartWorker()
{
    var progress = new Progress<int>(UpdateProgressBar);

    worker1.Start(progress)
}


void UpdateProgressBar(int Value)
{
    //This code is invoked on the UI thread
    myProgressBar.Value = value;
}

//Elsewhere in Worker1
void Start(IProgress<int> progress)
{

    int count=0;
    Worker2 worker2 = new Worker2();
    while (bytesRead =(readData() !=0) 
    {
        count++;
        worker2.doWork();
        progress.Report(count);
    }
}

However that all being said you should try to rethink your data model. "Embrace WPF" and start using concepts like MVVM to set up bindings and you update data objects that then update your UI (And you can put all your UI marshaling code in your OnPropertyChanged event in your ViewModel).

Community
  • 1
  • 1
Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
  • I was so hoping to avoid something like this. This UI updating was/is an afterthought. I'll do it, either by MVVM or the patching on the Background worker (below). Mostly the UI does NOT update due to the app's operation, i.e, during the operation. THANKS!!! By the way (and I know I'm not supposed to use comments for that.) DID I note that everything up to now is on one thread? – Ron Dec 17 '13 at 21:45
  • @Ron If you go with a solution that is not listed as an answer please post your own answer explaining what you did and mark it accepted so the question does not get auto promoted every few months as a question with no accepted answer. As to why it is not updating, it is because everything up to now is on one thread. You can use `Task`s to spin up background workers, please note that if the worker will take more than a few seconds to run you should specify them [`LongRunning`](http://msdn.microsoft.com/en-us/library/system.threading.tasks.taskcreationoptions%28v=vs.110%29.aspx) – Scott Chamberlain Dec 17 '13 at 21:47
  • I second the MVVM approach, it's definitely the way to go. Binding handles all of the thread marshaling for you, it becomes very easy to bind a `ProgressBar` to a model and have it update the view automatically as you update the properties. – Erik Dec 17 '13 at 22:08
2

Use a BackgroundWorker for your background work and update you GUI via the ReportProgress call and handle it by servicing the ProgressChanged event, which contains "progress" - just update your progress bar from there.

If you need to start another background task when the first one finishes you could do that from the RunWorkerCompleted event.

noelicus
  • 14,468
  • 3
  • 92
  • 111
  • Is [BackgroundWorker](http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx) defined in [WPF](http://stackoverflow.com/tags/wpf/info)? –  Dec 17 '13 at 20:44
  • It is in `System.ComponentModel` but I would recommend against using it in WPF. There are far better methods to handle updating progress now. – Erik Dec 17 '13 at 21:56