从后台线程结果更新Winforms用户界面

5

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

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

当线程计算出结果时,如何更新窗体的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");
        }

    }

谢谢你


你是在询问ASP.NET(Web技术)还是WinForms(Windows本地技术)?问题看起来像是你正在使用WinForms,但标题和标签表明不是这样。 - jrcs3
1
“finished running” 是什么意思?是指线程已经退出,还是线程已经计算出你需要知道的东西? - Dour High Arch
运行完成,表示线程已经计算出某些结果,并且我需要在另一个线程中刷新一些内容。 - Sergey
这是一种Winforms窗口本地技术。 - Sergey
请参考以下链接:https://dev59.com/wnRB5IYBdhLWcg3wUVtd - wimh
4个回答

16

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

  • 将其拖放到窗体上。
  • 连接事件。至少使用DoWork。您可能还需要RunWorkerCompleted
  • DoWork事件中编写您的后台任务。
  • RunWorkerCompleted事件中编写任何UI更改。
  • 调用backgroundWorker1.RunWorkerAsync();启动过程,可能来自某个按钮处理程序。

使用BackgroundWorker可以避免所有令人讨厌的线程处理和IsInvokeRequired问题。

这是一篇更详细的如何文章


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

你必须调用ar.EndInvoke,否则MyCallback将会泄漏机器资源。 - Dour High Arch
在AR中没有这样的方法,这依旧是现状吗? - Sergey
实际上是(AsyncDelegate)ar.EndInvoke,请参考我的回答。 - Dour High Arch

2
非常不清晰的问题,我将假定:
  1. 您正在运行一个Windows Winforms应用程序,而不是ASP.Net网页。
  2. 您想触发一个可能需要很长时间的后台计算,并且不希望在此过程中阻塞UI。
  3. 当后台计算完成时,您希望UI得到某种结果。
  4. 后台计算在向UI提供结果后退出。

如果是这样,您应该使用异步委托,而不是线程。例如:

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

0

你可以选择以下两种方式: 1. 执行 theThread.Join(),这将阻塞调用线程。 2. 将第一个线程传递给第二个线程,以便它可以回调到主线程,需要执行 Invoke() 以便重新绘制表单。

不过我很好奇。你是在使用 Asp.Net(WebForms)还是 WinForms?如果你正在尝试在 Web 上执行此操作,则需要完全不同的方法。


Win Forms,我尝试使用Thread.Join(),但它会锁定整个应用程序。 - Sergey
这是因为Thread.Join将等待线程完成,这正是您所要求的。如果这不是您的意思,请更新您的问题。 - Dour High Arch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接