Windows 窗体:UI 线程使用 Show() 和 ShowDialog() 流动
Posted
技术标签:
【中文标题】Windows 窗体:UI 线程使用 Show() 和 ShowDialog() 流动【英文标题】:Windows Forms: UI threads flow with Show() and ShowDialog() 【发布时间】:2014-01-19 17:15:30 【问题描述】:在 Windows 窗体上开发解决方案时,我进入了一个向用户显示持续进度的例程。我实现了带有连续进度条的简单虚拟窗口:
在解决方案树中,它与主窗口位于同一级别:
在做某事时显示持续进步的最简单的工作方法是以下代码。它确实有效:
//This method works
private void DoSomeBackgroundStuffWithShow()
ContinuousProgressWindow continuousProgressWindow =
new ContinuousProgressWindow();
BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += (sender, arguments) =>
//Do some stuff for 4 seconds
Thread.Sleep(4000);
;
backgroundWorker.RunWorkerCompleted += (sender, arguments) =>
//Window is closed when needed. Great!
continuousProgressWindow.Dispose();
;
continuousProgressWindow.Show(this);
backgroundWorker.RunWorkerAsync();
但我需要此窗口出现在最顶部并在工作时阻止其父级。以下代码非常相似,但它不起作用 - 显示对话框,但 从未关闭:
//This method DOES NOT WORK
private void DoSomeBackgroundStuffWithShowDialog()
ContinuousProgressWindow continuousProgressWindow =
new ContinuousProgressWindow();
BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += (sender, arguments) =>
//Do some important stuff for 4 seconds
Thread.Sleep(4000);
;
backgroundWorker.RunWorkerCompleted += (sender, arguments) =>
//None of the following work for "ShowDialog() method"
//Ran with debugger - breakpoints not hit!
continuousProgressWindow.DialogResult = DialogResult.OK;
continuousProgressWindow.Close();
continuousProgressWindow.Dispose();
;
continuousProgressWindow.ShowDialog(this);
backgroundWorker.RunWorkerAsync();
然后,我意识到问题在于 UI 线程流:当进度窗口作为对话框运行时,MainWindow
线程被冻结,并且无法由RunWorkerCompleted
委托中的BackgroundWorker
调用以关闭对话框。
最简单的解决方案是什么?
【问题讨论】:
使用 showDialog,backgroundWorker.RunWorkerAsync();
直到 continuousProgressWindow
退出时才会被调用。您是否尝试过在显示窗口之前启动backgroundWorker
?
@StevenMills 我刚刚尝试过,它奏效了。发布带有编辑和注释代码的答案,我会接受。
等待想要接受有用的答案。太多的人只是读了答案就永远消失了。
【参考方案1】:
这里的问题是您在 backgroundWorker.RunWorkerAsync() 之前调用了 ContinuousProgressWindow.ShowDialog(this)。因此,一旦您关闭窗口,就会调用 backgroundWorker.RunWorkerAsync()。
我认为下面的代码应该可以工作,正如@Steven Mills 所建议的那样。
private void DoSomeBackgroundStuffWithShowDialog()
ContinuousProgressWindow continuousProgressWindow =
new ContinuousProgressWindow();
BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += (sender, arguments) =>
//Do some important stuff for 4 seconds
Thread.Sleep(4000);
;
backgroundWorker.RunWorkerCompleted += (sender, arguments) =>
//None of the following work for "ShowDialog() method"
//Ran with debugger - breakpoints not hit!
continuousProgressWindow.DialogResult = DialogResult.OK;
continuousProgressWindow.Close();
continuousProgressWindow.Dispose();
;
backgroundWorker.RunWorkerAsync();
continuousProgressWindow.ShowDialog(this);
【讨论】:
【参考方案2】: continuousProgressWindow.ShowDialog(this);
backgroundWorker.RunWorkerAsync();
您遇到了一个简单的先有鸡还是先有蛋的问题,直到对话框关闭后 才启动worker。 ShowDialog() 是一个阻塞调用。所以 RunWorkerCompleted 事件不会触发,因为工人没有开始。最简单的解决方法是交换两个语句:
backgroundWorker.RunWorkerAsync();
continuousProgressWindow.ShowDialog(this);
这样做并不完全安全。这个 sn-p 不是问题,但在实际代码中存在工作人员在显示对话框之前完成 的危险。赔率低但不为零。为了解决这个问题,您希望延迟工作人员,直到您确定对话框已启动并正在运行。这可以通过对话框的 OnShown() 方法使用 Set() 的 AutoResetEvent 来完成。或者,更优雅的是,利用一个技巧:
this.BeginInvoke(new Action(() => backgroundWorker.RunWorkerAsync()));
continuousProgressWindow.ShowDialog(this);
Control.BeginInvoke() 的委托目标在程序重新进入消息循环时运行。这发生在对话框变得可见之后:)
【讨论】:
简短而有用的答案:经过测试,它可以正常工作。我在哪里可以阅读有关消息循环的这些技巧? 当然是在 *** 上 :) 这是一个古老的技巧,早在 .NET 出现之前就已经使用了。 C 程序员使用 PostMessage(),Charles Petzold 写过,C# 使代码更漂亮。以上是关于Windows 窗体:UI 线程使用 Show() 和 ShowDialog() 流动的主要内容,如果未能解决你的问题,请参考以下文章
在 C# 中使用多线程屏蔽/过滤图像(Windows 窗体应用程序)
JetBrains Rider C# | Windows 窗体 UI [关闭]
c#出现timer线程跑丢的情况,网上有说明需要线程重启来解决问题,请提供举例代码。
winform 利用 多线程 处理窗体假死,利用 Invoke BeginInvoke 处理子线程调用 UI 控件报错的问题