How I learned to love DialogResult

December 23rd, 2015 by June Blender
Last updated on December 21st, 2015

 

This post discusses the DialogResult property of buttons, which often confuses beginning PowerShell GUI app designers.


When I first started learning how to write PowerShell GUI applications, I thought of the DialogResult property of buttons as the “evil property.”

image

What else would I think? If the DialogResult property of a button is set to any value other than None, when you click the button, the GUI app closes immediately after running the commands in my event handler.

In this little app, when you click OK, it pipes a Get-Service command to Out-GridView — um, very briefly.

 

After banging my head against the wall (and then searching the SAPIEN blog/forums), I realized that my app closes immediately because the DialogResult property of the OK button is set to OK.

Screenshot 2015-12-21 16.47.06

It also closes immediately when the DialogResult value is Cancel, Abort, Retry, Ignore, Yes, or No. When the value of the DialogResult property of a button is None, the app works perfectly.

So DialogResult is evil, right?

The First Value of DialogResult

I’m new to GUI apps, but the science and art of user experience and computer-based user interfaces is decades old. When I design my PowerShell GUI apps, I’m standing on the shoulders of people who’ve studied user experience as a discipline. I want to learn from them.

And, they’ve learned that uniformity and predictability make buttons usable. If my OK button does the same thing as your OK button, and almost every other OK button, our users learn how the OK button works. If, instead, my OK button opens a box and your OK button finds a file, the user needs more guidance before clicking OK.

You might rely on each developer to do the right thing. It’s easy enough to write a Click event handler for the button that closes the form.

$buttonOK_Click={
    form1.Close()
}

But, it’s much easier to standardize button behavior. That’s what DialogResult tries to do.

$buttonOK.DialogResult = OK

And, if the behavior of OK buttons changed, the .NET folks would only have to change what the OK value does. Developers worldwide wouldn’t have to rewrite the Click event handlers of their buttons.

It reminds me of all of the things we do in PowerShell to create a more predictable experience, including using approved verbs and common parameters. It might frustrate developers at times, but it’s really much better for users.

The Second Value of DialogResult

Actually, the DialogResult values do a lot more than just close the form. Their primary job is to tell a parent form which user action closed its child form.

Here’s the model. You start with a parent form (Main Form). When you click a button (like “Open child form”) on the parent form, the child form opens. When you close the child form, the system returns the DialogResult value of the closing action to the parent form.

(Want to learn how to create a parent and child form? Passing and returning values using forms)

clip_image006

In this example, the text on each the buttons matches its DialogResult value. For example, the Retry button has a DialogResult value of ‘Retry’.

clip_image008

When the user clicks the ‘Retry’ button, the system returns a value of ‘Retry’ to the parent form. The parent form can use the DialogResult value.

For example, here is the Click event handler for the Open child form button. The Call-ChildForm_psf function opens the child form. When the child form closes, the system returns the DialogResult value as output. You can ignore the DialogResult value, or you can capture the DialogResult value and use it. For example, this event handler assigns the DialogResult value to the Text property of the RichTextbox, so the value is displayed in the rich textbox.

$buttonOpenChildForm_Click = {
    $richtextbox1.Text = Call-ChildForm_psf
}

Now, when you click a button that closes the child form, the DialogResult value appears in the rich textbox.

 

There are many practical uses of this functionality. For example, this the app reacts differently to each DialogResult value. The button’s Click event handler opens the child form (Call-ChildForm_psf) and saves the DialogResult in the $dr variable. Then, it uses a Switch statement to respond to the DialogResult value in the $dr variable.

$buttonOpenChildForm_Click = {
    do
    {
        $dr = Call-ChildForm_psf
        $retry = $false
 
        switch ($dr)
        {
            OK { $richtextbox1.ForeColor = 'Green'; break; }
            Cancel { $richtextbox1.ForeColor = 'Red'; break; }
            No { $formMain.Close() }
            Retry { $richtextbox1.ForeColor = 'Red'; $retry = $true; break; }
            default { $richtextbox1.ForeColor = 'Black'; break; }
        }
        $richtextbox1.Text = $dr
    }
    while ($retry -eq $true)
}

Here’s the result:

DialogResult in a Message Box

You might not work with parent and child forms, but you can use DialogResult in a message box, too. The MessageBox control is a child of its parent form.

I often use message boxes to display errors to users. For example, in the Click event handler for a Start button, I run a Get-Service command, but if it fails, I use a Try-Catch block to catch errors and display them in a message box. To create the message box, I use the msgbox preset snippet in PowerShell Studio.

$buttonStart = {
    Try {
        Get-Service -ComputerName $textboxCn.Text.Trim() | Out-GridView
    }
    Catch {
        [void][System.Windows.Forms.MessageBox]::Show('Cannot get services from the specified computer', 'Computer name error')
    }
}

Notice that the output type on the MessageBox snippet is [void], which means $null or “nothing.” It’s like piping the output of a command to the Out-Null cmdlet. But, if you don’t void out the output, the system returns the DialogResult value of the control that closed the message box.

In this code, I save the DialogResult output in the $dr variable. Also, I selected an argument list (“overload”) for the Show method that creates a message box with Yes and No buttons that return a DialogResult of Yes and No, respectively. In the Catch block, the error message asks the user if they want to get services on the local machine. If they say ‘Yes’, it runs a Get-Service command locally. Otherwise, it just displays the parent form again.

$buttonStart = {
    Try {
        Get-Service -ComputerName $textboxCn.Text.Trim() | Out-GridView
    }
    Catch {
        $dr = [System.Windows.Forms.MessageBox]::Show('Failed. Get services on local computer?', 'Computer name error', 'YesNo')
        if ($dr -eq 'Yes') { Get-Service | Out-GridView }
    }
}

This is just one of many possible uses of DialogResult in a message box.

Fixing DialogResult Errors: Add a button

Most PowerShell Studio users first encounter the DialogResult property when they use the Dialog Style Template.

The Dialog Style Template is awesome because it creates a fixed-size (not resizable) GUI app with no minimize or maximize buttons, so you don’t have to worry about controls floating around on your form when the end-user resizes the box.

clip_image010

But, because it follows best-practice standards, the OK button in the Dialog Style Template has a DialogResult value of OK.

clip_image011

If you try to re-purpose the OK button by writing an event handler for it, it runs the event handler and then closes immediately — and rather abruptly.

$buttonOK_Click={
    Get-Service | Out-GridView
}

To fix the problem, you can set the DialogResult property of the OK button to None. That fixes your app, but it breaks the best practice expectation of how OK buttons work.

Instead, add a button that doesn’t have an expected purpose (not one of the DialogResult values), like this Start button, and set its DialogResult property to None.

clip_image012

Then, move the feature code to the Click event handler for the Start button. To make sure that I don’t misuse the OK button, I add a comment reminding me about the DialogResult value.

$buttonOK_Click={
    #DialogResult = OK
}
 
$buttonStart_Click={
    Get-Service | Out-GridView
}

 

Now that I understand DialogResult, I realize that it’s not really evil. It’s helps to enforce best practice standards and I can even use productively. I can’t quite say I love it, but I certainly have a crush on it.

June Blender is a technology evangelist at SAPIEN Technologies, Inc. You can reach her at juneb@sapien.com or follow her on Twitter at @juneb_get_help.

 

 
[Google+]   [Facebook]   [LinkedIn]   [StumbleUpon]   [Digg]   [Reddit]   [Google Bookmark]  

Tags: , , , , ,