C#:如何从另一个线程显示UI线程上的对话框

10

我是C#的新手,但我已经做了很多Java。这是我的问题:我试图从不是UI线程的线程中打开一个“SaveFileDialog”。

这就是我要做的:

public partial class Form1: Form
{
    public string AskSaveFile()
    {
        var sfd = new SaveFileDialog();
        sfd.Filter = "Fichiers txt (*.txt)|*.txt|Tous les fichiers (*.*)|*.*";
        sfd.FilterIndex = 1;
        sfd.RestoreDirectory = true;
        DialogResult result = (DialogResult) Invoke(new Action(() => sfd.ShowDialog(this)));
        if(result == DialogResult.OK)
        {
            return sfd.FileName;
        }

        return null;
    }
}

这个方法总是在与拥有表单的线程不同的线程中调用。问题在于,当我执行这段代码时,“Form1”会冻结,并且“SaveFileDialog”不会出现。您有什么提示可以帮助我从独立的线程中显示对话框吗?
2个回答

15
让它看起来像这样:
    public string AskSaveFile() {
        if (this.InvokeRequired) {
            return (string)Invoke(new Func<string>(() => AskSaveFile()));
        }
        else {
            var sfd = new SaveFileDialog();
            sfd.Filter = "Fichiers txt (*.txt)|*.txt|Tous les fichiers (*.*)|*.*";
            sfd.FilterIndex = 1;
            sfd.RestoreDirectory = true;
            return sfd.ShowDialog() == DialogResult.OK ? sfd.FileName : null;
        }
    }

如果你仍然遇到死锁问题,请确保使用调试器的“调试”>“窗口”>“线程”窗口,并查看UI线程正在做什么。只有在UI线程空闲并执行Application.Run()时,Control.Invoke()才能完成。如果不是这样,比如等待工作线程完成,那么这段代码将一直发生死锁。
此外,还要考虑到从用户界面可用性的角度来看,这种代码是有风险的。用户可能不希望这个对话框突然出现,并且可能会在鼠标或键盘操作窗口时意外关闭它。

谢谢,这对我有用。我在FolderBrowserDialog中使用了它。 - SWIK

7

试试这个:

public partial class Form1: Form
{
    public string AskSaveFile()
    {
        if (this.InvokeRequired)
        {
            Invoke( new MethodInvoker( delegate() { AskSaveFile(); } ) );
        }
        else
        {
            var sfd = new SaveFileDialog();
            sfd.Filter = "Fichiers txt (*.txt)|*.txt|Tous les fichiers (*.*)|*.*";
            sfd.FilterIndex = 1;
            sfd.RestoreDirectory = true;
            if(sfd.ShowDialog() == DialogResult.OK) return sfd.FileName; 
        }               
        return null;
    }
}

无法工作: "AskSaveFile" 需要返回一个字符串。如果我使用 "EndInvoke" 等待结果,我会遇到相同的问题("Form1" 冻结)。 - Moinonime
新代码的第一部分使对话框在主线程中执行...所以这应该可以工作...哦,是的,主线程将执行此操作...但是您是否意味着您的主线程正在执行不同的操作,无法使用?相信我,在我的应用程序中,我在不同的线程中使用此代码,并且它可以正常工作...所以现在肯定有些事情我还没有理解... - Marco
@Hans:你的评论是针对我还是针对Mathieu的? - Marco
你说得对,第一部分是有效的,它显示了对话框。但我找到了一个解决方案。我将调用方法的线程设置为"STA",现在这个线程执行方法"sfd.ShowDialog()"。 - Moinonime
对于您来说,C#编译器不会让您编写没有保证返回值始终设置的代码。 - Hans Passant

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接