Windows PowerShell Tip of the Week

Here’s a quick tip on working with Windows PowerShell. These are published every week for as long as we can come up with new tips. If you have a tip you’d like us to share or a question about how to do something, let us know.

Find more tips in the Windows PowerShell Tip of the Week archive.

Creating a Custom Input Box

When Windows PowerShell was released it was touted as a command line-driven management system for people who prefer working in the console window. So what happened as soon as system administrators got their hands on PowerShell? You got it: they immediately began looking for ways to create graphical user interfaces using PowerShell.

Go figure.

As it turns out, it’s not too difficult to create GUI applications using Windows PowerShell; that’s because Windows PowerShell has access to all the form-building capabilities found in the .NET Framework. Admittedly, creating these forms isn’t always fun, and it’s far from intuitive; that’s due, in large part, to the fact that there is no “Visual PowerShell” that enables you to draw and position controls on a form; instead, you have to explicitly specify sizes and positions, pixel-by-pixel. (Sort of like you would if you were creating an HTA.) It’s a bit of a hassle, but the end result can definitely be worth all the trouble.

As you’re about to find out.

To introduce you to GUI programming using Windows PowerShell we thought we’d show you how to create a custom input box. This might not be the fanciest form in the world, but it is a form, and it will give you a relatively-painless introduction to form creation in .NET. Here’s the code for our custom input box, with an explanation to follow:

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

$objForm = New-Object System.Windows.Forms.Form
$objForm.Text = "Data Entry Form"
$objForm.Size = New-Object System.Drawing.Size(300,200)
$objForm.StartPosition = "CenterScreen"

$objForm.KeyPreview = $True
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter")
    {$x=$objTextBox.Text;$objForm.Close()}})
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape")
    {$objForm.Close()}})

$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(75,120)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
$OKButton.Add_Click({$x=$objTextBox.Text;$objForm.Close()})
$objForm.Controls.Add($OKButton)

$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(150,120)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Cancel"
$CancelButton.Add_Click({$objForm.Close()})
$objForm.Controls.Add($CancelButton)

$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,20)
$objLabel.Size = New-Object System.Drawing.Size(280,20)
$objLabel.Text = "Please enter the information in the space below:"
$objForm.Controls.Add($objLabel)

$objTextBox = New-Object System.Windows.Forms.TextBox
$objTextBox.Location = New-Object System.Drawing.Size(10,40)
$objTextBox.Size = New-Object System.Drawing.Size(260,20)
$objForm.Controls.Add($objTextBox)

$objForm.Topmost = $True

$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()

$x

Well, you’re right: that is quite a bit of code just to display an input box, isn’t it? And, to be honest, if that’s all you want to do is display this one input box, well, maybe this isn’t the best way to go. But if you’re interested in creating fancier applications, ones that incorporate list boxes and check boxes and calendar controls and who knows-what-all, well, in that case, there are some important lessons to be learned here.

What kind of lessons? Well, for starters, how about the process of creating a form in the place? To that end, our script begins loading two .NET classes: System.Drawing and System.Windows.Forms. We then instantiate a new instance of the .NET Framework class System.Windows.Forms.Form; that gives us a blank form (window) that we can start adding controls to:

$objForm = New-Object System.Windows.Forms.Form

Once we have an instance of the Form class we then assign values to three properties of this class:

  • Text. Disregard the name; this is actually the window title.

  • Size. This is the size of the form, in pixels. In our case, we’re creating a form that’s 300 pixels wide by 200 pixels tall.

  • StartingPosition. This is actually optional; if we leave this property out Windows will pick a location to use when displaying the form. By setting the StartingPosition to CenterScreen our form will automatically be displayed in the middle of the screen each time it loads. We just thought that was kind of cool.

That brings us to these three lines of code:

$objForm.KeyPreview = $True
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter")
    {$x=$objTextBox.Text;$objForm.Close()}})
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape")
    {$objForm.Close()}})

What we’re doing here is configuring our form so we can do two things:

  • Use the ENTER key instead of the OK button.

  • Use the ESC key instead of the Cancel button.

