views:

914

answers:

4

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

A: 

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
Win Forms, and I've tried using theThread.Join() it locks up the whole app.
McLovin
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
+5  A: 

The simplest technique for executing a background task in WinForms is to use a BackgroundWorker. You drop it onto a form, wire up the events, and call RunWorkerAsync(). Then you write your background task in the DoWork event. Any UI refresh can be put in the RunWorkerCompleted event.

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

Here's a more detailed how-to article.

Don Kirkby
+1  A: 

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
You must call ar.EndInvoke or MyCallback will leak machine resources.
Dour High Arch
there is no such method in ar,,, would it still be the case?
McLovin
It's actually (AsyncDelegate)ar.EndInvoke, see my answer.
Dour High Arch
+1  A: 

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