Building a PowerShell GUI (Part 10)

by [Published on 22 Sept. 2016 / Last Updated on 22 Sept. 2016]

This article continues the PowerShell GUI discussion by adding some extra interface controls and reorganizing the code to make it more logical.

If you would like to read the other parts in this article series please go to:

In my previous article, I showed you how to add some color to the GUI, how to perform a screen clear, and how to output data following a list box selection. All of these are important techniques that will be further used as we build a more useful application. However, I want to do a bit of cleanup work first.

One of the big problems with the script as it currently exists is that it doesn’t flow very well. Part of the reason for that is that the script has grown organically. It started out as a proof of concept script designed to show that it is possible to use PowerShell to display controls and information graphically.

As you may recall, the script’s initial screen currently displays a collection of virtual machines and asks the user to pick one. When the user makes a selection, the screen clears and the chosen VM is displayed within a text box.

We are eventually going to make this output far more useful, but for right now we need to do some cleanup work. The script could be better organized, and there is currently no option to end the script. Similarly, making a selection takes the user to an output screen, but there are no options on this screen to actually do anything. I want to address these issues because they directly impact usability. After that, we will focus on making the script a bit more useful.

In order to make the script easier to read, more easily extensible, and better functioning, I have done three things. I added a new text label, I added three buttons, and I completely reorganized the code. Let me show you what the scripts output looks like right now, and then I will discuss what I did to the script.

Here is the script’s initial screen. As before, the script acquires a list of the virtual machines residing on a Hyper-V host and displays those virtual machines within a drop box. I have rearranged the graphical elements on this screen in an effort to make the display a little bit cleaner, and I also added a Cancel button. Clicking on the Cancel button terminates the script and closes the window.

Image

As was the case before, a user can click on a virtual machine name, and then click OK. Doing so will cause the script to clear the screen, and then display the virtual machine name in a test box. However, you will notice that I have added a text label above the text box that says “Your Selected Virtual Machine”. This was done purely for cosmetic reasons. I also added two buttons. The Return button takes the user back to the script’s initial screen. The other button will eventually be used for something that I do not yet want to reveal. For now, clicking on the Reserved for Future Use button closes the window and terminates the script.

Image

As previously mentioned, I have also reorganized the code to make it easier to follow, and to make the script more easily extensible.  The script now contains the following sections (in this order):

  • Load Assemblies
  • Function to Display the Main Screen
  • Button Click Actions
    • Return Button Click Function
    • OK Button Click Function
  • Define All GUI Objects
    • Define Label
    • Define Label 2
    • Define Label 3
    • Define List Box
    • Define Button 1
    • Define Button 2
    • Define Button 3
    • Define Button 4
  • Draw the Empty Form
  • Populate the Form

OK, so the script is arranged more logically, but I also mentioned that I rearranged the code in an effort to make it more easily extensible. In fact, there are two aspects to the script that simply would not have worked had the code not been rearranged.

The first of these is the Return button, shown in the previous figure. Clicking Return clears the display and takes the user back to the screen that allows them to pick a virtual machine. Prior to rearranging the code, the code used to create the selection screen was in the script’s main body, not in a function. Moving that code into a function is what makes it possible to return to the main screen in response to a button press. When the user clicks the Return button, the script calls the function to display the main screen.  This is a two-step process. The first step is to clear the screen by removing the graphical elements that are currently being displayed. This is handled by the Return-MainScreen function, which you can see below:

#Define Return Button Click Function

Function Return-MainScreen

{

 

$Form.Controls.Remove($Label)

$Form.Controls.Remove($Label3)

$Form.Controls.Remove($Button3)

$Form.Controls.Remove($Button4)

$Form.Refresh()

Display-MainScreen

}

The last thing that this function does is to call the Display-MainScreen function, which displays the drop list and the virtual machine names. Here is what that function looks like:

# Display the Main Screen

Function Display-MainScreen

{

$Form.Controls.Add($ListBox1)

$Form.Controls.Add($Button1)

$Form.Controls.Add($Button2)

$Form.Controls.Add($Label2)

}

The other impact to rearranging the code was that the $Label variable was previously defined within a function called Display-VMInfo. The problem with doing so was that the $Label variable was local to the function. This meant that it was impossible to clear the screen prior to returning to the selection screen, because other parts of the script had no knowledge of the $Label variable and its associated text box. I could have solved the problem by defining $Label as a global variable, but it made more sense to move all of the GUI object declarations to one section of the code.

So with that said, here is what the code looks like in its entirety:

#Load Assemblies

