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; 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.

Show a message box:

# Show message box popup and return the button clicked by the user.
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)
}

 

Prompt for simple user input (on a single line):

# 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)
}

 

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

# 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 }
}

 

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):

# 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
}

 

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

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(510,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(415,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
}

 

All of these but the multi-line input box just use existing Windows Forms / Visual Basic controls.  Here is what my custom multi-line input box looks like:

Multi-line input box

 

And here is a quick example of using each of these functions.  For reference I show how to provide all parameters in each call (even ones with default values), but you don’t have to if you don’t need.

$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" }

$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" }

$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." }

$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." }

$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" }

 

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

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