Skip to content
Menu
ScriptingNerd
  • Home
  • Quick Tips
  • GitHub
ScriptingNerd

Writing Powershell GUIs the easy way

Posted on February 9, 2019April 20, 2022 by Patrik

One of the most important tools for any IT professional or service desk technician that works in a Windows environment is without a doubt Powershell. Not only is it extremely powerful but it is also very fun to work with if you ask me.

One of the great features of Powershell that I feel doesn't get enough love from the Powershell community is the ability to write scripts with graphical user interfaces. Yeah, yeah, I know, you want to be able to do a quick change in the script and then rerun it inside ISE and anyone that doesn't know how to use a console shouldn't be in IT anyway, right? Wrong!

It is my firm belief that a clear and easy to use interface is just as important as the logic itself when you write a script that is going to be used by someone else. For most people, that means the interface needs to be graphical. I base this belief on having worked in an IT department at a large company for several years as well as maintained the Booksonic project since the start in late 2015. If you want to change my mind, feel free to try and do so in the comments below.

Now with that out of the way I will stop trying to convince you about writing user interfaces for your scripts, the fact that you are reading this post probably means that you are already doing it or thinking about doing it. Instead I am going to focus on how to do it in a way so the code is easy to read and maintain even once you start writing more complex UIs.

When I first started writing powershell I noticed that most examples of people writing powershell UIs get really messy real fast.

A typical example you may find online looks something like this

Add-Type -AssemblyName System.Windows.Forms
Add-Type -Name Window -Namespace Console -MemberDefinition '
        [DllImport("Kernel32.dll")]
        public static extern IntPtr GetConsoleWindow();     
        [DllImport("user32.dll")]
        public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);

[Console.Window]::ShowWindow([Console.Window]::GetConsoleWindow(), 0)       '

$font = New-Object System.Drawing.Font("Times New Roman",24,[System.Drawing.FontStyle]::Bold)

$label = New-Object System.Windows.Forms.Label
$label.Cursor = [System.Windows.Forms.Cursors]::Hand
$label.Text = "Click me to open popeen.com" 
$label.BackColor = "Transparent"
$label.ForeColor = "Blue" 
$label.AutoSize = $true
$label.Location.X = 15
$label.Location.Y = 15
$label.Add_Click = { Start-Process -FilePath iexplore  -ArgumentList "https://popeen.com" }

$form = New-Object system.Windows.Forms.Form
$form.Width = 600
$form.Height = 200
$form.Text = "Example form"
$form.Font = $font
$form.Controls.Add($label)
$form.ShowDialog() 

Now functionally speaking there is nothing wrong with that script and in fact it may not look that bad either but remember, all we have done is created a window and added one label to it. Imagine how cluttered this would become once you started doing some more advanced UIs. A better way to write the above that I have actually never seen anyone use other than me and some of my colleages would be using -Properties like this.

Add-Type -AssemblyName System.Windows.Forms
Add-Type -Name Window -Namespace Console -MemberDefinition '
        [DllImport("Kernel32.dll")]
        public static extern IntPtr GetConsoleWindow();     
        [DllImport("user32.dll")]
        public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);

[Console.Window]::ShowWindow([Console.Window]::GetConsoleWindow(), 0)

$label = New-Object System.Windows.Forms.Label -Properties @{
    Cursor = [System.Windows.Forms.Cursors]::Hand
    Text = "Click me to open popeen.com" 
    BackColor = "Transparent"
    ForeColor = "Blue"
    AutoSize = $true
    Location = "15, 15"
    Add_Click = { Start-Process -FilePath iexplore  -ArgumentList "https://popeen.com" }
}

$form = New-Object system.Windows.Forms.Form -Properties @{
    Size = "600, 200"
    Text = "Example form"
    Font = New-Object System.Drawing.Font("Times New Roman", 24, [System.Drawing.FontStyle]::Bold)
}
$form.Controls.Add($label)
$form.ShowDialog() 

While this takes up about the same space it looks much better right? This way a quick glance at the code gives you a much better understanding of what it is you are looking at.

If you choose to stop reading here and start writing your UIs in this way I beleive you will have a much easier time maintaining that script then you had before. However, I have taken it a step further in my scripts.

When I write a script I use a specific framework that makes the code even quicker to read and also includes some other nice features that I won't go in to in this post but might make future posts about. One of the most important parts of this framework is a module I call EasyGUI and that you can download at https://gitlab.com/Popeen/EasyGUI

This module builds on the second example above and simplifies it even more. It also includes some other cool stuff that I also won't go in to here but again, might make another post about. If we decided to use EasyGUI to write the example above it would look like this

Import-Module EasyGUI

Initialize-EasyGUI
Hide-Console

$form = New-Form @{
    Size = "600, 200"
    Text = "Example form"
    Font = New-Font -Font "Times New Roman" -Size 24 -Style $FONTSTYLE.Bold
}

$label = New-Label @{
    Cursor = $CURSOR.Hand
    Text = "Click me to open popeen.com" 
    BackColor = "Transparent"
    ForeColor = "Blue"
    AutoSize = $true
    Location = "15, 15"
    Add_Click = { Start-Process -FilePath iexplore  -ArgumentList "https://popeen.com" }
}

$form.Controls.Add($label)

Show-Form $form
Stop-Console 

I dont know about you but I find that to be much more readable then the first example we looked at, especially when you are working with a lot more then just a label.

It should be noted that not every forms object is in EasyGUI yet as I add them when I need them but adding them yourself should be very easy even for someone new to Powershell and if not just post a comment below and let me know what object you would like added.

I would love to hear your thoughts on this, is it something you would ever use or do you think I'm just talking rubbish when I say that UIs are this important or that a simple module like this can make it a lot more quick, fun and easy to code and maintain UIs.

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

  • Patrik Johansson
    Senior System Administrator

    Working with a wide flora of systems but with a focus on Configuration Manager (MEMCM/SCCM).

    Passionate about Powershell and building tools that are easy to use for everyone.
    In my spare time I work on other coding projects as well, mainly Booksonic

    LinkedIn GitHub Blog

Recent Posts

  • How to download CMTrace from Microsoft
  • Powershell counting to $null instead of 1
  • Running ForEach in parallel on Windows Powershell 5 (and older)
  • Use Powershell to create a "fake" program in the programs and features list for indexing purposes
  • How to read the manifest of an appx package file using Powershell

Archives

  • February 2023
  • April 2022
  • November 2021
  • March 2020
  • December 2019
  • February 2019

Categories

  • Powershell
  • Quick Tips
  • Below are affiliate links, if you click on them and buy something I may earn a small amount of money without any additional cost to you. Any links found here are for products I am using myself and can vouch for.
  • InterServer
    Very cheap SSD/HDD based virtual servers (VPS). Starting at $6/month for 1TB. Use coupon code BOOKSONIC and get the first month for just 1 cent. I have recently started using intersever for personal projects and so far I am very happy with them.
  • Contabo
    Cheap HDD/SSD/NVMe based Virtual servers (VPS)
    I have used Contabo for years both for my private needs as well as to host the booksonic demo server
©2023 ScriptingNerd | Powered by WordPress & Superb Themes