Sergey
Sergey

Reputation: 3213

Update Winforms UI from background thread result

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

Upvotes: 5

Views: 12704

Answers (4)

Don Kirkby
Don Kirkby

Reputation: 56230

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.

Upvotes: 16

Dour High Arch
Dour High Arch

Reputation: 21722

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();
}

Upvotes: 2

Jeff Fritz
Jeff Fritz

Reputation: 9861

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();
}

Upvotes: 2

David McEwing
David McEwing

Reputation: 3340

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.

Upvotes: 0

Related Questions