41

I am currently writing my first program on C# and I am extremely new to the language (used to only work with C so far). I have done a lot of research, but all answers were too general and I simply couldn't get it t work.

So here my (very common) problem: I have a WPF application which takes inputs from a few textboxes filled by the user and then uses that to do a lot of calculations with them. They should take around 2-3 minutes, so I would like to update a progress bar and a textblock telling me what the current status is. Also I need to store the UI inputs from the user and give them to the thread, so I have a third class, which I use to create an object and would like to pass this object to the background thread. Obviously I would run the calculations in another thread, so the UI doesn't freeze, but I don't know how to update the UI, since all the calculation methods are part of another class. After a lot of reasearch I think the best method to go with would be using dispatchers and TPL and not a backgroundworker, but honestly I am not sure how they work and after around 20 hours of trial and error with other answers, I decided to ask a question myself.

Here a very simple structure of my program:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        Initialize Component();
    }

    private void startCalc(object sender, RoutedEventArgs e)
    {
        inputValues input = new inputValues();

        calcClass calculations = new calcClass();

        try
        {
             input.pota = Convert.ToDouble(aVar.Text);
             input.potb = Convert.ToDouble(bVar.Text);
             input.potc = Convert.ToDouble(cVar.Text);
             input.potd = Convert.ToDouble(dVar.Text);
             input.potf = Convert.ToDouble(fVar.Text);
             input.potA = Convert.ToDouble(AVar.Text);
             input.potB = Convert.ToDouble(BVar.Text);
             input.initStart = Convert.ToDouble(initStart.Text);
             input.initEnd = Convert.ToDouble(initEnd.Text);
             input.inita = Convert.ToDouble(inita.Text);
             input.initb = Convert.ToDouble(initb.Text);
             input.initc = Convert.ToDouble(initb.Text);
         }
         catch
         {
             MessageBox.Show("Some input values are not of the expected Type.", "Wrong Input", MessageBoxButton.OK, MessageBoxImage.Error);
         }
         Thread calcthread = new Thread(new ParameterizedThreadStart(calculations.testMethod);
         calcthread.Start(input);
    }

public class inputValues
{
    public double pota, potb, potc, potd, potf, potA, potB;
    public double initStart, initEnd, inita, initb, initc;
}

public class calcClass
{
    public void testmethod(inputValues input)
    {
        Thread.CurrentThread.Priority = ThreadPriority.Lowest;
        int i;
        //the input object will be used somehow, but that doesn't matter for my problem
        for (i = 0; i < 1000; i++)
        {
            Thread.Sleep(10);
        }
    }
}

I would be very grateful if someone had a simple explanation how to update the UI from inside the testmethod. Since I am new to C# and object oriented programming, too complicated answers I will very likely not understand, I'll do my best though.

Also if someone has a better idea in general (maybe using backgroundworker or anything else) I am open to see it.

vulkanino
  • 9,074
  • 7
  • 44
  • 71
philkark
  • 2,417
  • 7
  • 38
  • 59
  • 2
    This is an old and answered question, but popular enough that I though I'd share this for anyone looking to implement a very simple 'Progress reporter' between threads. Use the class Progress. Implementation is detailed in a nice article by Stephan Cleary here: http://blog.stephencleary.com/2012/02/reporting-progress-from-async-tasks.html. – SeanOB Nov 10 '16 at 04:55

8 Answers8

58

First you need to use Dispatcher.Invoke to change the UI from another thread and to do that from another class, you can use events.
Then you can register to that event(s) in the main class and Dispatch the changes to the UI and in the calculation class you throw the event when you want to notify the UI:

class MainWindow : Window
{
    private void startCalc()
    {
        //your code
        CalcClass calc = new CalcClass();
        calc.ProgressUpdate += (s, e) => {
            Dispatcher.Invoke((Action)delegate() { /* update UI */ });
        };
        Thread calcthread = new Thread(new ParameterizedThreadStart(calc.testMethod));
        calcthread.Start(input);
    }
}

class CalcClass
{
    public event EventHandler ProgressUpdate;

    public void testMethod(object input)
    {
        //part 1
        if(ProgressUpdate != null)
            ProgressUpdate(this, new YourEventArgs(status));
        //part 2
    }
}

UPDATE:
As it seems this is still an often visited question and answer I want to update this answer with how I would do it now (with .NET 4.5) - this is a little longer as I will show some different possibilities:

class MainWindow : Window
{
    Task calcTask = null;

    void buttonStartCalc_Clicked(object sender, EventArgs e) { StartCalc(); } // #1
    async void buttonDoCalc_Clicked(object sender, EventArgs e) // #2
    {
        await CalcAsync(); // #2
    }

    void StartCalc()
    {
        var calc = PrepareCalc();
        calcTask = Task.Run(() => calc.TestMethod(input)); // #3
    }
    Task CalcAsync()
    {
        var calc = PrepareCalc();
        return Task.Run(() => calc.TestMethod(input)); // #4
    }
    CalcClass PrepareCalc()
    {
        //your code
        var calc = new CalcClass();
        calc.ProgressUpdate += (s, e) => Dispatcher.Invoke((Action)delegate()
            {
                // update UI
            });
        return calc;
    }
}

class CalcClass
{
    public event EventHandler<EventArgs<YourStatus>> ProgressUpdate; // #5

    public TestMethod(InputValues input)
    {
        //part 1
        ProgressUpdate.Raise(this, status); // #6 - status is of type YourStatus
        // alternative version to the extension for C# 6+:
        ProgressUpdate?.Invoke(this, new EventArgs<YourStatus>(status));
        //part 2
    }
}

static class EventExtensions
{
    public static void Raise<T>(this EventHandler<EventArgs<T>> theEvent,
                                object sender, T args)
    {
        if (theEvent != null)
            theEvent(sender, new EventArgs<T>(args));
    }
}

@1) How to start the "synchronous" calculations and run them in the background