In order to do this we first set the KeyPreview property to true ($True); that tells the form to intercept specific keystrokes rather than allow those keystrokes to be used by the controls on the form. What keystrokes do we want to capture? You got it: the ENTER key and the ESC (Escape) key. Notice the syntax for capturing a keystroke:

$objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter")
    {$x=$objTextBox.Text;$objForm.Close()}})

As you can see, we’re using the Add_KeyDown method to add the ENTER key ($_.KeyCode –eq "Enter") to our “capture” list. We’re also specifying what we want to happen if and when the user does press the ENTER key: we want to set the value of a variable $x to the value stored in our text box ($objTextBox.Text), and we want to use the Close method to then close the form. That’s a very simple way for us to capture the value typed into the text box. (Are there other ways to do this? Yes, but for our simple form this seemed like an equally simple method for retrieving the text box value.)

Notice, too that, for the ESC key, we don’t set the value of $x to anything; instead we just close the form. Why do we do that? That’s right: because the ESC key is equivalent to the Cancel button which means that we want to cancel this operation without retrieving any value from the text box.

Incidentally, we agree with you: the syntax is a bit overwhelming at first. However, if you take a deep breath and just think about what we’re doing here, and why, you’ll see that it actually makes sense.

Speaking of OK and Cancel buttons, that’s our next step; this block of code adds a button labeled OK to our form:

$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(75,120)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
$OKButton.Add_Click({$x=$objTextBox.Text;$objForm.Close()})
$objForm.Controls.Add($OKButton)

To add a button, we first create an instance of the System.Windows.Forms.Button class. (This, by the way, is a standard theme when working with forms: each time you add a new control you’ll typically create a new instance of a .NET Framework class.) We next specify a value for the Location property (in this case our button is located 75 pixels from the left side of the form and 120 pixels down from the top of the form); then assign values to the Size and Text properties. After that we use this line of code to indicate what should happen when the user clicks this button:

$OKButton.Add_Click({$x=$objTextBox.Text;$objForm.close()})

Look familiar? It should; these are exactly the same things we want to happen if the user presses the ENTER button: we want to assign the value in our text box to $x, and we want to close the form. Simple, huh? From there we then use the Add method to add the button to the form:

$objForm.Controls.Add($OKButton)

And then we repeat the process to add the Cancel button as well.

See, that’s not so bad, is it?

Next we use a very similar approach to add a label and a textbox to our form. To add the label, we create an instance of the System.Windows.Forms.Label class; to add the textbox we use the System.Windows.Forms.TextBox class. After configuring properties for each control we once again use the Add method to add the item to the form.

Note. Needless to say, we’ve barely scratched the surface when it comes to configuring a control on a form. For more information, see the .NET Framework Class Reference on MSDN.

Admittedly, that’s a lot of typing. On the bright side, however, most of this is boilerplate: once you’ve figured out how to add a button or label or textbox or whatever to a form then you can simply copy and reuse that same code over and over. (Perhaps making a minor tweak here and there in order to change the Size or the Text or what-have-you.)

Best of all, at this point we’re almost done. Our next step is to set the value of the Topmost property to $True; this ensures that our form will appear as the topmost window on the desktop. (That is, when it pops up it won’t be hidden behind any other windows.) We then use this line of code to activate the form and give it the focus:

$objForm.Add_Shown({$objForm.Activate()})

All that’s left now is to use the following line of code to actually display the form onscreen:

[void] $objForm.ShowDialog()

Note. What’s the [void] for? That suppresses any messages that might otherwise be echoed back to the Windows PowerShell window when we display the form.

At that point we should see something very similar to this onscreen:

We can now type anything we want in the text box and then press ENTER (or click OK). When we do so, the form will disappear and the value we entered in the text box will be stored in the variable $x. (Something we verify by echoing back the value of $x with our very last line of code.) Alternatively, we could press ESC or click Cancel and the form will disappear without changing the value of $x.

Give it a try and you’ll see what we mean.

That’s all we have time for this week and, now that we think about it, for this month as well: the weekly Windows PowerShell tip will be on hiatus during the Scripting Games. However, we’ll be back on the first Friday in March and, to celebrate our return, we’ll show you how to display – and make use of – a popup calendar. See you then!