HOWTO: Quickly create and iterate a GUI for your PowerShell script

Due to the nature of how PowerShell was designed, it actually offers direct access to the .NET framework which means we can do some pretty cool things.  For example, with only a relative few lines of code, we can create a full “Visual Basic” style GUI based application entirely in PowerShell!  The catch to this however is that you will likely typically be building this in your PowerShell IDE of choice such as the PowerShell ISE or PowerGUI — both of which are purely text editors.  This means that in order to “draw” the windows, you’ll need to figure out all of the x,y positions of all of the windows and their relationship to each other and… well that becomes a nightmare pretty quickly.  I decided to see how much of this process I could automate or streamline.  Now it’s worth mentioning that there are GUI based designers available such as Sapien PowerShell Studio that can drastically simplify the process.  Unfortunately this is a commercial application.  I wanted to see if I could find a free alternative.  After playing around, I came up with and solved for the following requirements:

1) Be able to use a GUI based tool to easily design the layout of my PowerShell GUI based application

2) Be able to define the layout of my form in a different file thus separating out the layout from the “business logic”

3) Be able to quickly change and iterate the layout of the GUI after it was initially created

4) Use only free tools to accomplish the above

I’m happy to say I was able to accomplish all of these goals and I did so by taking advantage of PrimalForms Community Edition.  This is a free software put out by Sapien that allows for a drag and drop style interface for PowerShell UI creation.  I suppose I should actually say “was a free software” however.  It turns out that Sapien has discontinued the free version and rebranded the commercial iteration as PowerShell Studio and removed the links to the free version from their website.  Fortunately, the free version is still available from other sources online.  At the time of this writing, I was able to download version 1.0.6.0 from here.  Now an important disclaimer.  It turns out this software does not work with Windows 8/8.1.  It’ll install fine but will never actually open.  Some investigation reveals something to do with how it relies on .NET Framework 2.0 and how that’s changed in newer Windows releases.  So if you’re going to use this, you’ll have to install it on a Windows 7 machine.  In my case, I used a virtual machine.

Once the software is installed, build the form the way you would like it, being sure to name your fields with a useful name as you will be referencing these in your PowerShell script to actually make this do something later.  Note I have created a simple sample GUI below.  It doesn’t do anything and is for illustrative purposes only.

image

Once the form looks the way you’d like to to look, you should save the layout file.  This will save in a .PFF extension and I recommend you keep this in the project directory for your script so you can easily modify it in the future.  To actually add this layout to your PowerShell script however, you need to export the script using the Export Powershell button on the ribbon.  You’ll have the option to export to a file or the clipboard.

Either way, save the script project folder and give it a .psm1 extension to make it a Powershell module.  Once you’re done, you’ll get something that looks like this:

image

This is impressively everything you need as you can run this code directly in PowerShell and get your GUI on screen.  However, as the code exists now, we’d have to add all of our business logic directly into this file.  That would be fine except that if we decide we later need another element such as a button or a listbox, we’d have to generate a new file and carefully copy in our changes.  That’s a recipe for frustration.  So instead we’re going to turn the code that PrimalScript generates into a module and make it responsible only for window layouts and we’ll do our custom logic in a separate file.  To accomplish this, we have to change a few things in the file.

First, all of the windowed objects that PrimalScript generates are scoped to only exist inside a function.  Since we need to access these in another file, we have to scope them differently.  In my case, I have decided to scope them globally so they are available everywhere.  I know this isn’t always best practice for programming but it fits all of my requirements here so we’ll run with it.  The next thing we need to do is remove all of the OnClick events script blocks that it tries to initialize as we’re going to move those into our business logic file.  Lastly, this script includes an execution of the function at the end of it and so we want to remove that.

Now we can do everything I just said manually as it actually doesn’t take that long. But if you’re going to iterate through a design multiple times, that’s quickly going to get annoying which means it’s time to automate.  Below is the PowerScript script I wrote that accomplishes the steps above. Disclaimer: This script has only been tested when using the Button, ListBox and Textbox controls. It’s possible other controls may alter the structure of the file and produce unexpected results.

# Transforms output from PrimalForms Community Edition to make the output work as a module
# PrimalForms; GUI; 

# Define the location of the layout file that was generated from Primal Script Community Edition 1.0.6.0
$SourceFilePath = "C:\temp\test2.psm1"
# Get the contents of the generated script and save it into a variable called $SourceFile so that we can reorder and remove the code to meet our needs
$SourceFile = get-content $SourceFilePath -ErrorAction SilentlyContinue
# If last action (trying to load the file) failed, provide browse file dialog box so the user can select the file
if (! $?) 
{ 
	$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
	$OpenFileDialog.filter = "All Files (*.*)|*.*"
	$OpenFileDialog.ShowDialog() | Out-Null
	$SourceFile = get-content $OpenFileDialog.filename -ErrorAction SilentlyContinue 
}

