【问题标题】:Windows Forms GUI hangs when calling OpenFileDialog.ShowDialog()调用 OpenFileDialog.ShowDialog() 时 Windows 窗体 GUI 挂起
【发布时间】:2011-10-06 18:49:23
【问题描述】:

我的项目是一个三层架构项目,它与后端的 WCF 服务通信。当后端能够从服务中获取数据时,它会使用发布-订阅通知业务层,然后通知 GUI 层。

我使用 Visual Studios 设计器在我的 UI 设计中添加了一个 OpenFileDialog。按钮事件处理程序调用 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 编程的复杂性。

我在这里遗漏了什么?

【问题讨论】:

  • 我很好奇,为什么不直接private void bOpen_Click(object sender, EventArgs e) { OpenFileDialog d = new OpenFileDialog(); if (d.ShowDialog() == DialogResult.OK) { Image.FromFile(d.FileName); } };
  • 它挂起而不使用委托。我创建了委托,因为我发现几个网站说这是执行由 GUI 触发的冗长操作的首选方式。有点像 Java 中的 SwingWoker,我想。
  • 提到的站点的首选方式必须是在单独的线程中启动委托。类似new Thread(del).Start();
  • 但是UI挂了很奇怪,因为OpenFileDialog必须自己运行一个内部消息循环。
  • 您是否将 STA 线程属性正确应用于 GUI 进程的主进程? msdn.microsoft.com/en-us/library/system.stathreadattribute.aspx

标签: c# .net winforms user-interface


【解决方案1】:

我相信“委托”首选方式实际上是指使用单独的线程。 我会给你一个使用 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 将不会响应。

【讨论】:

  • 如果我不能在线程上使用它,我很不走运。一旦执行进入 ShowDialog() 方法,程序就会挂起。我可以尝试在另一台机器上运行它。如果是你在下面的回复中说的,汉斯,它应该可以在另一台机器上工作,对吧?
【解决方案2】:

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

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

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

【讨论】:

  • 奇怪的是我可以毫无问题地打开颜色选择器。但这似乎证实了 shell 扩展论点。
【解决方案3】:

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

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

谁能解释一下到底发生了什么

【讨论】:

  • 文件打开/保存对话框只能在单线程单元模型中显示。文档对此进行了很好的解释。如果我看到这个问题,我本来会发布这个。
【解决方案4】:
openFileDialog1->ShowHelp = true;

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

【讨论】:

  • 哈哈,在我的情况下,这是我唯一的解决方案!
  • 也为我工作。这很奇怪,我很好奇为什么这会使对话框正常工作。
  • 此解决方法在早期的 windows vista 中很有帮助。相同的代码在 Windows XP 上正常工作,在 Windows Vista 上总是挂起。这是一个简单的解决方法。
  • 在整个互联网上进行了搜索,包括许多关于 SO 的问题,这是从 OpenFileDialog.ShowDialog() 中消除无法解释的 5 秒延迟的唯一解决方案。谁会想到 ShowHelp 与它有任何关系?但不知何故,它确实如此。
【解决方案5】:

我也遇到过这个问题。我在这里尝试了所有解决方案,但没有一个可以解决。然后我把目标框架从.Net Framework 4.7改成4.6.2,问题解决了……

【讨论】:

    【解决方案6】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多