@2) How to start it "asynchronous" and "await it": Here the calculation is executed and completed before the method returns, but because of the async/await the UI is not blocked (BTW: such event handlers are the only valid usages of async void as the event handler must return void - use async Task in all other cases)

@3) Instead of a new Thread we now use a Task. To later be able to check its (successfull) completion we save it in the global calcTask member. In the background this also starts a new thread and runs the action there, but it is much easier to handle and has some other benefits.

@4) Here we also start the action, but this time we return the task, so the "async event handler" can "await it". We could also create async Task CalcAsync() and then await Task.Run(() => calc.TestMethod(input)).ConfigureAwait(false); (FYI: the ConfigureAwait(false) is to avoid deadlocks, you should read up on this if you use async/await as it would be to much to explain here) which would result in the same workflow, but as the Task.Run is the only "awaitable operation" and is the last one we can simply return the task and save one context switch, which saves some execution time.

@5) Here I now use a "strongly typed generic event" so we can pass and receive our "status object" easily

@6) Here I use the extension defined below, which (aside from ease of use) solve the possible race condition in the old example. There it could have happened that the event got null after the if-check, but before the call if the event handler was removed in another thread at just that moment. This can't happen here, as the extensions gets a "copy" of the event delegate and in the same situation the handler is still registered inside the Raise method.

