
Sometimes it’s not possible to fully automate a certain process and we need some input from the user(s) of the script in order to determine the further path of action. If this is based on a fixed set of choices the built-in PromptForChoice method can come to the rescue. Here is an example:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| $Title = "Title" | |
| $Info = "Pick Something!" | |
| $options = echo Option1 Option2 Option3 | |
| $defaultchoice = 2 | |
| $selected = $host.UI.PromptForChoice($Title , $Info , $Options, $defaultchoice) | |
| $options[$selected] |
Running the code below in PowerShell ISE will produce the following result:

Running the same from the PowerShell console though will not look as fancy:

The reason for the difference is that the underlying PromptForChoice method on the System.Management.Automation.Host.PSHostUserInterface is declared as an abstract method. This basically means that the implementation details are up to the respective PowerShell host (as long as the method complies with the declaration).
As a result your script will not provide a consistent user experience across PowerShell hosts (e.g. ISE, Console). Because of this I wrote a little Windows.Form based helper function that provides the same features as PromptForChoice but will look the same across all PowerShell hosts:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #An alternative to the built-in PromptForChoice providing a consistent UI across different hosts | |
| function Get-Choice { | |
| [CmdletBinding()] | |
| Param | |
| ( | |
| [Parameter(Mandatory=$true,Position=0)] | |
| $Title, | |
| [Parameter(Mandatory=$true,Position=1)] | |
| [String[]] | |
| $Options, | |
| [Parameter(Position=2)] | |
| $DefaultChoice = -1 | |
| ) | |
| if ($DefaultChoice -ne -1 -and ($DefaultChoice -gt $Options.Count -or $DefaultChoice -lt 1)){ | |
| Write-Warning "DefaultChoice needs to be a value between 1 and $($Options.Count) or -1 (for none)" | |
| exit | |
| } | |
| Add-Type -AssemblyName System.Windows.Forms | |
| Add-Type -AssemblyName System.Drawing | |
| [System.Windows.Forms.Application]::EnableVisualStyles() | |
| $script:result = "" | |
| $form = New-Object System.Windows.Forms.Form | |
| $form.FormBorderStyle = [Windows.Forms.FormBorderStyle]::FixedDialog | |
| $form.BackColor = [Drawing.Color]::White | |
| $form.TopMost = $True | |
| $form.Text = $Title | |
| $form.ControlBox = $False | |
| $form.StartPosition = [Windows.Forms.FormStartPosition]::CenterScreen | |
| #calculate width required based on longest option text and form title | |
| $minFormWidth = 100 | |
| $formHeight = 44 | |
| $minButtonWidth = 70 | |
| $buttonHeight = 23 | |
| $buttonY = 12 | |
| $spacing = 10 | |
| $buttonWidth = [Windows.Forms.TextRenderer]::MeasureText((($Options | sort Length)[-1]),$form.Font).Width + 1 | |
| $buttonWidth = [Math]::Max($minButtonWidth, $buttonWidth) | |
| $formWidth = [Windows.Forms.TextRenderer]::MeasureText($Title,$form.Font).Width | |
| $spaceWidth = ($options.Count+1) * $spacing | |
| $formWidth = ($formWidth, $minFormWidth, ($buttonWidth * $Options.Count + $spaceWidth) | Measure-Object -Maximum).Maximum | |
| $form.ClientSize = New-Object System.Drawing.Size($formWidth,$formHeight) | |
| $index = 0 | |
| #create the buttons dynamically based on the options | |
| foreach ($option in $Options){ | |
| Set-Variable "button$index" -Value (New-Object System.Windows.Forms.Button) | |
| $temp = Get-Variable "button$index" -ValueOnly | |
| $temp.Size = New-Object System.Drawing.Size($buttonWidth,$buttonHeight) | |
| $temp.UseVisualStyleBackColor = $True | |
| $temp.Text = $option | |
| $buttonX = ($index + 1) * $spacing + $index * $buttonWidth | |
| $temp.Add_Click({ | |
| $script:result = $this.Text; $form.Close() | |
| }) | |
| $temp.Location = New-Object System.Drawing.Point($buttonX,$buttonY) | |
| $form.Controls.Add($temp) | |
| $index++ | |
| } | |
| $shownString = '$this.Activate();' | |
| if ($DefaultChoice -ne -1){ | |
| $shownString += '(Get-Variable "button$($DefaultChoice-1)" -ValueOnly).Focus()' | |
| } | |
| $shownSB = [ScriptBlock]::Create($shownString) | |
| $form.Add_Shown($shownSB) | |
| [void]$form.ShowDialog() | |
| $result | |
| } |
Using Get-Choice like this:
Get-Choice "Pick Something!" (echo Option1 Option2 Option3) 2
Will look in both ISE and Console like that:

The most notable parts of the function are probably in the loop on lines 46-59. Where the buttons are created dynamically based on the options provided.:
foreach ($option in $Options){ Set-Variable "button$index" -Value (New-Object System.Windows.Forms.Button) $temp = Get-Variable "button$index" -ValueOnly $temp.Size = New-Object System.Drawing.Size($buttonWidth,$buttonHeight) $temp.UseVisualStyleBackColor = $True $temp.Text = $option $buttonX = ($index + 1) * $spacing + $index * $buttonWidth $temp.Add_Click({ $script:result = $this.Text; $form.Close() }) $temp.Location = New-Object System.Drawing.Point($buttonX,$buttonY) $form.Controls.Add($temp) $index++ } Similar to the way it works in PromptForChoice preceding a character from within the option values with an ampersand (e.g. Option &1) will make the button accessible via ALT-key + the letter (e.g. ALT + 1).
The function can also be found in my GitHub repo.

This is a nice interface option however, I would like it to return numeric representations of the choices instead of the verbose text in the button (sometimes my buttons are rather long) and having to type in the entire string is a bit cumbersome. per your example above, it would be nice to define a return value of 1 2 or 3 based on Red, Blue or Green, instead of having to spell out the choices. (i.e. if ($returnvalue = ‘red’) [myfunc1] elseif ($returnvalue=’green’) {myfunc2})
I also agree with a $text option to help explain the prompt. You can include both a prompt and text with PromptfforChoice:
$decision = $Host.UI.PromptForChoice(
“Perform Action?” , “What to you want to do with the found device? You can install and APP, Reboot the device or Leave it alone”,@(‘&Leave it alone’, ‘&Install App’ ,’&Reboot Device ‘), 0)
LikeLike
this is nice, thanks! only thing i would improve would be to add a $prompt block of text below the title bar and above the buttons, to better explain what these choices are for, and to give a consistent look/feel in your script – script name in $title, question/text in $prompt, and buttons in $options. ie: $title = “User Creation Script v2.1” … $prompt = “Which OU will the new user be in?” … “$options = (“IT-Staff”,”Admin-Staff”,”Teachers”,”Students”)
i’ll see if i can add $prompt to the function so that it’ll properly resize the dialog box to fit, but my powershell isn’t that great yet. still learning.
LikeLiked by 1 person
Hi, im new to powershell and not 100% sure how to define what the options do in this script.
LikeLike
Hi Robert,
The function could use some help. Here you go.
The Get-Choice function accepts three parameters:
1. Title: Mandatory. Defines the title of the dialog window.
2. Options: Mandatory. Defines the options a user can choose from, provided as an array of strings.
3. DefaultChoice: Optional. If a number is provided for this parameter it defines the option that is the default choice when the dialog is started (defaults to -1 which means there is no default choice).
An example. If you want to create a dialog that asks the user about a choice between three colors:
$title = ‘Please pick a color’
$options = ‘Red’, ‘Blue’, ‘Green’
#make Blue the default choice
$default = 2
Get-Choice -Title $title -Options $options -DefaultChoice $default
Kind regards,
Dirk
LikeLike
I’ve been trying to use this function in a script I’m working on… I understand that I can pick a specific button, but how do I get it to run a function based on my choice?
LikeLike
The Get-Choice function returns the chosen option as a string. You could use IF constructs to call a function of your choice based on the return value. E.g. .
$title = ‘Please pick a color’
$options = ‘Red’, ‘Blue’, ‘Green’
#make Blue the default choice
$default = 2
$returnValue = Get-Choice -Title $title -Options $options -DefaultChoice $default
if ($returnValue -eq ‘Red’) { myfunc1 }
elseif ($returnValue -eq ‘Green’) { myfunc2 }
LikeLike