正确使用DialogResult

37

在我提出最近的一个问题(Here)时,Hans Passant表示我应该将 DialogResult 设置为关闭我的窗体,而不是 form.Close(),但我无法弄清楚为什么?

如果我阅读得正确,MSDN 文档表明这样做只会隐藏窗体,而不是像我所认为的那样正确地处理它,使用 .Close() 操作可以正确处理窗体?

Extract 来自文档。

当用户单击对话框的关闭按钮或设置 DialogResult 属性的值时,不会自动调用 Close 方法。相反,窗体将被隐藏,可以再次显示而不创建新的对话框实例。由于此行为,当您的应用程序不再需要该窗体时,必须调用窗体的 Dispose 方法。

另外,微软创建了一个支持页面,介绍了如何使用DialogResult属性,在“验证它是否起作用”部分中指出点击后将关闭表单。

所以我的问题有两个方面,我应该继续使用Close还是DialogResult;而且DialogResult是否关闭或隐藏表单。从下面的代码中(一个简单的带有两个按钮的表单),似乎只是被隐藏了,因为当命中this.Close()时会触发断点..(注释掉this.Close(),表单仍然消失,只是不确定是否已经隐藏)

    public Form1()
    {
        InitializeComponent();
        button1.Click += (s, e) =>
            {
                 //I edited my question to include using
                using(Form1 form = new Form1())
                {
                    form.ShowDialog();
                }

            };
        button2.Click += (s, e) => 
            {
                this.DialogResult = DialogResult.OK;
                this.Close();
            };
    }
2个回答

29

使用ShowDialog打开模态对话框时,调用代码会被阻塞直到所调用的窗体关闭或隐藏。如果您想要读取所调用窗体的一些公共属性,并想根据用户单击“确定”或“取消”按钮执行操作(例如将数据保存到数据库或文件中),那么您需要知道用户是否想要执行该操作。ShowDialog()方法返回的DialogResult允许您采取适当的行动...

例如:

using (Form1 form = new Form1())
{
    DialogResult dr = form.ShowDialog();
    if(dr == DialogResult.OK)
    {
        string custName = form.CustomerName;
        SaveToFile(custName);
    }
    
}

在这个答案中需要补充的一个重要事项是,DialogResult属性存在于Form类和Button类中。 将按钮的DialogResult属性(通过代码或设计器)设置为与DialogResult.None不同的值是激活表单的重要行为的关键。 如果您单击具有此属性设置的按钮,则表单引擎会将按钮属性的值传输到表单属性并触发自动关闭表单以重新激活调用者代码。 如果您在按钮单击上有事件处理程序,则可以运行代码以验证表单的输入并强制表单保持打开状态,覆盖表单的DialogResult属性,并将其设置回DialogResult.None。

例如,在模态显示的表单中,您可以使用以下内容:

// Event handler for the OK button set with DialogResult.OK
public void cmdOK_Click(object sender, EventArgs e)
{
     // Your code that checks the form data and
     // eventually display an error message.
     bool isFormDataValid = ValidateFormData();

     // If data is not valid force the form to stay open
     if(!isFormDataValid)
        this.DialogResult = DialogResult.None;
}

1
不,我已经修正了答案,更加关注由ShowDialog返回的DialogResult所扮演的功能。(非常接近,不要调用Dispose,因此您仍然可以读取窗体的公共属性) - Steve
3
@Sayse 是的,你说得对。我们正在处理两个不同的概念。使用 using 来处理 Dispose() 概念,就像这里的两个答案一样。另一个概念是设置 DialogResult 来指示对话框关闭的方式,以便启动对话框的代码可以在必要时知道如何响应。通常你可能会忽略结果,但这取决于对话框的创建者。对话框应始终报告关闭的原因,以便创建者有选择的余地。(如果它只有一个退出状态,那当然没关系) - Matthew Watson
@MatthewWatson - 我本来就想到这种情况,但我的担心是在我的示例代码中,断点会命中在设置结果的下一行;所以如果我在表单中的某个其他方法中关闭对话框(例如,在if语句中),如果使用DR而不是关闭,那么该方法的其余部分是否会继续运行? - Sayse
1
我尝试了一下,似乎当你设置 DialogResult 时,它在窗体的消息循环从处理 Windows 消息返回之前没有任何效果。例如,如果你在窗体的构造函数中这样做:Task.Run(() => {Thread.Sleep(1000);this.DialogResult = DialogResult.Cancel;}); 如果你将鼠标远离窗体,什么也不会发生,但是当你将鼠标移到上面时,它突然关闭。 - Matthew Watson
@ToolmakerSteve - 我猜你在谈论我的先前评论,如果是这样的话,我无法记得那个评论背后的完整解释,但我可以立即想到一些可能导致断点未被命中的原因.. returnbreakcontinuegoto.. 任何短路方法的东西。 - Sayse
显示剩余6条评论

5

无论你调用Close还是设置DialogResult属性,都不是问题的关键。你只需要确保调用Dispose方法即可。我更喜欢使用using代码块来实现:

using (Form1 form = new Form1())
{
    form.ShowDialog();
}

我原本认为,你可以在已经调用其Close方法的Form上调用ShowDialog。但事实并非如此。如果使用ShowDialog模态显示表单,则无论是通过Close方法还是设置DialogResult属性关闭它似乎都没关系。看起来,设置DialogResult只是关闭Form的捷径。

但无论你调用Close还是设置DialogResult属性,关键是确保调用Dispose()或将表单放入使用块中。


上面的代码显然只是我匆忙写的一个例子,我确实倾向于使用using语句,只是不知道为什么我应该使用DialogResult而不是this.Close(),难道它就是知道表单是被取消还是确认吗? - Sayse
我尝试了您的示例,仍然保留了this.Close,就像我的示例中所示,我仍然可以重用该窗体... - Sayse
@Sayse 我刚刚查看了文档(应该是我的第一步)。它说如果使用ShowDialog显示窗体,则不会调用Close。我很惊讶。看起来似乎并不重要。 - Dan
是的,我也觉得这很困惑,看起来我的答案基本上就是“使用它,这样你就可以根据所点击的按钮来做一些事情”。 - Sayse
1
@Dan - 我认为你夸大了调用Dispose(或将其放置在“using”语句中)的实用性。与.Net中的任何其他对象一样,一旦没有到达的引用指向一个窗体,它就会(最终)被垃圾回收。在我看来,只有在窗体持有需要及时释放的资源时,显式Dispose才是重要的。 - ToolmakerSteve
2
我并不想就这个问题争论下去,特别是考虑到我认为WinForms早已死亡,所以我会放弃。但是请问:如果不需要调用Dispose,那么为什么要实现IDisposable呢? - Dan

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