Christoph Fink
  • 22,727
  • 9
  • 68
  • 113
  • Thanks for the fast answer, I added this to my code, however in the CalcClass I get problems with the "ProgressUpdate", the class doesn't recognise it. Is there anything I have to include to use that? So far I just added "using System.Threading". – philkark Mar 07 '12 at 14:16
  • Yes, I get the following error message: Error 1 Invalid token ';' in class, struct, or interface member declaration – philkark Mar 07 '12 at 14:25
  • I use the code as stated and get the error on the line where the "public event ProgressUpdate;" is declared. – philkark Mar 07 '12 at 14:37
  • Okay, sorry for asking too many questions, but I am really new to C#. I get a few more error messages. In MainWindow there is a problem with the variable e: A local varibale named 'e' cannot be declared in this scope because it would give a different meaning to 'e' which is already used in a 'parent or current' schope to denote somethhing. In CalcClass: YourEventArgs could not be found. Sorry, that might be obvious to others, but this would be my very first multithreading program an I just don't have much practice... – philkark Mar 07 '12 at 14:49
  • Okay, I just created a class myself. Thank you! Now I just seem to have one more problem. Obviously I would like to send the current progress (lets say in my example i) from the testmethod. But once i am back in the MainWindow I cannot seem to access the value, obviously it doesn't exist in this class, but I thought ProgressUpdate in CalcClass sends the argument to MainWindow... – philkark Mar 07 '12 at 15:09
  • I tried that =S, i called my e "arg" and in my I sent an object now from my class: public class MyEventArgs : EventArgs { public int i; } But when I try to access arg.i, I get the doesnt exist error. – philkark Mar 07 '12 at 15:17
  • Ahhh! Thankyou so much!!! It works perfectly now. I will definetly give you a vote up, once my reputation increases to 15! – philkark Mar 07 '12 at 15:28
  • I removed my old comments as most of them are not valid anymore with the updated answer... – Christoph Fink Sep 18 '14 at 07:56
  • what is Dispatcher of yours? Is it have something to do with GUI thread? – Yola Mar 24 '15 at 10:38
  • @Yola: Yes, `Dispatcher` is used to invoke something on the UI thread. Every WPF control should have a `Dispatcher` property... – Christoph Fink Mar 24 '15 at 11:54
  • The first example can't be done because `'CalcClass' does not contain a definition for 'ProgressUpdate' and no extension method 'ProgressUpdate' accepting a first argument type of 'CalcClass' could be found (are you missing a using directive or an assembly reference?)` -- and adding a `using MyNamespace;` statement at the top of my page didn't remove the error. – vapcguy Aug 18 '16 at 14:35
  • The second example can't be done in .NET 4.0 because `The type or namespace name 'Task' could not be found`, but I guess that's why it was mentioned it was a 4.5.2 example. Problem is a lot of our machines are lucky to have been upgraded to 4.0, let alone 4.5.2. A better 4.0 example is needed. – vapcguy Aug 18 '16 at 14:39
  • @vapcguy: The first example had two typos (fixed now, but other error than yours) and works fine now (see https://dotnetfiddle.net/aHrK5G) and yes, the second one needs .NET 4.5 as stated in the answer. – Christoph Fink Aug 18 '16 at 15:20
  • @ChrFin Nice fiddle!! Sorry, I got the error I got because I didn't include `public event EventHandler ProgressUpdate;` in the `calcClass` when trying to re-create this. Works great... I ended up going a different way, but I think I could've used it like you did, now. – vapcguy Aug 18 '16 at 16:45
  • An object reference is required for the non-static field, method or property Dispatcher.Invoke(Action) – m12lrpv Aug 27 '18 at 01:50
  • @m12lrpv That means (as the error method hints at) you are in a static method and not an instance method. You need to be either in an instance method or use an object reference to access the `Dispatcher` property (my example is an instance method). – Christoph Fink Aug 27 '18 at 05:07
  • Actually it means that the example doesn't work in a Window class object which is the OP's scenario. Best solution I ended up finding was to use a Progress object which worked without issue. – m12lrpv Aug 28 '18 at 06:07
  • Of course it does if you are "actually in the object". If you get the error you mentioned you are in a static method which does not have access to instance properties. This error does not happen if you do it as I did it in my example. – Christoph Fink Aug 28 '18 at 06:16
  • This might be a stupid question, but I'm going to ask it anyway: What do you mean by "Status Object" in "#5"? IS that a UI element? – Steven C. Britton Oct 24 '22 at 21:43
  • 1
    @StevenC.Britton The "Status Object" can be any type you define. It's just a way to send data to the receiving end of the event. – Christoph Fink Oct 25 '22 at 05:22
  • Okay, another question - This does not appear to MVVM friendly. I realize that MVVM means one tries to "minimize" the use of code in the View (or something like that) in that the ViewModel should handle all the presentation code and then update the View through bindings. I'm finding that I need to force the View to return the Dispatcher Object to the ViewModel, otherwise, I get a scenario where my VM, while not static, gives me the error that @m12lrpv references above. Are you trying for MVVM friendliness, am I missing something, or is this one of "those" "edge" cases? – Steven C. Britton Oct 26 '22 at 19:02
  • @StevenC.Britton Even if you use MVVM you need the dispatcher to update properties bound to the UI. But you could manage that inside the VM by wrapping all properties, update the "internal" props via the dispatcher and binding to those and not the externally updated ones... – Christoph Fink Oct 27 '22 at 05:28
31

I am going to throw you a curve ball here. If I have said it once I have said it a hundred times. Marshaling operations like Invoke or BeginInvoke are not always the best methods for updating the UI with worker thread progress.

In this case it usually works better to have the worker thread publish its progress information to a shared data structure that the UI thread then polls at regular intervals. This has several advantages.

  • It breaks the tight coupling between the UI and worker thread that Invoke imposes.
  • The UI thread gets to dictate when the UI controls get updated...the way it should be anyway when you really think about it.
  • There is no risk of overrunning the UI message queue as would be the case if BeginInvoke were used from the worker thread.
  • The worker thread does not have to wait for a response from the UI thread as would be the case with Invoke.
  • You get more throughput on both the UI and worker threads.
  • Invoke and BeginInvoke are expensive operations.

So in your calcClass create a data structure that will hold the progress information.

public class calcClass
{
  private double percentComplete = 0;

  public double PercentComplete
  {
    get 
    { 
      // Do a thread-safe read here.
      return Interlocked.CompareExchange(ref percentComplete, 0, 0);
    }
  }

  public testMethod(object input)
  {
    int count = 1000;
    for (int i = 0; i < count; i++)
    {
      Thread.Sleep(10);
      double newvalue = ((double)i + 1) / (double)count;
      Interlocked.Exchange(ref percentComplete, newvalue);
    }
  }
}

Then in your MainWindow class use a DispatcherTimer to periodically poll the progress information. Configure the DispatcherTimer to raise the Tick event on whatever interval is most appropriate for your situation.

public partial class MainWindow : Window
{
  public void YourDispatcherTimer_Tick(object sender, EventArgs args)
  {
    YourProgressBar.Value = calculation.PercentComplete;
  }
}
Brian Gideon
  • 47,849
  • 13
  • 107
  • 150
  • 1
    Hello Brian, I think your answer is simple and elegant. I was wondering in your opinion, how fast is the dispatchertimer tick firing? In your experience, what is an acceptable update rate? Thanks. – DoubleDunk Feb 03 '14 at 21:34
  • 2
    @DoubleDunk: Probably somewhere between 500ms and 2000ms maybe. Any faster and the users won't be able to tell the difference. Any slower and the users will wonder why the progress bar doesn't increment. Decide on a good balance that works best for your application and users. – Brian Gideon Feb 05 '14 at 15:51
  • @BrianGideon The update rate should depend on how long the calculations will approximately take. If it's updated twice a second and the whole calculation takes only about 3 seconds, then the progress bar will still have big jumps. If the calcualtions on the other hand take 20 minutes (as they have for me in one case) then 500ms is by far too frequent. Nice answer though and a good alternative to Invoke! – philkark Feb 04 '15 at 16:52
9

You're right that you should use the Dispatcher to update controls on the UI thread, and also right that long-running processes should not run on the UI thread. Even if you run the long-running process asynchronously on the UI thread, it can still cause performance issues.

It should be noted that Dispatcher.CurrentDispatcher will return the dispatcher for the current thread, not necessarily the UI thread. I think you can use Application.Current.Dispatcher to get a reference to the UI thread's dispatcher if that's available to you, but if not you'll have to pass the UI dispatcher in to your background thread.

Typically I use the Task Parallel Library for threading operations instead of a BackgroundWorker. I just find it easier to use.

For example,

Task.Factory.StartNew(() => 
    SomeObject.RunLongProcess(someDataObject));

where

void RunLongProcess(SomeViewModel someDataObject)
{
    for (int i = 0; i <= 1000; i++)
    {
        Thread.Sleep(10);

        // Update every 10 executions
        if (i % 10 == 0)
        {
            // Send message to UI thread
            Application.Current.Dispatcher.BeginInvoke(
                DispatcherPriority.Normal,
                (Action)(() => someDataObject.ProgressValue = (i / 1000)));
        }
    }
}
Rachel
  • 130,264
  • 66
  • 304
  • 490
4

Everything that interacts with the UI must be called in the UI thread (unless it is a frozen object). To do that, you can use the dispatcher.

var disp = /* Get the UI dispatcher, each WPF object has a dispatcher which you can query*/
disp.BeginInvoke(DispatcherPriority.Normal,
        (Action)(() => /*Do your UI Stuff here*/));

I use BeginInvoke here, usually a backgroundworker doesn't need to wait that the UI updates. If you want to wait, you can use Invoke. But you should be careful not to call BeginInvoke to fast to often, this can get really nasty.

By the way, The BackgroundWorker class helps with this kind of taks. It allows Reporting changes, like a percentage and dispatches this automatically from the Background thread into the ui thread. For the most thread <> update ui tasks the BackgroundWorker is a great tool.

dowhilefor
  • 10,971
  • 3
  • 28
  • 45
1

You are going to have to come back to your main thread (also called UI thread) in order to update the UI. Any other thread trying to update your UI will just cause exceptions to be thrown all over the place.

So because you are in WPF, you can use the Dispatcher and more specifically a beginInvoke on this dispatcher. This will allow you to execute what needs done (typically Update the UI) in the UI thread.

You migh also want to "register" the UI in your business, by maintaining a reference to a control/form, so you can use its dispatcher.

squelos
  • 1,189
  • 6
  • 16
1

If this is a long calculation then I would go background worker. It has progress support. It also has support for cancel.

http://msdn.microsoft.com/en-us/library/cc221403(v=VS.95).aspx

Here I have a TextBox bound to contents.

    private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        Debug.Write("backgroundWorker_RunWorkerCompleted");
        if (e.Cancelled)
        {
            contents = "Cancelled get contents.";
            NotifyPropertyChanged("Contents");
        }
        else if (e.Error != null)
        {
            contents = "An Error Occured in get contents";
            NotifyPropertyChanged("Contents");
        }
        else
        {
            contents = (string)e.Result;
            if (contentTabSelectd) NotifyPropertyChanged("Contents");
        }
    }
