【问题标题】:Show a modal dialog while running a long UI operation on Main thread在主线程上运行长 UI 操作时显示模式对话框
【发布时间】:2015-11-24 05:53:28
【问题描述】:

我想在主 UI 线程上做一些工作时显示一个带有进度条的模式对话框。如果不使用任何多线程技术,我的 UI 肯定会处于挂起状态。

如何在主线程上运行计算密集型长操作时实现这一点(因为它涉及 UI 元素,我不寒而栗地触摸这个遗留代码),同时在等待窗口上显示计算状态(应该运行在不同的线程上)

我需要在另一个线程上运行一个进度对话框窗口,同时保持主线程被占用来计算涉及主 UI 表单元素的长绑定操作。

感谢指点

【问题讨论】:

  • 您不在主线程上运行 cpu 绑定操作。您在 BackgroundWorker 上运行它们。此外,模态对话框(等待窗口)通常在主(UI)线程上运行,因为它通常由在主(UI)线程中执行的语句创建。因此,启动一个后台工作程序,附加到 DoWork 事件并在事件处理程序中实现您的长时间运行的任务。启动后,您可以立即显示模态窗口。
  • 你也应该让你的进度/等待窗口modeless,以防你想添加一个Cancel按钮。想想资源管理器中的 Windows 文件复制窗口。然后你可以取消BackgroundWorker
  • 如果你可以在 .NET 4.5+ 中使用 asyncawait 以及 Task.Run ,这将更容易。正如其他人所指出的那样,您需要在后台线程上进行工作以防止 UI 结霜,但您不需要求助于后台工作人员。
  • 另外,看看System.Progress<T> 类型。
  • 非常感谢大家的建议。我将尝试实现它并返回

标签: c# multithreading asynchronous


【解决方案1】:

UI 不能在不同的线程中运行。它应该在 UIThread 中运行。您必须在不同的线程中重写密集的操作并使用调度程序将参数传递给它(因为 UI 元素可以从 UI 线程访问,正如我所提到的)。如果你阻塞主线程,你将无法显示进度。

【讨论】:

  • 这正是我所关心的。长时间运行的操作与 UI 元素交互。而且我无法阻止他们显示等待窗口
  • 据我所知,后台工作人员可以访问 UI 控件。问题是您的进度不会显示进度,因为密集的操作会阻塞主线程而不是阻塞 ui 元素。
  • 是的,通过ReportProgress()。您可以在ProgressChanged 的事件处理程序中更新 UI
  • @this-Me 除非您的计算可以迭代运行,否则可能无法取得进展。
  • 问题是即使等待窗口也必须处理进度条UI元素,而在后台运行的主窗口必须处理相应的UI元素。
【解决方案2】:

如果您的问题是在运行该长任务时向用户更新状态(进度条),那么解决方案是 In Control.CheckForIllegalCrossThreadCalls = false

社区建议不要将该属性设置为 false,但我喜欢从任何线程访问所有控件,它工作正常,而且它是直截了当的,除非有人给我们一个很好的理由不使用它,而不是“它不好用”改为调用”。

   private void button1_Click(object sender, EventArgs e) {
        //To avoid  Cross-thread exception
        Control.CheckForIllegalCrossThreadCalls = false;
        //Start background task
        bkg.RunWorkerAsync();
        //show wait form 
        var frmWait = new WaitForm();
        frmWait.ShowDialog();
    }

    private void bkg_DoWork(object sender, DoWorkEventArgs e) {
        //Do your work and update status
    }

【讨论】:

    【解决方案3】:

    技术上可以在 UI 线程以外的线程上创建模型对话框(但绝对不建议

    无论如何,由于您无法更改遗留代码,并且希望模型对话框在另一个线程上工作,这里有一个建议

    1> 在单独的线程上创建并打开模型对话框

    2> 计算密集型工作必须触发一些会挂钩报告进度的事件。这些事件是从 UI 线程触发的,您必须将它们编组到创建此模型对话框的线程。使用Control.InvokeRequired 来检测调用堆栈是否来自另一个线程并使用control.Invoke(action) 来封送它

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-05
      • 1970-01-01
      相关资源
      最近更新 更多