【问题标题】:Update Winforms UI from background thread result从后台线程结果更新 Winforms UI
【发布时间】:2009-06-11 19:58:59
【问题描述】:

这可能是一个愚蠢的问题,但我在 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,但标题和标签却暗示了其他问题。
  • “跑完了”是什么意思?线程已经退出,或者线程计算了一些你需要知道的东西?
  • 运行完毕,表示线程计算了一些东西,我需要在另一个线程中刷新一些东西
  • 这是一个 Winforms windows 原生技术

标签: .net winforms multithreading


【解决方案1】:

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

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

使用 BackgroundWorker 避免了所有烦人的线程处理和 IsInvokeRequired 的东西。

这里有更详细的how-to article

【讨论】:

    【解决方案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 会泄漏机器资源。
    • ar中没有这种方法,,,还会这样吗?
    • 其实是(AsyncDelegate)ar.EndInvoke,看我的回答。
    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

      • Win Forms,我尝试使用 theThread.Join() 它锁定了整个应用程序。
      • 那是因为 Thread.Join 会等到线程完成,这正是你所要求的。如果这不是您的意思,请更新您的问题。
      猜你喜欢
      • 2012-04-24
      • 1970-01-01
      • 1970-01-01
      • 2023-03-28
      • 2018-05-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多