paparazzo
  • 44,497
  • 23
  • 105
  • 176
  • I'd vote this up, as it got me on the right track and to an MSDN page that had the information I needed, but this is FAR from a full example. You needed to show *MUCH* more, like where to instantiate the worker, the properties and event handlers to give it, show what the `DoWork` function was doing and how to report its progress. This just shows what to do when you cancel the worker and shows `e.Result` that presumably you have a `backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { ... }` function giving you, but not what `DoWork` is actually doing and how to return that as `Result`. – vapcguy Aug 18 '16 at 16:06
0

Felt the need to add this better answer, as nothing except BackgroundWorker seemed to help me, and the answer dealing with that thus far was woefully incomplete. This is how you would update a XAML page called MainWindow that has an Image tag like this:

<Image Name="imgNtwkInd" Source="Images/network_on.jpg" Width="50" />

with a BackgroundWorker process to show if you are connected to the network or not:

using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;

public partial class MainWindow : Window
{
    private BackgroundWorker bw = new BackgroundWorker();

    public MainWindow()
    {
        InitializeComponent();

        // Set up background worker to allow progress reporting and cancellation
        bw.WorkerReportsProgress = true;
        bw.WorkerSupportsCancellation = true;

        // This is your main work process that records progress
        bw.DoWork += new DoWorkEventHandler(SomeClass.DoWork);

        // This will update your page based on that progress
        bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);

