A Quicker PowerShell Form

What is the most controversial thing you can do with PowerShell? Build a GUI. Who wants a GUI when keeping it in the shell lets everyone walking past your cube make some comment about the Matrix? Here is the key: it’s not about you. Typically, when the need arises to create a GUI, it is so you can give someone else a tool that is easy to use but powerful (like PowerShell). My personal motivation was to provide a tool for our Help Desk.

In my previous post, I hinted at a follow-up. Here it is. Why was I trying to create a function that could name a variable and turn it into an object? Because creating GUIs from PowerShell is a mess. And that kind of makes sense all things considered. If you want to get a primer on creating .NET forms from PowerShell, check out http://blogs.technet.com/b/csps/archive/2011/12/07/guiapp.aspx. Following that link will put you face-to-face with code like this:


$objOutputBox = New-Object System.Windows.Forms.TextBox

$objOutputBox.Location = New-Object System.Drawing.Size(680,40)

$objOutputBox.Size = New-Object System.Drawing.Size(460,500)

$objOutputBox.Multiline = $True

$objOutputBox.Font = New-Object System.Drawing.Font("Courier New", "8.5")

$objOutputBox.Wordwrap = $True

$objOutputBox.ReadOnly = $True

$objOutputBox.ScrollBars = [System.Windows.Forms.ScrollBars]::Vertical

$objForm.Controls.Add($objOutputBox)

As with my “car” example in the previous post, this gets messy and repetitive. I found that when creating form objects, there were certain attributes I used over and over: type (button, label, etc.), size, location, parent (the object the new object would attach to), and text. Also explained in the previous post, creating a function to handle this was a bit arduous. But, here it is. A function to allow faster (and more readable in my opinion) .NET form creation in PowerShell:


Function New-FormObject ($varName,$parent,$type,$size,$location,$text) {

$object = New-Object System.Windows.Forms.$type
$object.Location = New-Object System.Drawing.Size($location)
$object.Size = New-Object System.Drawing.Size($size)
$object.Text = $text
New-Variable $varName -Value $object -Scope Global
If ($parent -ne $null) {
(get-variable $parent).Value.Controls.Add((get-variable $varName).value)
}

}

New-FormObject "myButton" "myTab1" "button" "75,30" "10,10" "Click Me!"

The above code creates the “$myButton” button, sets its size, location, text, and then adds it as a control to the “$myTab1” tab. There is also some logic that handles an object that will not be attached to a tab or some other object. This would be for creating another form (a pop-up perhaps?). For example:

New-FormObject "alertForm" $null "form" "400,200" "0,0" "Alert"

It is important to note the scope given to the “$myButton” variable. In this case it is global, but that is because I pulled it in from another script. Typically, you would only need to set the scope to script. The reason is that you want to manipulate and use the object outside of the function. If we create a text box using this function, we want to be able to pull text from it and put text into it later on.

The next important thing to note is “(get-variable $parent).Value.Controls.Add((get-variable $varName).value)”. As mentioned in the previous post (how many times will I use that line?), since we are passing variable names through the function, we have to modify them using Set- and New-Variable. To get data from the variables, we need to use Get-Variable. So, typing “(Get-Variable $parent)” is similar to typing “$myTab1”. Similar because, as you will note, there is this “value” business. When you “Get-” a variable, it returns the variable with two members: Name and Value. To really return the same thing as “$myTab1” you need to type “(Get-Variable $parent).value”. Otherwise, it is an apples and oranges type of situation.

Finally, a comment on the attributes. I chose to make the function accept a variable name, parent name, object type, size, location, and text. That is only because I use those attributes most commonly. You could easily add your own by including additional parameters. Too many parameters might start to get clunky, which is why I need to create a smarter function that can adapt based on the attributes sent to it. But, this has been a great start for me. The benefits have already paid off while reworking a GUI script nearly 1000 lines long.

Advertisements

2 thoughts on “A Quicker PowerShell Form

  1. Ross Johnston says:

    I’m relatively new to Windows Forms, so I’m not sure if this is the case for all .NET versions (I’m using .NET 4.5 on Windows 8.1) but I found the $location value does not work when defining a Form. I had to modify the function to add a special case when defining a Form:

    if( $type.ToUpper() -eq ‘FORM’ )
    { $object.StartPosition=”Manual”; $object.Location=$location }
    else { $object.Location = New-Object System.Drawing.Size($location) }

    • Thanks for sharing. I have only tested this on Windows 7 with .NET 4.0. By the way, if you are interested in building forms for PowerShell, I suggest looking into a web app that can call PowerShell scripts in the background, or send requests to a messaging bus. A solution like that will be more accessible and the form elements will be a bit simpler to maintain.

      Thanks again!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s