Home > PowerShell > PowerShell Multi-Line Input Box Dialog, Open File Dialog, Folder Browser Dialog, Input Box, and Message Box

PowerShell Multi-Line Input Box Dialog, Open File Dialog, Folder Browser Dialog, Input Box, and Message Box

Updated May 17, 2013 to fix potential bug and add more parameters to some functions.

Updated Dec 5, 2013 to release COM object from Read-FolderBrowserDialog function.

I love PowerShell, and when prompting users for input I often prefer to use GUI controls rather than have them enter everything into the console, as some things like browsing for files or folders or entering multi-line text aren’t very pleasing to do directly in the PowerShell prompt window.  So I thought I’d share some PowerShell code that I often use for these purposes.  Below I give the code for creating each type of GUI control from a function, an example of calling the function, and a screen shot of what the resulting GUI control looks like.

Show a message box

Function:

# Show message box popup and return the button clicked by the user.
function Read-MessageBoxDialog([string]$Message, [string]$WindowTitle, [System.Windows.Forms.MessageBoxButtons]$Buttons = [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]$Icon = [System.Windows.Forms.MessageBoxIcon]::None)
{
	Add-Type -AssemblyName System.Windows.Forms
	return [System.Windows.Forms.MessageBox]::Show($Message, $WindowTitle, $Buttons, $Icon)
}

Example:

$buttonClicked = Read-MessageBoxDialog -Message "Please press the OK button." -WindowTitle "Message Box Example" -Buttons OKCancel -Icon Exclamation
if ($buttonClicked -eq "OK") { Write-Host "Thanks for pressing OK" }
else { Write-Host "You clicked $buttonClicked" }

Message Box Example

 

Prompt for single-line user input

Function:

# Show input box popup and return the value entered by the user.
function Read-InputBoxDialog([string]$Message, [string]$WindowTitle, [string]$DefaultText)
{
	Add-Type -AssemblyName Microsoft.VisualBasic
	return [Microsoft.VisualBasic.Interaction]::InputBox($Message, $WindowTitle, $DefaultText)
}

Example:

$textEntered = Read-InputBoxDialog -Message "Please enter the word 'Banana'" -WindowTitle "Input Box Example" -DefaultText "Apple"
if ($textEntered -eq $null) { Write-Host "You clicked Cancel" }
elseif ($textEntered -eq "Banana") { Write-Host "Thanks for typing Banana" }
else { Write-Host "You entered $textEntered" }

Input Box Example

 

Prompt for a file (based on a post the Scripting Guy made)

Function:

# Show an Open File Dialog and return the file selected by the user.
function Read-OpenFileDialog([string]$WindowTitle, [string]$InitialDirectory, [string]$Filter = "All files (*.*)|*.*", [switch]$AllowMultiSelect)
{  
	Add-Type -AssemblyName System.Windows.Forms
	$openFileDialog = New-Object System.Windows.Forms.OpenFileDialog
	$openFileDialog.Title = $WindowTitle
	if (![string]::IsNullOrWhiteSpace($InitialDirectory)) { $openFileDialog.InitialDirectory = $InitialDirectory }
	$openFileDialog.Filter = $Filter
	if ($AllowMultiSelect) { $openFileDialog.MultiSelect = $true }
	$openFileDialog.ShowHelp = $true	# Without this line the ShowDialog() function may hang depending on system configuration and running from console vs. ISE.
	$openFileDialog.ShowDialog() > $null
	if ($AllowMultiSelect) { return $openFileDialog.Filenames } else { return $openFileDialog.Filename }
}

Example:

$filePath = Read-OpenFileDialog -WindowTitle "Select Text File Example" -InitialDirectory 'C:\' -Filter "Text files (*.txt)|*.txt"
if (![string]::IsNullOrEmpty($filePath)) { Write-Host "You selected the file: $filePath" }
else { "You did not select a file." }

Select Text File Example

 

Prompt for a directory (based on this post, as using System.Windows.Forms.FolderBrowserDialog may hang depending on system configuration and running from the console vs. PS ISE)

Function:

# Show an Open Folder Dialog and return the directory selected by the user.
function Read-FolderBrowserDialog([string]$Message, [string]$InitialDirectory, [switch]$NoNewFolderButton)
{
    $browseForFolderOptions = 0
    if ($NoNewFolderButton) { $browseForFolderOptions += 512 }

	$app = New-Object -ComObject Shell.Application
	$folder = $app.BrowseForFolder(0, $Message, $browseForFolderOptions, $InitialDirectory)
	if ($folder) { $selectedDirectory = $folder.Self.Path } else { $selectedDirectory = '' }
	[System.Runtime.Interopservices.Marshal]::ReleaseComObject($app) > $null
	return $selectedDirectory
}

