我的项目是一个三层架构项目,与后端WCF服务通信。当后端能够从服务获取数据时,它使用publish-subscribe通知业务层,业务层反过来通知GUI层。
我已经添加了一个OpenFileDialog到我的UI设计使用Visual studio设计器。按钮事件处理程序调用ShowDialog消息。但是,一旦我点击这个按钮,整个UI就挂起了。
在谷歌上搜索了一下之后,我发现使用委托是处理这类任务的首选方式。然而,无论有没有授权,问题仍然存在。目前我的代码是这样的:
private void bOpen_Click(object sender, EventArgs e)
{
Func<Image> del = delegate
{
OpenFileDialog d = new OpenFileDialog();
if (d.ShowDialog() == DialogResult.OK)
{
return Image.FromFile(d.FileName);
}
return null;
};
Invoke(del);
}
我来自Java世界,所以我不太熟悉c# UI编程的复杂性。
我在这里错过了什么吗?
openFileDialog1->ShowHelp = true;
我把这行放在我的代码中,然后问题就解决了。
我似乎已经解决了将[STAThread]属性添加到主方法的问题。当我在调试器中运行程序时,我被告知要这样做——我以前没有这样做过,因为我经常在Visual Studio中运行服务,而在Windows中运行客户端。
[STAThread]
public static void Main(string[] args)
{
GUI gui = new GUI();
gui.ShowDialog();
}
有谁能解释一下到底是怎么回事吗
这往往是一个环境问题,当您使用OpenFileDialog时,许多shell扩展被加载到您的进程中。一个行为不当的人很容易搞砸你的程序。外面有很多不好的人。
调试这个是困难的,您需要一个非托管调试器,因为这些shell扩展是非托管代码。在死锁之后,您可能能够从调用堆栈中得知一些信息。Windows调试符号必需,启用微软符号服务器。但最有效的方法是使用SysInternals的AutoRuns实用程序。首先禁用所有非Microsoft生成的shell扩展。然后开始一个接一个地重新启用那些你不能没有的。
并且,正如您所发现的,这些shell扩展期望在STA线程上运行,并且当它们没有得到它时悲惨地失败。程序的UI线程必须始终是STA,也支持剪贴板、拖放和各种控件,如WebBrowser。通常总是由Main()方法上的[STAThread]属性自动处理,由项目模板放在那里。以及Application.Run()调用,这是实现STA契约所必需的。
我认为"委托"的首选方式实际上是指使用单独的线程。我将给你一个使用BackgroundWorker的例子。
它看起来像这样:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
m_Worker.DoWork += new DoWorkEventHandler(m_Worker_DoWork);
m_Worker.ProgressChanged += new ProgressChangedEventHandler(m_Worker_ProgressChanged);
m_Worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(m_Worker_RunWorkerCompleted);
}
void m_Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//Usually, used to update a progress bar
}
void m_Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Usually, used to add some code to notify the user that the job is done.
}
void m_Worker_DoWork(object sender, DoWorkEventArgs e)
{
//e.Argument.ToString() contains the path to the file
//Do what you want with the file returned.
}
private void bOpen_Click(object sender, EventArgs e)
{
OpenFileDialog d = new OpenFileDialog();
if (d.ShowDialog() == DialogResult.OK)
{
m_Worker.RunWorkerAsync(d.FileName);
}
}
BackgroundWorker m_Worker = new BackgroundWorker();
}
现在,至于你的UI"挂起"的原因,这是因为默认情况下,你的操作运行在UI线程上,所以如果你运行一些重的东西,UI将不会响应。
我也遇到过这个问题。我尝试了所有的解决方案,但没有一个能解决它。然后我把目标框架从。net framework 4.7改成了4.6.2,问题解决了…
我想我的问题是不同的,因为上面的解决方案都不适合我。
我编写了临时代码将OpenFileDialog.FileName
属性设置为非空或空字符串(当挂起发生时它是空字符串),并且我重新启动了我的计算机。当我再次启动Visual Studio并运行它时,它再次工作而没有挂起。