PowerShell Popup Prompt – How To Notify Users

PowerShell Popup Prompt – How To Notify Users

Do you display popups in your tooling?

Working within the tech industry you soon realise that even the savviest of users are prone to mistakes. Often a misclick or a mistyped value. These can all occur and depending on the action can have different levels of consequences. When developing tools I try to develop for everyone that might use the tool. I find that including Popup windows is a simple and effective way to reduce mistakes from happening. This allows a lot of people a second chance to review their work before proceeding.

A little background

A lot of tools I have developed for a handful of users who were completing actions for migrating users, mailboxes and data. As I progressed in this role I soon realise no matter the level of user you’re dealing with mistakes can often happen. Meaning as developers we need to ensure any action with a tool is thought about.

One way in which I tried to mitigate as many mistakes as possible was a confirmation box before any action would be taken. 

This would allow the user time to look over their selected inputs and review that the action they wanted to take was correct. By not only giving the time to confirm their actions but also give feedback to the user as something happened within the tooling. 

A popup box has multiple potentials and I have looked at a few possibilities with the version I created below.

Why?

As I worked within PowerShell as my default language I would often go the extra mile to create a GUI interface for all my tools.

I found that most people preferred to work this way. Although it took me longer to write a tool it also allowed people using the tool to take their time to complete the action and reduce risk. This made it simple for everyone to use and understand.

I want to explorer one versatile script I created within the GUI tools and that is the popup box. We are able to run this even if the whole tool is not set in a GUI and can use it for command-line applications as well. Sometimes it’s just nice to have that extra visual feedback. 

Within the tooling, the popup has also been extended to not only include confirmations and feedback but to also look at questions. I’ll show this towards the end and adds to the countless possibilities available to this tool.

Creating the popup

Load the ability to draw

#region GUIdesign
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

Colour Scheme – I like to be consistent

$SCRIPT:GreyColour = [System.Drawing.Color]::FromArgb(26, 26, 26)
$SCRIPT:MainWindowColour = [System.Drawing.Color]::FromArgb(38, 38, 38)
$SCRIPT:WhiteColour = [System.Drawing.Color]::FromArgb(255, 255, 255)
$SCRIPT:BlueBtnColour = [System.Drawing.Color]::FromArgb(82, 136, 174)
$SCRIPT:GreenColour = [System.Drawing.Color]::FromArgb(138, 199, 68)
$SCRIPT:RedColour = [System.Drawing.Color]::FromArgb(213, 0, 0)
$SCRIPT:Transparant = [System.Drawing.Color]::FromArgb(0, 0, 255, 0)
$SCRIPT:BlackColour = [System.Drawing.Color]::FromArgb(0, 0, 0)
$SCRIPT:WhiteColour = [System.Drawing.Color]::FromArgb(255, 255, 255)
$SCRIPT:HighlightGreen = [System.Drawing.Color]::FromArgb(72, 199, 68)
$SCRIPT:ActiveBlue = [System.Drawing.Color]::FromArgb(53, 90, 115)
$SCRIPT:ProgressBarForeColor = [System.Drawing.Color]::FromArgb(140, 195, 255)
$SCRIPT:ProgressBarBackColor = [System.Drawing.Color]::FromArgb(32, 130, 234)

Popup function

