Using DialogResult Correctly
Asked Answered
S

2

38

In an answer to a recent question I had (Here), Hans Passant stated that I should set the DialogResult to close my forms instead of form.Close() although I cannot seem to find out why?

If I've read correctly, the MSDN documentation states that doing this will just hide the form instead of correctly disposing it which I believed .Close() to do?

Extract from documentation.

The Close method is not automatically called when the user clicks the Close button of a dialog box or sets the value of the DialogResult property. Instead, the form is hidden and can be shown again without creating a new instance of the dialog box. Because of this behavior, you must call the Dispose method of the form when the form is no longer needed by your application.

On the other hand, Microsoft has created a support page that says how to use DialogResult property and in the "Verify It Works" section of this it states that clicking so will Close the form.

So my question is two fold, should I continue to use Close or DialogResult instead; and does dialog result close or hide a form. From the code I made below (a simple form with two buttons), it would seem that it is indeed hidden only as a breakpoint on this.Close() is hit..(with this.Close() commented, the form still disappears, just not sure whether hidden or not)

    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();
            };
    }
Sudan answered 30/5, 2013 at 22:2 Comment(0)
A
29

When you open a modal dialog with ShowDialog, the calling code is blocked until the form called closes or hides. If you want to read some public properties of the called form and want to do things (for example save data to a database or to a file) based on the click on the OK or Cancel button, then you need to know if the user wants to do the action or not. The DialogResult returned by the ShowDialog() method allows you to take the appropriate actions...

So for example

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

An important thing to add to this answer is the fact that the DialogResult property exists both on the Form class and in the Button class. Setting the button's DialogResult property (both via code or designer) to a value different from DialogResult.None is the key to activate an important behavior for forms. If you click a button with that property set then the Forms Engine transfers the value of the Buttons property to the Forms one and triggers the automatic closure of the form reactivating the caller code. If you have an event handler on the button click then you can run code to validate the form's inputs and force the form to stay open overriding the form's DialogResult property setting it back to DialogResult.None

For example, in the modally showed form you can have:

// 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;
}
Aloud answered 30/5, 2013 at 22:14 Comment(11)
I just extended my example code to include something similar to what you were showing me and still with this.Close() present, I am able to access form properties, do you mean there is a chance that this is not the case?Sudan
No, I have fixed the answer to be more focused on the function played by the DialogResult returned by the ShowDialog. (Really close do not call Dispose and thus you could still read the public properties of the form)Aloud
Am I right in thinking then that the idea of favouring DialogResult is so you can use it for further logic dependant on the dialogs result? (I've always just included both DialogResult and Form.Close() when this has been needed)Sudan
@Sudan Yes, you are right. We're dealing with two separate concepts. The Dispose() concept is handled using using as in both the answers here. The other concept is the setting of DialogResult to indicate the manner in which the dialog was closed - so the code that launched the dialog can tell how to respond if necessary. Often you might ignore the result, but that's up to the creator of the dialog. The dialog should always report the reason for closing to give the creator that option. (If it has only one exit state, then it doesn't matter ofc)Flintlock
@MatthewWatson - I had imagined that was the case, but my concern would be like in my example code, a breakpoint was hit on the following line to setting the result; so if I were to close a dialog during some other method in the form (in an if statement for example) would the rest of the method continue to run if using DR and not close?Sudan
Setting the DialogResult property of a form doesn't immediately hides the form, you still need to exit from the current click event before you notice the hiding of the form. Calling Form.Close instead triggers immediately an internal method (SendMessage) that ultimately (I think) ends with the form to be hiddenAloud
so in this case I should continue to use form.Close? (with DialogResult if needed)Sudan
I played around with this and it seems that when you set DialogResult it doesn't seem to have any effect until the form's message loop returns from processing a windows message. So for example if you do this in the form's constructor: Task.Run(() => {Thread.Sleep(1000);this.DialogResult = DialogResult.Cancel;}); nothing happens if you keep your mouse away from the form, but when you mouse over it, it suddenly closes.Flintlock
@Sayse: of course you can hit the breakpoint on the line after setting DialogResult. Were you imagining that the MOMENT you set DialogResult, .Net would yank control away from the method you were executing? That would be rather extreme behavior for a property setter, The method runs to completion, and upon return, the system code acts, based on what you did within the method.Refrigerant
@Refrigerant - I presume you are talking about my previous comment, in which case I cannot remember the full explanation behind that comment but I can think of a few straight away that would cause a breakpoint not to be hit .. return, break, continue, goto.. anything that short circuits the method.Sudan
@Sudan - I've edited my answer to indicate that it is surprising that you would think a PROPERTY SETTER (setting DialogResult to a value) would cause a short-circuit to the method. (or perhaps you thought the form is abruptly aborted, killing any ongoing method execution?) As you say, there are specific mechanisms that intentionally cause such behavior. A person reading code needs to be able to assume that unless they hit such a mechanism, flow of control will continue.Refrigerant
D
5

Whether you call Close or set the DialogResult property is not really the issue. You just need to make sure to call Dispose. I prefer doing this with a using block:

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

I originally thought that you could call ShowDialog on a Form that has already had its Close method called. This is not the case. If you show the form modally with ShowDialog, it doesn't seem to matter whether it is closed as a result of the Close method, or setting the DialogResult property. It would seem that setting the DialogResult is just a short-cut for closing the Form.

But whether you call Close or set the DialogResult property, the key is to make sure that you call Dispose() or put your form in a using block.

Decolonize answered 30/5, 2013 at 22:5 Comment(6)
the code above is obviously just an example that I hastily wrote, and I do tend to use a using statement, just don't know why I should be using DialogResult over this.Close(), is it as simple as knowing whether the form was cancelled or ok'd?Sudan
I tried your example with this.Close still in as shown in my example and I was still able to reuse the form it seemed...Sudan
@Sudan I just looked up the documentation (should have been my first step). It says that Close isn't called if the form is shown using ShowDialog. I was surprised. It doesn't look like it matters.Decolonize
Yeah I'm finding this very confusing too, it looks like my answer is basically "use it so you can do stuff depending on what button was clicked"Sudan
@Decolonize - I think you overstate the usefulness of calling Dispose (or placing in a "using" statement). Like any other object in .Net, once there are no reachable references to a form, it will (eventually) be garbage-collected. IMHO, an explicit Dispose is only important if the form is holding resources that it is important to release promptly.Refrigerant
I don't really want to get into an argument over this especially since I consider WinForms long dead, so I will just forfeit. But ask this: If Dispose didn't need to be called, why implement IDisposable then?Decolonize

© 2022 - 2024 — McMap. All rights reserved.