# The first part of the script is the defintion of the GUI objects.  Fortunately, PrimalScript marks these with region tags
# We therefore want to identify the line numbers where these defintions begin and end so we can move them outside of the function and declare them global
# Note: We need to subtract 2 from the Startline because our method of line removal starts counting at zero while select-string returns a value that starts
# counting at 1.  We other +1 is because we want to also exclude the line that we have selected so go to the one before that 
$StartLine = ($SourceFile | select-string "Generated Form Objects" | select linenumber -First 1).LineNumber -2
$EndLine = ($SourceFile | select-string "Generated Form Objects" | select linenumber -Last 1).LineNumber
$LastLine = $SourceFile.Count

# Start by grabbing the New-Object defintions from the middle of the file and inserting them at the start of the new file
$outTempA = $SourceFile[$StartLine..$EndLine] -replace '\$', '$global:'
# Append after the New-Object initalizations the rest of the original file up to where the original defintions started and skip them
$outTempA += $SourceFile[0..$StartLine]
# Grab the remaining data and dumb it into the new variable
$outTempA += $SourceFile[$EndLine..$LastLine]

# Remove the section with the custom defined button functions as we'll run those from a seperate file
$StartLine = ($outTempA | select-string "Generated Event Script Blocks" | select linenumber -First 1).LineNumber -2
# The original file doesn't make it obvious where this script block ends so we'll look for the second reference to the #----- line
$EndLine = ($outTempA | select-string "#----------------------------------------------" | select linenumber -skip 2).LineNumber
# Take everything from the working variable (the one with the New-Objects at the start) and load it into the new variable
$outTempB = $outTempA[0..$StartLine]
# Skip over (effectively deleting) the sections found for the event script blocks 
$outTempB += $outTempA[$EndLine..$LastLine]

# The end of the script includes the execution of the function.  As this will be a module, we don't want that so we need to remove that call
$StartLine = ($outTempB | select-string "Call the Function" | select linenumber -First 1).LineNumber -2

# Overwrite the original file with the new one featuring our changes and bring everything just before the function call 
$outTempB[0..$StartLine] | Set-Content $SourceFilePath –Force

At this point you should now have a file called something like formlayout.psm1 that will will create all of your GUI objects. The next thing you need to do now is create a new PowerShell script file and include this line at the top:

$FormModulePath = "$PSScriptRoot\formlayout.psm1"
if (Get-Module -Name (([io.fileinfo]$FormModulePath).basename)) 
	{ Write-Output "Already loaded in memory, reloading..."; Remove-Module  (([io.fileinfo]$FormModulePath).basename); Import-module $FormModulePath; }
else
	{ Write-Output "Module not found.  Importing..."; Import-module $FormModulePath; }

Note we are going out of our way to manually reload it if it’s already in memory as for testing purposes if you make changes, you don’t want to be using the obsolete version.

There are two things left to do.  First we need to create some code to make our GUI objects do something.  This is done through the use of “Script Blocks”.  They are written in the notation of $Variable_OnClick={ }.  Note the ={}.  This means that whenever (in this case) the OnClick event is fired for the GUI component, the code inside the script block will execute.  Consider this simple example that will update the listbox with a random number every time the Add button is pressed:

$cmdAdd_OnClick={
	$lstServers.Items.Add((Get-Random))
}

Ok, now the absolute last thing we need to do is call the GUI form that PrimalForms generated so it will display to the screen. You do this simply calling the function at the very end of your script. The default name that PrimalForms generates is generateform. When we put it altogether, we can end up with a full graphical front end for your PowerShell script!

 

guiexample1

 

8 comments

Skip to comment form

    • Henning on July 4, 2015 at 12:17 pm
    • Reply

    Hello,

    i am beginning to code some powershell scripts.

    It is required for me to have a nice GUI.

    For a long time i am looking for the PrimalForms CE Edition.

    No link or anything is avaiable.

    Is there any way to get it from you ?!?

    Please….kind regards,
    Henning

    1. It seems that Primal Forms Community Edition really is no longer available. I tried to find a new source online but was unable to do so. I’m uncertain of the legalities of hosting what was free software that is no longer supported (aka Abandonware). For now, I’ve made the software available here:

      http://pleasework.robbievance.net/PowerShellScripts/PrimalForms.msi

      However please note that if Sapien contacts me and requests that I remove the download, I will do so.

      I hope you find this useful.

  1. PrimalForms CE is copyrighted software. You cannot host our software on your site for download. Please remove the download.

  2. Hi Alexander,

    It is unfortunate that a software is expressly called the “Community Edition” is no longer available to the community. But such is life I suppose.

    The software has been removed.

    • Shiron on March 6, 2016 at 2:36 am
    • Reply

    There you are guys http://www.filedropper.com/primalforms

    • freetools on March 10, 2016 at 7:58 am
    • Reply

    @Shiron : Thanks , I already had it installed on my pc but lost he MSI , now i have it back.

    Thanks.

    • Ernie on March 7, 2017 at 10:58 pm
    • Reply

    I have multiple list boxes, say ListBoxA and ListBoxB. I want to be able to drag and drop line items from ListBoxC to either ListBoxA or ListBoxB in Powershell.
    Does any one have any ideas?

    Thanks
    Ernie

    • Nazir Shaikh on March 16, 2022 at 9:00 am
    • Reply

    Can anyone please share the PrimalForms.msi v1.0.10?

Leave a Reply to Ernie Cancel reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.