Back in mid-September, I presented a talk on “Building PowerShell GUI Tool Solutions” at the PowerShell Conference Asia in Singapore. The talk centered around GUI application design and development using PowerShell as the core language. During that talk , I developed an application that used PowerShell’s PSGet module to communicate with the PowerShell Gallery. Finishing work was done by Devin Leaman of SAPIEN Technologies. The recorded talk should be available at some point on the PowerShell Conference Asia Website.
The application creates an interface that displays gallery module information and allows the user to easily add and remove modules from their system. The complete source code can be found on the SAPIEN github repository here.
The github repository contains the complete PowerShell Studio source code as well as a packaged .EXE of the final project and an .MSI ready for deployment. For those without PowerShell Studio, we have exported the complete project into a .PS1 file that can be run from the PowerShell console. We have also taken the .PS1 file and “modularized” it and posted it to the PowerShell Gallery here.
So let’s take a look at the project…
Figure 1 shows the completed application interface. The top left pane displays a sorted list of the modules found on the PowerShell Gallery. The top right pane shows details for the module selected from the module list. The bottom pane is basically a dump of all the actions that are taken by the user and PowerShell cmdlet responses.
At the top of our file we start off with two calls to Import-Module: the first to import PowerShellGet and the second to import PackageManagement. (Actually, these are inserted automatically by PowerShell Studio when we include the appropriate cmdlet in our code.)
Since GUI applications are event driven, most of our operational code goes into event-handlers. These are functions that are called by the application when a specific event occurs. For instance, when a user clicks on a button, that button’s “click” event handler is called. Default event handlers are automatically generated by PowerShell Studio when you double-click on an object in the designer. Event handlers are named using the following formula:
So for a BUTTON named MyButton, the CLICK event handler would be named:
For the purposes of this blog article, we are only going to examine the actual code that gets executed when an event occurs. Bear in mind that many object properties have been manipulated in the object ‘s Property list to help create a working UI. These are not being discussed here.
The first event handler that we are concerned with is the form_load event ($formPowerShellGet_Load), seen at the top of Figure 2. This event gets fired when the form is first loaded (as the name implies.) Our form_load event handler calls a user defined function called Check-NuGet which verifies the availability of NuGet on the system. If NuGet is not available it prompts the user to download and install it. Add-TextToOutputWindow is the helper function that writes to the output pane at the bottom of the application.
With NuGet installed, we can then implement our menu selection File->Get Modules. Besides enabling some menu options which the reader can explore on their own, this event handler calls a user defined function called Update-ListView as seen in Figure 3.
This is a fairly long function so let’s go through it a step at a time.
First, because we know that this function will take a bit of time (as it needs to communicate across the internet), we change the cursor to a wait cursor on line 22. We also could have put this code in a separate thread so as not to impact the UI. In lines 24-29, we store the top item’s index so that when we refresh the list we can return it to the same position. In lines 31-34, we check to see if there is an item selected, and if so, we store that item’s index.
Using the Find-Module cmdlet (line 40), we connect to the PSGallery and store the result in a local variable, $psgModules. Then in line 43 we use Get-InstalledModule to get a list of the currently installed modules and store that as well ($instModules).
In lines 46-65, we populate our list. By enclosing all the work between .BeginUpdate and .EndUpdate calls, we delay drawing the list until all the work has been done, thereby eliminating flashing of the list control. For each module in $psgModules, we get the name and the version and place those items into the list control. We also store the module itself in the listItems’s TAG property for easy access to the module later. See this article for more detailed information about the TAG property.
In lines 55-63 we check to see if the module we just added is included in our installed modules ($instModules) array and if it is, we add a checkmark image to the list control.
Remember when we stored the current selection above in line 33? Well, next we use that to check to see if an item had previously been selected and if so, we select it in the list. Then we scroll the list to the same location we stored ($topIndex) in line 26 to restore the list to the same state it was in previously (assuming that it had been scrolled and had an item selected.)
Finally, we restore the cursor to an arrow cursor.
Whew! Quite a lot of work there. I think that is enough for this post. We’ll look at the rest of the code in part 2.