function Get-Popup {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true)]
        [String]$Message,
        [Parameter(Mandatory = $true)]
        [ValidateSet("Error", "Warning", "Information", "Confirmation", "Question")]
        [String]$Type,
        [Parameter(Mandatory = $false)]
        [String[]]$Buttons
    )

    $SCRIPT:Check = "False"
    $SCRIPT:PopCheck = $null

    $Window = New-Object System.Windows.Forms.Form
    $Window.StartPosition = "CenterScreen"
    $Window.BackColor = "White"
    $Window.FormBorderStyle = 'none'
    $Window.Size = '100, 100'

    $borderPanel = New-Object System.Windows.Forms.Panel
    $borderPanel.BackColor = $MainWindowColour
    $borderPanel.Location = '1,1'

    $PopTopPanel = New-Object System.Windows.Forms.Panel
    $PopTopPanel.Location = '0,0'
    $PopTopPanel.Anchor = "Top, Right, Left"
    $PopTopPanel.Backcolor = $GreyColour

    $SCRIPT:PopDragging = $false;
    $SCRIPT:PopmouseDragX = 0
    $SCRIPT:PopmouseDragY = 0

    $PopTopPanel.Add_MouseDown( {
            $SCRIPT:PopDragging = $true
            $SCRIPT:PopmouseDragX = [System.Windows.Forms.Cursor]::Position.X - $Window.Left
            $SCRIPT:PopmouseDragY = [System.Windows.Forms.Cursor]::Position.Y - $Window.Top
        })

    $PopTopPanel.Add_MouseMove( {
            if ($SCRIPT:PopDragging) {
                if ([System.Windows.Forms.Screen]::AllScreens.Count -gt 1) {
                    $PopScreenRight = [System.Windows.Forms.Screen]::AllScreens.WorkingArea[1].Right + $($Window.Width)
                    $PopScreenBottom = [System.Windows.Forms.Screen]::AllScreens.WorkingArea[1].Bottom + $($Window.Height)
                }
                else {
                    $PopScreenRight = [System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea.Right + $($Window.Width)
                    $PopScreenBottom = [System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea.Bottom + $($Window.Height)
                }

                $PopCurrentX = [System.Windows.Forms.Cursor]::Position.X
                $PopCurrentY = [System.Windows.Forms.Cursor]::Position.Y
                [int]$PopnewX = [Math]::Min($PopCurrentX - $SCRIPT:PopmouseDragX, $PopScreenRight - $Window.Width)
                [int]$PopnewY = [Math]::Min($PopCurrentY - $SCRIPT:PopmouseDragY, $PopScreenBottom - $Window.Height)
                $Window.Location = New-Object System.Drawing.Point($PopnewX, $PopnewY)
            }
        })

    $PopTopPanel.Add_MouseUp( {
            $SCRIPT:PopDragging = $false
        })

    $CloseWindow = New-Object System.Windows.Forms.Button
    $CloseWindow.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
    $CloseWindow.FlatAppearance.BorderSize = 0
    $CloseWindow.Backcolor = $GreyColour
    $CloseWindow.Size = '24,24'
    $CloseWindow.Cursor = 'Hand'
    $CloseWindow.Anchor = "Right, Top"
    $CloseWindow.Add_Click( {

            if ($Type -eq "Question" -or $Type -eq "Confirmation") {
                Write-Host "User quit our of prompt" -ForegroundColor Red
            }

            $Window.Close()
        })
    $CloseWindow.Image = [System.Convert]::FromBase64String("LINKED ON GITHUB")

    $OutputMessage = New-Object System.Windows.Forms.Label
    $OutputMessage.ForeColor = $WhiteColour
    $OutputMessage.BackColor = $MainWindowColour
    $OutputMessage.Location = '7,30'
    $OutputMessage.Size.Width = '' + $($Window.Size.Width - 45) + ''
    $OutputMessage.Font = New-Object System.Drawing.Font("Calibri", 16)

    if ($Type -eq "Confirmation" -or $Type -eq "Question") {
        $ConfirmationBox = New-Object System.Windows.Forms.CheckBox
        $ConfirmationBox.Text = "Please confirm the actions above and check the box to proceed."
        $ConfirmationBox.AutoSize = $true
        $ConfirmationBox.Size = '' + $($Window.Size.Width - 45 ) + ',100'
        $ConfirmationBox.ForeColor = $WhiteColour
        $ConfirmationBox.BackColor = $MainWindowColour
        $ConfirmationBox.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
        $ConfirmationBox.FlatAppearance.BorderSize = 0
        $ConfirmationBox.Font = New-Object System.Drawing.Font("Calibri", 14)
        $Window.Controls.Add($ConfirmationBox)
    }

    if ($Type -eq "Question") {
        $EnableBtn = New-Object System.Windows.Forms.RadioButton
        $EnableBtn.Text = "Yes"
        $EnableBtn.Size = '100,35'
        $EnableBtn.ForeColor = $WhiteColour
        $EnableBtn.BackColor = $MainWindowColour
        $EnableBtn.Font = New-Object System.Drawing.Font("Calibri", 14)
        $EnableBtn.Checked = $false

        $DisableBtn = New-Object System.Windows.Forms.RadioButton
        $DisableBtn.Text = "No"
        $DisableBtn.Size = '100,35'
        $DisableBtn.ForeColor = $WhiteColour
        $DisableBtn.BackColor = $MainWindowColour
        $DisableBtn.Font = New-Object System.Drawing.Font("Calibri", 14)
        $DisableBtn.Checked = $false

        $Window.Controls.Add($EnableBtn)
        $Window.Controls.Add($DisableBtn)
    }

    $OkayBtn = New-Object System.Windows.Forms.Button
    $OkayBtn.Text = "OKAY"
    $OkayBtn.Size = '115,25'
    $OkayBtn.ForeColor = $WhiteColour
    $OkayBtn.BackColor = $BlueBtnColour
    $OkayBtn.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
    $OkayBtn.FlatAppearance.BorderSize = 0
    $OkayBtn.Cursor = "Hand"
    $OkayBtn.Add_Click( {
            if ($Type -eq "Confirmation" -and $ConfirmationBox.Checked -ne $true) {
                Get-Popup -Message "Ensure you have confirmed your actions." -Type Information
                $SCRIPT:Check = "False"
            }
            elseif ($Type -eq "Confirmation" -and $ConfirmationBox.Checked -eq $true) {
                $SCRIPT:Check = "True"
            }

            if (($Type -eq "Question") -and ($EnableBtn.Checked -eq $false -and $DisableBtn.Checked -eq $false) -and ($ConfirmationBox.Checked -eq $false)) {
                Get-Popup -Message "No action selected and confirmation not selected" -Type Information
                Write-Host "Popup box canclled confirmation and selection were empty" -ForegroundColor Blue
                $SCRIPT:Check = "False"
            }
            elseif (($Type -eq "Question") -and ($EnableBtn.Checked -eq $true -or $DisableBtn.Checked -eq $true) -and ($ConfirmationBox.Checked -eq $false)) {
                Get-Popup -Message "Ensure you've ticked the to confirm your action" -Type Information
                Write-Host "Popup box was cancelled confirmation box not ticked" -ForegroundColor Purple
                $SCRIPT:Check = "False"
            }
            elseif (($Type -eq "Question") -and ($EnableBtn.Checked -eq $false -and $DisableBtn.Checked -eq $false) -and ($ConfirmationBox.Checked -eq $true)) {
                Get-Popup -Message "Ensure you've selected an action" -Type Information
                Write-Host "Popup box canclled no action selected" -ForegroundColor Red
                $SCRIPT:Check = "False"
            }
            elseif ($Type -eq "Question" -and ($EnableBtn.Checked -eq $true -or $DisableBtn.Checked -eq $true) -and ($ConfirmationBox.Checked -eq $true)) {
                if ($EnableBtn.Checked -eq $true) {
                    $SCRIPT:Check = "True"
                    $SCRIPT:PopCheck = "Enable"
                    $Window.Close()
                }
                elseif ($DisableBtn.Checked -eq $true) {
                    $SCRIPT:Check = "False"
                    $SCRIPT:PopCheck = "Disable"
                    $Window.Close()
                }
            }

            $Window.Close()
        })

    $Window.Controls.Add($OkayBtn)
    $Window.Controls.Add($OutputMessage)
    $Window.Controls.Add($CloseWindow)
    $Window.Controls.Add($PopTopPanel)
    $Window.Controls.Add($borderPanel)

    $OutputMessage.AutoSize = $true
    $borderPanel.AutoSize = $true
    $OutputMessage.Text = "$($Message)"
    $Window.AutoSize = $true

    if ($Type -ne "Confirmation" -and $Type -ne "Question") {
        $OkayBtn.Location = '' + [math]::Round($($Window.Size.Width / 2 - $OkayBtn.Size.Width / 2)) + ',' + ($OutputMessage.Size.Height + $OutputMessage.Location.Y + 20) + ''
        $Window.Size.Height = '' + ($OkayBtn.Location.Y + $OkayBtn.Size.Height) + ''
        $borderPanel.Size = '' + $($Window.Size.Width) + ',' + $($Window.Size.Height) + ''
    } elseif ($Type -eq "Confirmation") {
        $ConfirmationBox.Location = '10,' + $($OutputMessage.Location.Y + $OutputMessage.Size.Height + 20) + ''
        $OkayBtn.Location = '10 ,' + $($ConfirmationBox.Location.Y + $OutputMessage.Size.Height + 20) + ''
        $Window.Size.Height = '' + ($OkayBtn.Location.Y + $OkayBtn.Size.Height) + ''
        $borderPanel.Size = '' + $($Window.Size.Width) + ',' + $($Window.Size.Height) + ''
    } else {
        $ConfirmationBox.Location = '10,' + $($OutputMessage.Location.Y + $OutputMessage.Size.Height + 20) + ''
        $EnableBtn.Location = '10,' + $($ConfirmationBox.Location.Y + $ConfirmationBox.Size.Height + 5) + ''
        $DisableBtn.Location = '10,' + $($EnableBtn.Location.Y + $EnableBtn.Size.Height + 5) + ''
        $OkayBtn.Location = '' + [math]::Round($($Window.Size.Width / 2 - $OkayBtn.Size.Width / 2)) + ',' + ($DisableBtn.Size.Height + $DisableBtn.Location.Y + 20) + ''
        $Window.Size.Height = '' + ($OkayBtn.Location.Y + $OkayBtn.Size.Height) + ''
        $borderPanel.Size = '' + $($Window.Size.Width) + ',' + $($Window.Size.Height) + ''
    }


    $PopTopPanel.Size = '' + $($Window.Size.Width - 10) + ',25'
    $PopTopPanel.Location = '5,5'
    $CloseWindow.Location = '' + $($Window.Size.Width - 45) + ',5'


    [void]$Window.ShowDialog()
}

Lets call the function

The confirmation box can take 5 different inputs depending on what you’re trying to achieve. 

First, we have the options for feedback to the user, they get an okay button to confirm the message they’ve received. 

These include Error, Warning and Information. 

The second set of prompt types include Confirmation and Question. These options give a bit more flexibility and require user input. 

Let’s take a look at each one and how they respond. 

Error

Get-Popup -Message “There has been an error” -Type Error

Warning

Get-Popup -Message “This is a warning” -Type Warning

Information

Get-Popup -Message “This is some information” -Type Information

Although the top 3 look very similar, you can customise the prompt to show a title or in my needs, I have a log file that shows if the user was given a warning, information or error message. This can be a fantastic help when it comes to analysing any issues that occurred. 

Clicking Okay, or ‘X’ will exit out the prompt without any actions being taken as these are just for displaying information.

The last two options that we have are confirmation and question. Let’s look at confirmation first. This is fantastic if you’re wanting to confirm an action before a large chunk of the script is ran.

Confirmation

Get-Popup -Message “Conf” -Type Information 

If you click okay, cancel out the prompt or confirm and click okay we get different results. This can be tailored as you use your script. 

Click Okay without confirmation

Click X to close

Use Accepts and clicks okay

The prompt will disappear and continue your next action.

What about a question to the user?

Next, I wanted to create something that would give a specific set of questions depending on what’s happening.

Get-Popup -Message “Question” -Type Question

As you can see we are now prompted with a Yes or No value although I have not expanded on this option for personal use, you could easily enable to option to include your own options within the set radio set. This has countless possibilities. 

Easy to call from any command-line scripts as well as full GUI Scripts. 

Have you created your own GUI prompts for PowerShell? Would you have done anything differently? 

I am happy with how this turned out in the end, if you want to explorer the tooling on GitHub you can follow the (link) here.

PowerShell books on Amazon:

Leave a Reply

Your email address will not be published.