【问题标题】:OpenFileDialog freezes when called from another dialog从另一个对话框调用 OpenFileDialog 时冻结
【发布时间】:2014-01-24 17:37:22
【问题描述】:

我有表单:SplashscreenSearchNewEntry

Program.cs 启动 Splashscreen,它会检查一些参数,并在成功时(检查用户名、权限等)打开 Search 表单。从那里我可以拨打NewEntry 表格。

因此,由于SplashscreenMain() 函数调用的主要形式,因此一旦关闭,应用程序就会关闭(这是预期的行为)。
所以我所做的是从Splashscreen 内部启动Search 表单作为对话框并隐藏Splashscreen,所以它等待Search 关闭。它一关闭,我也关闭了Splashscreen,当时这一切似乎都说得通。从Search 表单我打开NewEntry 表单(通过按钮单击)也作为一个对话框(我不希望用户来回单击并创建多个NewEntry 窗口)。

今天我不得不添加一个允许用户选择文件的功能。作为一个显而易见的选择,我使用了OpenFileDialog。但是只要我调用.ShowDialog() 方法,整个应用程序就会冻结。

我在这里和其他网站上阅读了Windows Forms GUI hangs when calling OpenFileDialog.ShowDialog()OpenFileDialog.ShowDialog() freezing application c# 以及许多其他帖子,当我几乎失去希望时,我遇到了这个http://wishmesh.com/2011/06/call-to-openfiledialog-or-savefiledialog-hangs-or-freezes/
我的Main() 函数上确实设置了[STAThread] 属性

一个特别的兴趣点是:对于 OpenFileDialog,必须显式设置 ShowHelp 属性。
还有 ...它们不必设置为 true,只需将它们初始化为 true 或 false。

所以当我将ShowHelp 设置为true 时,会出现对话框(带有无用的帮助按钮)。

进一步的研究表明,当我在Program.csSplashscreen 内执行(new OpenFileDialog()).ShowDialog(); 时,它工作得很好;但是,当从对话框中调用时,会出现挂起(没有ShowHelp)。此外,MessageBoxDialog 中显示得很好...

那么有没有办法让这个工作?还是我应该以不同的方式管理我的窗户?
比如有Search作为主启动窗口,那么在窗口显示之前,调用Splashscreen作为对话框,如果失败,就关闭主窗口?但是,我将如何处理NewEntry 才能显示OpenFileDialogFolderBrowserDialog

谢谢。

【问题讨论】:

    标签: c# winforms


    【解决方案1】:

    我最关心的部分是当你说你有 SplashScreen 等待搜索关闭时。如果你这样做

    if (search.ShowDialog() == DialogResult.OK)
    {
        Show();
    }
    

    那么您已经冻结了 SplashScreen 的 UI 线程。由于它是主要形式,因此事情不太可能按预期工作。我建议将代码更改为此。

    Hide(); // Hides SplashScreen
    Search search = new Search();
    search.ParentForm = this;
    search.Show(); // Show Search without freezing SplashScreen thread
    

    ParentForm 是我添加到 Search 中的公共变量。这应该是私有的,并且应该通过 getter/setter 进行访问,但这会使代码示例保持简短。

    public partial class Search: Form
    {
        public SplashScreen ParentForm;
        ...
    }
    

    接下来,您需要添加一个处理程序来处理搜索表单的关闭。这将显示 SplashScreen。

    private void Form2_FormClosed(object sender, FormClosedEventArgs e)
    {
        ParentForm.Show();
    }
    

    进行这些更改后,OpenFileDialog 显示工作正常。

    【讨论】:

    • 感谢您的回答。第一部分虽然不相同,但差别不大,我在做:this.Hide(); (new Search()).ShowDialog(); this.Close();。主要区别在于我不太关心Search 表单的DialogResult,我只希望它像应用程序的“主窗口”一样。我会尝试你的建议并保持更新这篇文章。再次感谢。
    【解决方案2】:

    如果在您的program.cs 中,您调用的是:Application.Run(new splashscreen());,如果是我,我会这样做,以便在启动画面关闭后您可以打开多个表单。

    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            new splashscreen().ShowDialog(); 
            Application.Run(new Search());
        }
    }
    

    这将显示启动画面,然后在加载完所有内容后,您可以打开搜索表单并关闭启动画面,而无需调用ShowDialog()。 (您必须致电application.End() 才能结束申请。)

    【讨论】:

    • 我实现了这个建议,唯一看起来不正确的部分是,即使最后一个窗口关闭,应用程序也不会结束。 Windows 任务管理器显示该进程仍在运行且调试器未返回“正常”模式,我必须单击“停止”按钮。但这似乎是一个简单的解决方案,也是我所拥有的一个很好的替代方案。感谢您的建议。
    • 对不起,内联代码,不能在评论中格式化太多。这就是我现在所拥有的:if ((new Splashscreen()).ShowDialog() == DialogResult.OK) { (new Search()).Show(); Application.Run(); } 我关闭了窗口,按下右上角的“X”,我不处理CloseClosing 等可以覆盖和最小化而不是关闭的方法...
    • 我想,如果我将我拥有的内容更改为Application.Run(new Search());,这应该可以。我将隐藏Search 窗口并显示NewEntry,同时设置ParentForm 属性并按照@Scott MacMaster 的建议处理所有内容
    • @nurchi,我的错,你实际上必须在 closingclosed 事件处理程序中调用 Application.End() 以关闭最后一个表单(我看过一个多表单我刚刚制作的项目,并没有意识到当它关闭时我正在以我的“主窗体”结束应用程序)。
    • @nurchi,我更新了我的答案,以便显示启动画面,一旦它关闭,应用程序就会运行,搜索作为它的主要形式。
    【解决方案3】:

    非常感谢您在这件事上的帮助。

    总而言之,我创建了Form1Form2 来测试一些代码,这就是我所拥有的(仅相关部分):

    // Form1:
    void Button1Click(object sender, EventArgs e)
    {
        Form2 f2=new Form2();
        this.Hide();
        f2.Show(this);
    }
    
    // Form2:
    void Form2Load(object sender, EventArgs e)
    {
        if (this.Owner==null) throw new ArgumentException("This window must be called with the Owner property set!");
        // or just ignore, or show a MessageBox and close, or use your imagination...
    }
    
    void Form2FormClosed(object sender, FormClosedEventArgs e)
    {
        // sanity check...
        if (this.Owner!=null && !this.Owner.Visible) this.Owner.Show();
    }
    

    所以当我调用Form2 时,我首先隐藏当前表单并使用Show() 的重载,它接受1 个参数并设置Form2Owner 属性。或者我可以打电话给f2.Owner=this; 然后f2.Show();

    然后当Form2 关闭时/之后,我检查是否设置了所有者并显示该表单。

    现在回到最初的问题,这是我在Program.cs 里面的内容:

    if ((new Splashscreen()).ShowDialog() == DialogResult.OK)
        Application.Run(new Search());
    

    所以现在Search 仅显示Splashscreen 是否与DialogResult.OK 一起返回,并且当Search 关闭时,应用程序正确退出。

    感谢所有帮助。
    这为我节省了大量时间。

    附言
    我希望我可以将两个答案都标记为答案,然后扔硬币并随机标记一个。

    【讨论】:

    • @ScottMacMaster 和 nurchi,我知道那是什么感觉,nurchi,有两个很好的答案,因为我的答案是幸运的,所以我会继续支持 Scott 的(但我主要是因为我实际上认为它似乎与最终解决方案更相关)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-14
    • 2011-08-05
    • 2015-06-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多