Example:

$directoryPath = Read-FolderBrowserDialog -Message "Please select a directory" -InitialDirectory 'C:\' -NoNewFolderButton
if (![string]::IsNullOrEmpty($directoryPath)) { Write-Host "You selected the directory: $directoryPath" }
else { "You did not select a directory." }

Browse For Folder

 

Prompt for multi-line user input (based on code shown in this TechNet article)

Function:

function Read-MultiLineInputBoxDialog([string]$Message, [string]$WindowTitle, [string]$DefaultText)
{
<#
	.SYNOPSIS
	Prompts the user with a multi-line input box and returns the text they enter, or null if they cancelled the prompt.
	
	.DESCRIPTION
	Prompts the user with a multi-line input box and returns the text they enter, or null if they cancelled the prompt.
	
	.PARAMETER Message
	The message to display to the user explaining what text we are asking them to enter.
	
	.PARAMETER WindowTitle
	The text to display on the prompt window's title.
	
	.PARAMETER DefaultText
	The default text to show in the input box.
	
	.EXAMPLE
	$userText = Read-MultiLineInputDialog "Input some text please:" "Get User's Input"
	
	Shows how to create a simple prompt to get mutli-line input from a user.
	
	.EXAMPLE
	# Setup the default multi-line address to fill the input box with.
	$defaultAddress = @'
	John Doe
	123 St.
	Some Town, SK, Canada
	A1B 2C3
	'@
	
	$address = Read-MultiLineInputDialog "Please enter your full address, including name, street, city, and postal code:" "Get User's Address" $defaultAddress
	if ($address -eq $null)
	{
		Write-Error "You pressed the Cancel button on the multi-line input box."
	}
	
	Prompts the user for their address and stores it in a variable, pre-filling the input box with a default multi-line address.
	If the user pressed the Cancel button an error is written to the console.
	
	.EXAMPLE
	$inputText = Read-MultiLineInputDialog -Message "If you have a really long message you can break it apart`nover two lines with the powershell newline character:" -WindowTitle "Window Title" -DefaultText "Default text for the input box."
	
	Shows how to break the second parameter (Message) up onto two lines using the powershell newline character (`n).
	If you break the message up into more than two lines the extra lines will be hidden behind or show ontop of the TextBox.
	
	.NOTES
	Name: Show-MultiLineInputDialog
	Author: Daniel Schroeder (originally based on the code shown at http://technet.microsoft.com/en-us/library/ff730941.aspx)
	Version: 1.0
#>
	Add-Type -AssemblyName System.Drawing
	Add-Type -AssemblyName System.Windows.Forms
	
	# Create the Label.
	$label = New-Object System.Windows.Forms.Label
	$label.Location = New-Object System.Drawing.Size(10,10) 
	$label.Size = New-Object System.Drawing.Size(280,20)
	$label.AutoSize = $true
	$label.Text = $Message
	
	# Create the TextBox used to capture the user's text.
	$textBox = New-Object System.Windows.Forms.TextBox 
	$textBox.Location = New-Object System.Drawing.Size(10,40) 
	$textBox.Size = New-Object System.Drawing.Size(575,200)
	$textBox.AcceptsReturn = $true
	$textBox.AcceptsTab = $false
	$textBox.Multiline = $true
	$textBox.ScrollBars = 'Both'
	$textBox.Text = $DefaultText
	
	# Create the OK button.
	$okButton = New-Object System.Windows.Forms.Button
	$okButton.Location = New-Object System.Drawing.Size(415,250)
	$okButton.Size = New-Object System.Drawing.Size(75,25)
	$okButton.Text = "OK"
	$okButton.Add_Click({ $form.Tag = $textBox.Text; $form.Close() })
	
	# Create the Cancel button.
	$cancelButton = New-Object System.Windows.Forms.Button
	$cancelButton.Location = New-Object System.Drawing.Size(510,250)
	$cancelButton.Size = New-Object System.Drawing.Size(75,25)
	$cancelButton.Text = "Cancel"
	$cancelButton.Add_Click({ $form.Tag = $null; $form.Close() })
	
	# Create the form.
	$form = New-Object System.Windows.Forms.Form 
	$form.Text = $WindowTitle
	$form.Size = New-Object System.Drawing.Size(610,320)
	$form.FormBorderStyle = 'FixedSingle'
	$form.StartPosition = "CenterScreen"
	$form.AutoSizeMode = 'GrowAndShrink'
	$form.Topmost = $True
	$form.AcceptButton = $okButton
	$form.CancelButton = $cancelButton
	$form.ShowInTaskbar = $true
	
	# Add all of the controls to the form.
	$form.Controls.Add($label)
	$form.Controls.Add($textBox)
	$form.Controls.Add($okButton)
	$form.Controls.Add($cancelButton)
	
	# Initialize and show the form.
	$form.Add_Shown({$form.Activate()})
	$form.ShowDialog() > $null	# Trash the text of the button that was clicked.
	
	# Return the text that the user entered.
	return $form.Tag
}

Example:

$multiLineText = Read-MultiLineInputBoxDialog -Message "Please enter some text. It can be multiple lines" -WindowTitle "Multi Line Example" -DefaultText "Enter some text here..."
if ($multiLineText -eq $null) { Write-Host "You clicked Cancel" }
else { Write-Host "You entered the following text: $multiLineText" }

Multi Line Example

 

All of these but the multi-line input box just use existing Windows Forms / Visual Basic controls.

I originally was using the Get verb to prefix the functions, then switched to the Show verb, but after reading through this page, I decided that the Read verb is probably the most appropriate (and it lines up with the Read-Host cmdlet).

Hopefully you find this useful.

Happy coding!

  1. Richard
    July 22nd, 2013 at 04:13 | #1

    Hi,

    Great script.
    One remark though. I am using this in a script of my own (or the intention is there).
    I get the input into a variable which I am going to use as the Body for an email i’m sending.
    However no carriage return are carried over so the message in the email is a single line.
    Any idea.

    RvN

  2. Richard
    July 22nd, 2013 at 05:34 | #2

    never mind.
    ;-)
    fixed

  3. Jeremy
    August 1st, 2013 at 12:57 | #3

    Thanks for an excellent file select function as well as the link back to the scripting guys post. I read their page, but your ready-to-go example worked better for me than theirs.
    Cheers

  4. Derrick B.
    December 13th, 2013 at 12:05 | #4

    Great information!!! How do I tell the input script where to place the information thats being entered?

  5. December 13th, 2013 at 12:57 | #5

    @Derrick B.
    Hey Derrick, I didn’t fully understand your question (not enough info), but I’ve updated the post with an example of using each function. Let me know if you still have questions.

  6. Derrick B.
    December 13th, 2013 at 13:50 | #6

    @deadlydog

    Sorry about that. I’m a newbbie by far. I’ve created a script to add and setup apps within Citrix, but I was looking for something like a popup box to add to the code to answer the questions in the script. Looking at “Prompt for simple user input” looks like it will do the trick. What don’t understand is how to add that to what I have to input the text in the right field. Can you show an example?

  7. SeanD
    April 1st, 2014 at 10:43 | #7

    Not sure how to insert this into my script. I have a script that looks at a text file (pc names), ping them and exports to a csv with the responses. Script work fine.
    I would like to prompt the user fot the text file instead of having the path hard coded.
    I tried pasting your sample variable $filepath in front of my code but it says i did not pick a file.

    $filePath = Read-OpenFileDialog -WindowTitle “Select Text File Example” -InitialDirectory ‘C:\’ -Filter “Text files (*.txt)|*.txt”
    if (![string]::IsNullOrEmpty($filePath)) { Write-Host “You selected the file: $filePath” }
    else { “You did not select a file.” }
    get-content $filePath

    $Output2 = “c:\PSexport\PingOldPCs5.csv”

    Write-host `
    “Pinging List of Dead Computers and Writing to c:\Psexport\PingPC.csv `n WAIT” -foregroundcolor “green”
    $filePath | ForEach-Object {GWMI win32_pingstatus -filter (“address=’”+ $_ +”‘”) -computername .} | Select-Object ProtocolAddress,address,responsetime,statuscode | export-csv $Output2 -NoTypeInformation
    c:\PSexport\PingOldPCs5.csv

  8. April 1st, 2014 at 12:56 | #8

    @SeanD
    You will also need to copy and paste the Read-OpenFileDialog function into your script as well, before calling it.

  9. SeanD
    April 1st, 2014 at 14:36 | #9

    OK. I think i am getting further. FYI – I am not a programmer but an admin learning PS.

    My script pings the list of PCs that are in the text file. I am getting a blank excel sheet where normally if i hard code the text file location For-eachobject does its job and pings.

    It seems like the function is just passing the name of the file

  10. SeanD
    April 2nd, 2014 at 10:18 | #10

    Get-content was the trick. In case anyone wants to know – Here is the complete code that will allow you to pick a file and then read whats in it. Mine here reads it then pings the list

    $filePath = Read-OpenFileDialog -WindowTitle “Select Text File Example” -InitialDirectory ‘C:\’ -Filter “Text files (*.txt)|*.txt”
    if (![string]::IsNullOrEmpty($filePath)) { Write-Host “You selected the file: $filePath” }
    else { “You did not select a file.” }

    $Output2 = “c:\PSexport\PingOldPCs5.csv”

    # Show an Open File Dialog and return the file selected by the user.
    function Read-OpenFileDialog([string]$WindowTitle, [string]$InitialDirectory, [string]$Filter = “All files (*.*)|*.*”, [switch]$AllowMultiSelect)
    {
    Add-Type -AssemblyName System.Windows.Forms
    $openFileDialog = New-Object System.Windows.Forms.OpenFileDialog
    $openFileDialog.Title = $WindowTitle
    if (![string]::IsNullOrWhiteSpace($InitialDirectory)) { $openFileDialog.InitialDirectory = $InitialDirectory }
    $openFileDialog.Filter = $Filter
    if ($AllowMultiSelect) { $openFileDialog.MultiSelect = $true }
    # openFileDialog.ShowHelp = $true # Without this line the ShowDialog() function may hang depending on system configuration and running from console vs. ISE.
    $openFileDialog.ShowDialog() > $null
    if ($AllowMultiSelect) { return $openFileDialog.Filenames } else { return $openFileDialog.Filename }
    } # End Function

    $responses = @()

    ForEach ($path in $filePath) {

    ForEach ($computer in (Get-Content $path)) {

    $responses += GWMI win32_pingstatus -filter (“address=’”+ $computer +”‘”) -computername .
    }

    }

    $responses |
    Select-Object ProtocolAddress,address,responsetime,statuscode | export-csv $Output2 -NoTypeInformation

  11. Matthew
    June 26th, 2014 at 10:35 | #11

    @Richard
    How did you end up getting the carriage returns to work?

  12. Harry
    July 8th, 2014 at 23:37 | #12

    Nice work on the scripts bro! they fit nicely into the one I’m developing at work to automate admin tasks over lots of pc’s

  13. sergueik
    July 27th, 2014 at 13:41 | #13

    Check my article on code project for a big number of relevant examples…

  14. DemoD
    July 30th, 2014 at 11:23 | #14

    When I run this from Powershell ISE the windows that pop up always end up behind the ISE. Is there any way to code and always have them show up in the foreground?

  15. serguei kouzmine
    August 13th, 2014 at 19:19 | #15

    $openFileDialog.Topmost = $True

  16. August 13th, 2014 at 20:00 | #16

    @DemoD
    The only one that does not pop in the foreground for me is the Read-FolderBrowserDialog, and unfortunately I haven’t been able to find a way to force it. Luckily though it does create a new window icon in the start menu bar, so it’s at least a little obvious that a new popup was created and requires attention. If I do find a method that does it, I’ll update the post.

  17. August 13th, 2014 at 20:03 | #17

    @serguei kouzmine
    Topmost is not a valid property on the $openFileDialog object, so your code generates an error. Also, the Read-OpenFileDialog function does not have the problem in my testing, so that code is not even required.

  18. Aymen
    November 23rd, 2014 at 13:53 | #18

    When running the script I get an error message

    Read-MessageBoxDialog: Unable to find type [System.Windows.Forms.MessageBoxButtons]. Make sure that the assembly that contains this type is loaded.

    Any idea how to fix it?

  19. November 24th, 2014 at 14:15 | #19

    @Aymen
    Move the line:

    Add-Type -AssemblyName System.Windows.Forms

    from within the Read-MessageBoxDialog function to just before it, so it looks like this:

    Add-Type -AssemblyName System.Windows.Forms
    function Read-MessageBoxDialog([string]$Message, [string]$WindowTitle, [System.Windows.Forms.MessageBoxButtons]$Buttons = [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]$Icon = [System.Windows.Forms.MessageBoxIcon]::None)
    {
    return [System.Windows.Forms.MessageBox]::Show($Message, $WindowTitle, $Buttons, $Icon)
    }

    I don’t have that problem on my local PC, so I’m guessing it’s a PowerShell 2.0 problem maybe?

  20. Aymen
    November 25th, 2014 at 12:50 | #20

    With this change it worked.

    I have PowerShell 4.0 installed.

    By the way: did you mean in the comments that the help button in the file selection dialog box is obligatory although it does nothing here?

  1. August 29th, 2013 at 07:30 | #1