        // This starts your background worker and "DoWork()"
        bw.RunWorkerAsync();

        // When this page closes, this will run and cancel your background worker
        this.Closing += new CancelEventHandler(Page_Unload);
    }

    private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        BitmapImage bImg = new BitmapImage();
        bool connected = false;
        string response = e.ProgressPercentage.ToString(); // will either be 1 or 0 for true/false -- this is the result recorded in DoWork()

        if (response == "1")
            connected = true;

        // Do something with the result we got
        if (!connected)
        {
            bImg.BeginInit();
            bImg.UriSource = new Uri("Images/network_off.jpg", UriKind.Relative);
            bImg.EndInit();
            imgNtwkInd.Source = bImg;
        }
        else
        {
            bImg.BeginInit();
            bImg.UriSource = new Uri("Images/network_on.jpg", UriKind.Relative);
            bImg.EndInit();
            imgNtwkInd.Source = bImg;
        }
    }

    private void Page_Unload(object sender, CancelEventArgs e)
    {
        bw.CancelAsync();  // stops the background worker when unloading the page
    }
}


public class SomeClass
{
    public static bool connected = false;

    public void DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker bw = sender as BackgroundWorker;

        int i = 0;
        do 
        {
            connected = CheckConn();  // do some task and get the result

            if (bw.CancellationPending == true)
            {
                e.Cancel = true;
                break;
            }
            else
            {
                Thread.Sleep(1000);
                // Record your result here
                if (connected)
                    bw.ReportProgress(1);
                else
                    bw.ReportProgress(0);
            }
        }
        while (i == 0);
    }

    private static bool CheckConn()
    {
        bool conn = false;
        Ping png = new Ping();
        string host = "SomeComputerNameHere";

        try
        {
            PingReply pngReply = png.Send(host);
            if (pngReply.Status == IPStatus.Success)
                conn = true;
        }
        catch (PingException ex)
        {
            // write exception to log
        }
        return conn;
    }
}

For more information: https://msdn.microsoft.com/en-us/library/cc221403(v=VS.95).aspx

vapcguy
  • 7,097
  • 1
  • 56
  • 52
0

Thank God, Microsoft got that figured out in WPF :)

Every Control, like a progress bar, button, form, etc. has a Dispatcher on it. You can give the Dispatcher an Action that needs to be performed, and it will automatically call it on the correct thread (an Action is like a function delegate).

You can find an example here.

Of course, you'll have to have the control accessible from other classes, e.g. by making it public and handing a reference to the Window to your other class, or maybe by passing a reference only to the progress bar.

Vladislav Zorov
  • 2,998
  • 19
  • 32
  • 4
    Note that when you link to an example, instead of copy/pasting the relevant code in here, if that page ever goes down (like this morning) or removes their content, this answer then doesn't have the substance it needs for someone to work off of. Better to include that content. – vapcguy Aug 18 '16 at 14:30