5

This is probably a silly question, but I could not find an answer on stackoverflow.

I have a button click event in a Winform app that runs a thread to caclulate a result to display in a form.

How do I update the Forms UI when the thread has calculated the result?

    private void btnRequestR2Approval_Click(object sender, EventArgs e)
    {
        if (User.IsLogged)
        {
            ValidationResults results = new ValidationResults();
            results.Show();

            Logger log = Logger.Instance();
            Logger.NewLogAddedHandler messageDelegate = new Logger.NewLogAddedHandler(results.NewLogMessage);

            if (!log.IsEventHandlerRegistered())
            {
                log.NewLogAdded += messageDelegate;
            }

            ThreadStart operation = new ThreadStart(ValidateAndSubmit);
            Thread theThread = new Thread(operation);
            theThread.Start();

        }
        else
        {
            MessageBox.Show("Please login");
        }

    }

Thank you

Dour High Arch
  • 21,513
  • 29
  • 75
  • 90
Sergey
  • 3,214
  • 5
  • 34
  • 47
  • Are you asking about ASP.NET (web technology) or Winforms (Windows native technology)? The question looks like you are using WinForms, but the title and tags suggest otherwise. – jrcs3 Jun 11 '09 at 20:04
  • 1
    What does "finished running" mean? That the thread has exited, or that the thread has calculated something you need to know? – Dour High Arch Jun 11 '09 at 21:04
  • finished running, means thread calculated something and i need to refresh something in another thread – Sergey Jun 11 '09 at 21:07
  • This is a Winforms windows native tachnology – Sergey Jun 11 '09 at 21:09
  • see also http://stackoverflow.com/questions/661561/how-to-update-gui-from-another-thread-in-c – wimh Jun 29 '11 at 15:24

4 Answers4

16

The simplest technique for executing a background task in WinForms is to use a BackgroundWorker.

  • Drop it onto a form.
  • Wire up the events. As a minimum, use DoWork. You'll probably also want RunWorkerCompleted.
  • Write your background task in the DoWork event.
  • Write any UI changes in the RunWorkerCompleted event.
  • Call backgroundWorker1.RunWorkerAsync(); to kick off the process, probably from some button handler.

Using a BackgroundWorker avoids all the annoying thread handling and IsInvokeRequired stuff.

Here's a more detailed how-to article.

Don Kirkby
  • 53,582
  • 27
  • 205
  • 286
2

Try using a BeginInvoke with a callback operation... this will push your call to another thread, and call a method of your choosing when the thread completes:

private void btnRequestR2Approval_Click(object sender, EventArgs e)
{
    if (User.IsLogged)
    {
        ValidationResults results = new ValidationResults();
        results.Show();

        Logger log = Logger.Instance();
        Logger.NewLogAddedHandler messageDelegate = new Logger.NewLogAddedHandler(results.NewLogMessage);

        if (!log.IsEventHandlerRegistered())
        {

            log.NewLogAdded += messageDelegate;
        }

        ThreadStart operation = new ThreadStart(ValidateAndSubmit);
        operation.BeginInvoke(MyCallBack, this);
    }
    else
    {
        MessageBox.Show("Please login");
    }

}

private static void MyCallBack(IAsyncResult ar) {
  ((MyForm)ar.AsyncState).Refresh();
}
Jeff Fritz
  • 9,821
  • 7
  • 42
  • 52
2

Very unclear question, I will assume:

  1. You are running a Windows Winforms app, not an ASP.Net web page.
  2. You want to trigger a background calculation that can take a long time, and do not want your UI to block while this happens.
  3. You want your UI to get some kind of result when the background calculation is done.
  4. You want your background calculation to exit when it has supplied your UI with the result.

If that is true, you should be using an asynchronous delegate, not a thread. For example:

string SleepAndReturnParam(string param1)
{
    System.Threading.Thread.Sleep(10000);
    return param1;
}

// Declare a delegate matching our async method.
public delegate string DelegateWithParamReturningString(string param1);


private void button1_Click(object sender, EventArgs e)
{
    var myDelegate = new DelegateWithParamReturningString(SleepAndReturnParam);

    // Run delegate in thread pool.
    myDelegate.BeginInvoke("param1",
        OnCallBack, // Completion callback
        this); // value to pass to callback as AsyncState
}


private void OnCallBack(IAsyncResult ar)
{
    // first cast IAsyncResult to an AsyncResult object
    var result = ar as System.Runtime.Remoting.Messaging.AsyncResult;

    // grab the delegate
    var myDelegate = result.AsyncDelegate as DelegateWithParamReturningString;

    // Exit delegate and retrieve return value.
    string returnValue = myDelegate.EndInvoke(ar);

    // Declare an anonymous method to avoid having to define
    // a method just to update a field.
    MethodInvoker formFieldUpdater = delegate
    {
        formTextField.Text = returnValue;
    };

    // Winforms controls must be modified on the thread
    // they were created in.
    if (formTextField.InvokeRequired)
        Invoke(formFieldUpdater);
    else
        formFieldUpdater();
}
Dour High Arch
  • 21,513
  • 29
  • 75
  • 90
0

You can either 1. do a theThread.Join() which would block the calling thread. 2. Pass the first thread through to the second so it can make a call back into the main thread, which would need to do an Invoke() so it can redraw the form.

I'm curious though. Are you using Asp.Net (WebForms) or WinForms? If you are trying to do this on the web then you will need a completly different approach.

David McEwing
  • 3,320
  • 18
  • 16
  • Win Forms, and I've tried using theThread.Join() it locks up the whole app. – Sergey Jun 11 '09 at 20:11
  • That's because Thread.Join will wait until the thread is complete, which is exactly what you asked for. If this is not what you mean please update your question. – Dour High Arch Jun 11 '09 at 20:38