[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null

[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") | Out-Null

$net = New-Object -ComObject Wscript.Network

 

 

 

 

# Display the Main Screen

Function Display-MainScreen

{

$Form.Controls.Add($ListBox1)

$Form.Controls.Add($Button1)

$Form.Controls.Add($Button2)

$Form.Controls.Add($Label2)

}

 

 

 

 

# ---------- Button Click Actions ------

 

 

#Define Return Button Click Function

Function Return-MainScreen

{

 

$Form.Controls.Remove($Label)

$Form.Controls.Remove($Label3)

$Form.Controls.Remove($Button3)

$Form.Controls.Remove($Button4)

$Form.Refresh()

Display-MainScreen

}

 

 

 

 

#Define OK Button Click Function

Function Display-VMInfo($ChosenItem)

{

 

#Clear the window

$Form.Controls.Remove($ListBox1)

$Form.Controls.Remove($Label2)

$Form.Controls.Remove($Button1)

$Form.Controls.Remove($Button2)

$Form.Refresh()

 

#Create Output

Add-Type -AssemblyName System.Windows.Forms

 

#Create text

$Label.Text = $ChosenItem

$Form.Controls.Add($Label)

$Form.Controls.Add($Label3)

$Form.Controls.Add($Button3)

$Form.Controls.Add($Button4)

}

 

 

 

 

 

#------ Define All GUI Objects-------

 

# Define Label - This is a text box object that will display VM data

$Label = New-Object System.Windows.Forms.textbox

$Label.Location = new-object System.Drawing.Size(50,50)

$Label.Size = New-Object System.Drawing.Size(250,150)

$Label.MultiLine = $True

 

 

# Define Label2 - The Please Make a Selection Text

$Label2 = New-Object System.Windows.Forms.Label

$Label2.AutoSize = $True

$Label2.Location = new-object System.Drawing.Size(20,50)

$Label2.ForeColor = "DarkBlue"

$Label2.Text = "Please select a virtual machine from the list."

 

 

# Define Label3 - The This is Your Selected Virtual Machine Text

$Label3 = New-Object System.Windows.Forms.Label

$Label3.AutoSize = $True

$Label3.Location = new-object System.Drawing.Size(50,30)

$Label3.ForeColor = "DarkBlue"

$Label3.Text = "Your selected virtual machine:"

 

 

# Define List Box - This will display the virtual machines that can be selected

$ListBox1 = New-Object System.Windows.Forms.ListBox

$ListBox1.Location = New-Object System.Drawing.Size(20,80)

$ListBox1.Size = New-Object System.Drawing.Size(260,20)

$ListBox1.Height = 80

 

                # This code populates the list box with virtual machine names

                $VirtualMachines = Get-VM

                ForEach ($VM in $VirtualMachines)

                                {

                                [void] $ListBox1.Items.Add($VM.Name)

                                }

 

 

# Define Button 1  - This is the selection screen's OK button

$Button1 = new-object System.Windows.Forms.Button

$Button1.Location = new-object System.Drawing.Size(20,170)

$Button1.Size = new-object System.Drawing.Size(70,30)

$Button1.BackColor ="LightGray"

$Button1.Text = "OK"

$Button1.Add_Click({$ChosenItem=$ListBox1.SelectedItem;Display-VMInfo $ChosenItem})

 

 

# Define Button 2 - This is the selection screen's Cancel button

$Button2 = New-Object System.Windows.Forms.Button

$Button2.Location = New-Object System.Drawing.Size(120,170)

$Button2.Size = New-Object System.Drawing.Size(70,30)

$Button2.BackColor ="LightGray"

$Button2.Text = "Cancel"

$Button2.Add_Click({$Form.Close()})

 

 

# Define Button 3 - This is the Return to Main Screen button

$Button3 = New-Object System.Windows.Forms.Button

$Button3.Location = New-Object System.Drawing.Size(50,220)

$Button3.Size = New-Object System.Drawing.Size(70,30)

$Button3.BackColor ="LightGray"

$Button3.Text = "Return"

$Button3.Add_Click({Return-MainScreen})

 

 

# Define Button 4 - This button doesn't do anything yet, but we will use it eventually.

$Button4 = New-Object System.Windows.Forms.Button

$Button4.Location = New-Object System.Drawing.Size(140,220)

$Button4.Size = New-Object System.Drawing.Size(150,30)

$Button4.BackColor ="LightGray"

$Button4.Text = "Reserved for Future Use"

$Button4.Add_Click({$Form.Close()})

 

# -------- This is the end of the object definition section ------

 

 

 

# -----Draw the empty form----

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

$Form.width = 525

$Form.height = 350

$Form.BackColor = "lightblue"

$Form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::Fixed3D

$Form.Text = "Hyper-V Virtual Machines"

$Form.maximumsize = New-Object System.Drawing.Size(525,350)

$Form.startposition = "centerscreen"

$Form.KeyPreview = $True

$Form.Add_KeyDown({if ($_.KeyCode -eq "Enter") {}})

 $Form.Add_KeyDown({if ($_.KeyCode -eq "Escape")

     {$Form.Close()}})

 

#----Populate the form----

Display-MainScreen

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

$Form.ShowDialog()

In this article, I have cleaned up the code used to create the GUI interface. I have also defined some new buttons that allow the code to behave more like an application than a quick and dirty proof of concept script.

Now that the code is better organized, there are two things that I plan to do. First, I want to modify the script to display useful information about the virtual machine that has been selected. Second, I want to modify the interface so that it becomes possibly to interact with the virtual machine. For example, I might build a control to turn a virtual machine on and off, or to change the amount of memory that is allocated to a virtual machine. In any case, my goal is to demonstrate that a PowerShell GUI can be used to interact with the operating system in a useful way.

If you would like to read the other parts in this article series please go to:

See Also


The Author — Brien M. Posey

Brien M. Posey avatar

Brien Posey is an MCSE and has won the Microsoft MVP award for the last few years. Brien has written well over 4,000 technical articles and written or contributed material to 27 books.

Advertisement

Featured Links