The need to prompt a user for a choice in a text menu (i.e. in the console), and a slight dissatisfaction with the commonly recommended $Host.ui.PromptForChoice() after experimenting a bit with it, triggered me to ultimately write my own function.
PromptForChoice()
For a start, let’s create a script that makes use of such a dialog and uses the well-known PromptForChoice function:
In this minimalistic version, the only parameter that I define is -Interactive, and even that one is just non-essential demonstration material:
Because in my original code, the function initially worked fully automatic, but sometime down the line, the need arose to let the user make a decision directly – that’s when an interactive mode was added.
Anyways, that’s just some background noise and not really relevant to the actual topic…
Example
Param
(
[Parameter(ParameterSetName="Interactive")]
[switch] $Interactive
)
# --------------------------------------------------------------------------------------------------
# Helper function:
function HostUIPromptForChoice
{
Param
(
[Parameter(Mandatory=$true)] $Caption,
[Parameter(Mandatory=$true)] $Message,
[Parameter(Mandatory=$true)] $Choices,
$Default # Index of the default selection. '-1': No default.
)
[System.Collections.ObjectModel.Collection[System.Management.Automation.Host.ChoiceDescription]] $ChoiceDescriptions = @()
$Choices | % { $ChoiceDescriptions.Add([System.Management.Automation.Host.ChoiceDescription]::New($_[0], $_[1])) }
# [int[]] $MultipleDefaults = 0, 2
# Alternative syntax to allow multiple defaults (e.g. 0 and 2);
# but one's code must be aware of such a possibilty to be able to handle it!
$Host.ui.PromptForChoice($Caption, $Message, $ChoiceDescriptions, $Default)
}
# --------------------------------------------------------------------------------------------------
# Main part:
if ($Interactive)
{
$Caption = "--- Server Selection ---"
$Message = "Connect to which server?"
$Choices = @(
("Server&1", "Logon to server1.example.net"),
("Server&2", "Logon to server2.example.net"),
("Server&3", "Logon to server3.example.net"),
("&TestA" , "Test A: OK: First letter is the shortcut"),
#("&TestB" , "Test B: Fatal error: Not unique (same letter as the shortcut)"),
("TestC" , "Test C: Warning: Without shortcut marker"),
#("TestD&" , "Test D: Fatal error: Shortcut marker at invalid position"),
("&Quit" , "Quit/Abort")
)
$Result = HostUIPromptForChoice -Caption $Caption -Message $Message -Choices $Choices -Default 0
# The result of PromptForChoice is the index (offset, starting at zero) in the $Choices array.
# Problem: If the position or order changes, so must this switch statement!
switch ($Result)
{
"0" { $Server = "server1.example.net" }
"1" { $Server = "server2.example.net" }
"2" { $Server = "server3.example.net" }
"3" { $Server = "TEST" }
default { $Server = "~ No selection ~" }
}
write-host "You've chosen: $Server"
}
OK, so this works and is actually not too bad:
It looked fine to me on first glance, but it has some issues that I don’t particular like; for example:
- Options can only be listed horizontally (on one or more lines, left-to-right), but cannot be displayed vertically (as a list, one below the other). That is only possible if one allows multiple choice selection, but that is not always needed or wanted. And if you have a could of options to chose from, then it can soon appear a bit cramped.
- A few other minor things that I’ll mention below.
Select-saoeChoice
My function, Select-saoeChoice, solves the first issue and switches to a vertical layout. It also makes some checks on the validity of the shortcuts for the menu, plus some more format and layout changes.
Other important difference are these:
$Host.ui.PromptForChoice
returns the index of the “choices” array (e.g. 0 → Entry #1, 1 → Entry #2, etc.).
Select-saoeChoice
returns the actual shortcut, which can be almost any single character (e.g. “A”, “3”).$Host.ui.PromptForChoice
uses also a numerical index for the default value.
Select-saoeChoice
expects the actual shortcut character.
Example
# (... The same as before...)
# --------------------------------------------------------------------------------------------------
# Helper function:
#
# -> Separate function "Select-saoeChoice" in my module; see link blog post.
# --------------------------------------------------------------------------------------------------
# Main part:
if ($Interactive)
{
# (... The same as before...)
# Now calling my function; note the different -Default: It's a character, not an index!
$Result = Select-saoeChoice -Caption $Caption -Message $Message -Choices $Choices -Default 'T'
# No longer using an index/offset, but the shortcut character to match the selection!
switch ($Result)
{
"1" { $Server = "server1.example.net" }
"2" { $Server = "server2.example.net" }
"3" { $Server = "server3.example.net" }
"T" { $Server = "TEST" }
default { $Server = "~ No selection ~" }
}
write-host "You've chosen: $Server"
}
Film & Television (54)
How To (63)
Journal (17)
Miscellaneous (4)
News & Announcements (21)
On Software (12)
Projects (26)