从后台线程结果更新Winforms UI

时间:2009-06-11 19:59:00

标签: .net winforms multithreading

这可能是一个愚蠢的问题,但我找不到stackoverflow的答案。

我在Winform应用程序中有一个按钮单击事件,该应用程序运行一个线程来计算结果以显示在表单中。

当线程计算结果时,如何更新Forms UI?

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

    }

谢谢

4 个答案:

答案 0 :(得分:13)

在WinForms中执行后台任务的最简单方法是使用BackgroundWorker

  • 将其放在表格上。
  • 汇总事件。至少使用DoWork。您可能还需要RunWorkerCompleted
  • DoWork事件中编写后台任务。
  • RunWorkerCompleted事件中写下任何UI更改。
  • 调用backgroundWorker1.RunWorkerAsync();启动该过程,可能来自某个按钮处理程序。

使用BackgroundWorker可以避免所有恼人的线程处理和IsInvokeRequired。

以下是更详细的how-to article

答案 1 :(得分:2)

尝试使用带有回调操作的BeginInvoke ...这会将您的调用推送到另一个线程,并在线程完成时调用您选择的方法:

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

答案 2 :(得分:2)

非常不清楚的问题,我会假设:

  1. 您正在运行Windows Winforms应用程序,而不是ASP.Net网页。
  2. 您希望触发可能需要很长时间的后台计算,并且不希望在发生这种情况时阻止您的UI。
  3. 您希望在完成后台计算时,您的UI会获得某种结果。
  4. 您希望在向UI提供结果时退出后台计算。
  5. 如果确实如此,那么您应该使用异步委托,而不是线程。例如:

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

答案 3 :(得分:0)

你也可以 1.做一个会阻塞调用线程的theThread.Join()。 2.将第一个线程传递给第二个线程,以便它可以回调到主线程,这需要执行Invoke(),以便重绘表单。

我很好奇。您使用的是Asp.Net(WebForms)还是WinForms?如果您尝试在网络上执行此操作,那么您将需要一种完全不同的方法。