调用OpenFileDialog.ShowDialog()时,Windows窗体GUI挂起

时间:2011-07-16 14:54:51

标签: c# .net winforms user-interface

我的项目是一个三层架构项目,与后端的WCF服务进行通信。当后端能够从服务中获取数据时,它会使用publish-subscribe通知业务层,而后者通知GUI层。

我使用Visual Studios设计器为我的UI设计添加了一个OpenFileDialog。按钮事件处理程序调用ShowDialog消息。但是,一旦我点击按钮,整个用户界面就会挂起。

搜索了一下后,我发现使用委托是处理这样的任务的首选方式。但是,无论是否有代表,问题仍然存在。

目前我的代码如下:

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编程的复杂性。

我在这里缺少什么?

6 个答案:

答案 0 :(得分:9)

我似乎解决了将[STAThread]属性添加到main方法的问题。一旦我在调试器中运行程序,我被告知这样做 - 我以前没有这样做,因为我经常从Windows运行服务,而客户端定期从Windows运行。

[STAThread]
public static void Main(string[] args)
{
    GUI gui = new GUI();
    gui.ShowDialog();
}

有人可以解释究竟发生了什么吗

答案 1 :(得分:7)

这往往是一个环境问题,当您使用OpenFileDialog时,很多shell扩展会被加载到您的进程中。一个行为不端的人很容易搞砸你的程序。那里有很多不好的东西。

调试这很困难,你需要一个非托管调试器,因为这些shell扩展是非托管代码。在死锁后进入中断时,您可能能够从调用堆栈中分辨出某些内容。需要Windows调试符号,启用Microsoft符号服务器。但最有效的方法是使用SysInternals的AutoRuns实用程序。首先禁用所有未由Microsoft生成的shell扩展。然后开始重新启用那些你不能一个接一个地生活的人。

并且,正如您所发现的那样,这些shell扩展期望在STA线程上运行,并且当它们没有得到它时会失败。程序的UI线程必须始终是STA,也支持剪贴板和拖放以及各种控件,如WebBrowser。通常由Main()方法的[STAThread]属性自动处理,由项目模板放在那里。和Application.Run()调用,需要实现STA合同。当你没有死锁时。

答案 2 :(得分:7)

openFileDialog1->ShowHelp = true;

我把这行放在我的代码中然后问题就解决了。

答案 3 :(得分:4)

我认为“委托”首选方式实际上是指使用单独的线程。 我将使用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就不会响应。

答案 4 :(得分:0)

我也遇到了这个问题。我在这里尝试了所有解决方案,没有人能解决。然后我将目标框架从.Net Framework 4.7更改为4.6.2,问题已解决...

答案 5 :(得分:0)

我认为我的问题有所不同,因为上述解决方案都不适合我。

我编写了临时代码,以OpenFileDialog.FileName属性设置为非null 或空字符串(挂断时为空字符串),然后重新启动计算机。当我再次启动Visual Studio并运行它时,它又再次正常工作而没有挂机。