在Windows Forms上开发解决方案时,我进入了向用户展示持续进展的例程。我实现了带有连续进度条的简单虚拟窗口:
在解决方案树中,它与主窗口:
位于同一级别
在执行某些操作时显示持续进度的最简单的工作方法是以下代码。它可行:
//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();
}
但我需要此窗口显示 topmost 并在工作时阻止其父级。以下代码非常相似,它不起作用 - 显示对话框,但从未关闭:
//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
线程被冻结,而BackgroundWorker
中的RunWorkerCompleted
无法调用它委托关闭对话框。
使其按预期工作的最简单的解决方案是什么?
答案 0 :(得分:4)
continuousProgressWindow.ShowDialog(this);
backgroundWorker.RunWorkerAsync();
你有一个简单的鸡蛋问题,你不会在关闭对话框后启动工作人员。 ShowDialog()是一个阻塞调用。因此,RunWorkerCompleted事件不会触发,因为worker没有启动。最简单的解决方法是交换两个语句:
backgroundWorker.RunWorkerAsync();
continuousProgressWindow.ShowDialog(this);
这样做并不完全安全。这段代码不是问题,但在实际代码中,工作人员有可能在显示对话框之前完成。赔率低但不是零。要解决这个问题,您需要延迟工作者,直到您确定对话框已启动并运行。这可以通过对话框的OnShown()方法使用Set()的AutoResetEvent来完成。或者,更优雅地,通过利用一个技巧:
this.BeginInvoke(new Action(() => backgroundWorker.RunWorkerAsync()));
continuousProgressWindow.ShowDialog(this);
当程序重新进入消息循环时,Control.BeginInvoke()的委托目标将运行。对话框变得可见后会发生这种情况:)
答案 1 :(得分:2)
这里的问题是你在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);
}