using System.Windows.Forms;
public class App
{
[STAThread]
public static void Main()
{
string fname;
using (var d = new OpenFileDialog())
{
if (d.ShowDialog() != DialogResult.OK)
{
return;
}
fname = d.FileName;
}
//Application.ExitThread();
for (; ;)
;
}
}
上面的代码显示了一个文件对话框。选择文件并按open
后,将执行for循环,但(冻结)对话框仍然存在。
取消注释Application.ExitThread()
后,对话框会按预期消失。
这是否按预期工作?为什么using
没有让窗口消失?我在哪里可以找到更多关于此的信息?
答案 0 :(得分:12)
您已发现单线程应用程序的主要问题...长时间运行的操作会冻结用户界面。
您的DoEvents()
电话主要是"暂停"您的代码并提供其他操作,如UI,运行的机会,然后恢复。问题是您的UI现在再次被冻结,直到您再次呼叫DoEvents()
。实际上,DoEvents()
是very problematic approach(some call it evil)。你真的不应该使用它。
您有更好的选择。
将长时间运行的操作放在另一个线程中有助于确保UI保持响应,并确保您的工作尽可能高效地完成。处理器能够在两个线程之间来回切换,以提供同时执行的假象,而不会出现全面的多进程。
实现这一目标的一个更简单的方法是使用BackgroundWorker,虽然它们通常不受欢迎(原因我不会在这篇文章中介绍:{{3} })。然而,它们仍然是.NET的一部分,与其他方法相比,学习曲线较低,因此我仍然建议新开发人员在业余爱好项目中使用它们。
目前最好的方法是.NET的further reading库。如果您的长时间运行操作已经在一个线程中(例如,它是一个数据库查询而您只是等待它完成),并且如果库支持它,那么您可以使用{{{}来利用任务3}}关键字,而不必三思而后行。即使它不在线程或受支持的库中,您仍然可以启动一个新任务,并通过Tasks在单独的线程中执行它。 .NET任务具有语言支持和更多功能,例如协调多个任务和将任务链接在一起。
答案 1 :(得分:5)
JDB已在his answer 中解释了为什么(一般来说)您的代码无法按预期工作。让我添加一点建议解决方法(针对您的具体情况以及何时只需要使用系统对话框,然后继续进行,就像它是控制台应用程序一样)。
You're trying使用Application.DoEvents()
,确定似乎正常工作,在您的情况下,您没有可重入的代码。但是,确定是否正确处理了所有相关消息?你应该多少次拨打Application.DoEvents()
?您是否确定正确初始化所有内容(我在谈论ApplicationContext
)?第二个问题更实用,OpenFileDialog
需要COM,COM(此处)需要STAThread
,STAThread
需要消息泵。我不能告诉你它会以哪种方式失败,但肯定可能失败。
首先请注意,应用程序通常使用Application.Run()
启动主消息循环。您不希望看到new MyWindow().ShowDialog()
,对吗?您的示例没有什么不同,让Application.Run(Form)
重载为您创建ApplicationContext
(并在表单关闭时处理HandleDestroyed
事件,最终会调用 - surprise - Application.ExitThread()
)。很遗憾OpenFileDialog
不会从Form
继承,因此您必须在虚拟表单中托管它才能使用Application.Run()
。
如果您使用设计器在表单中添加对话框,则无需显式调用dlg.Dispose()
(让WinForms管理对象生命周期)。
using System;
using System.Windows.Forms;
public class App
{
[STAThread]
public static void Main()
{
string fname = AskForFile();
if (fname == null)
return;
LongRunningProcess(fname);
}
private static string AskForFile()
{
string fileName = null;
var form = new Form() { Visible = false };
form.Load += (o, e) => {
using (var dlg = new OpenFileDialog())
{
if (dlg.ShowDialog() == DialogResult.OK)
fileName = dlg.FileName;
}
((Form)o).Close();
};
Application.Run(form);
return fileName;
}
}
答案 2 :(得分:0)
不,您不必致电Application.ExitThread()
。
Application.ExitThread()
终止调用线程的消息循环并强制销毁冻结的对话框。虽然"有效,但如果已知冻结的原因,最好解冻对话框。
在这种情况下,按open
似乎会触发一个没有任何机会完成的近距离事件。 Application.DoEvents()
给了它机会并使对话框消失。