PowerShell WinForms: Build Simple GUI Applications

PowerShell WinForms: Build Simple GUI Applications

PowerShell Tips Editor 5 min read
PowerShell WinForms: Build Simple GUI Applications

Your helpdesk team needs a simple tool to reset user passwords or check service status, but they will not open a terminal. Building a PowerShell GUI WinForms application wraps your existing script logic in a window with buttons and text boxes that any user can operate. Windows Forms is built into .NET Framework on every Windows machine — no installations, no frameworks, just PowerShell loading an assembly.

Load Windows Forms Assembly

Before creating any form elements, you must load the Windows Forms assembly into your PowerShell session. Use Add-Type with the assembly name. In older scripts you may see [System.Reflection.Assembly]::LoadWithPartialName — that is deprecated, use Add-Type instead.

# Load Windows Forms (required before using any form controls)
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

# Set visual styles for modern appearance
[System.Windows.Forms.Application]::EnableVisualStyles()

Create a Basic Window

A form is a System.Windows.Forms.Form object. Set its properties before calling ShowDialog() — the method that displays the window and blocks script execution until the form is closed. ShowDialog() returns a DialogResult value indicating how the user closed it.

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

$form = New-Object System.Windows.Forms.Form
$form.Text            = 'Server Tools'
$form.Size            = New-Object System.Drawing.Size(400, 300)
$form.StartPosition   = 'CenterScreen'
$form.FormBorderStyle = 'FixedDialog'
$form.MaximizeBox     = $false

# Show the empty form
$form.ShowDialog() | Out-Null

Add Labels, TextBoxes, and Buttons

Controls are created as objects, their position set with Location, and then added to the form with $form.Controls.Add(). Use System.Drawing.Point for position and System.Drawing.Size for dimensions.

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

$form = New-Object System.Windows.Forms.Form
$form.Text = 'Check Service Status'
$form.Size = New-Object System.Drawing.Size(360, 180)
$form.StartPosition = 'CenterScreen'

# Label
$label = New-Object System.Windows.Forms.Label
$label.Text     = 'Service Name:'
$label.Location = New-Object System.Drawing.Point(10, 20)
$label.Size     = New-Object System.Drawing.Size(100, 20)

# TextBox
$textBox = New-Object System.Windows.Forms.TextBox
$textBox.Location = New-Object System.Drawing.Point(120, 18)
$textBox.Size     = New-Object System.Drawing.Size(200, 20)

# Button
$button = New-Object System.Windows.Forms.Button
$button.Text     = 'Check'
$button.Location = New-Object System.Drawing.Point(120, 55)
$button.Size     = New-Object System.Drawing.Size(80, 26)

$form.Controls.AddRange(@($label, $textBox, $button))

Handle Button Click Events

Register an event handler on the button’s Add_Click event. Inside the handler, reference form controls directly — they are in scope via closure. Use a Label or MessageBox to display results back to the user.

$resultLabel = New-Object System.Windows.Forms.Label
$resultLabel.Location = New-Object System.Drawing.Point(10, 95)
$resultLabel.Size     = New-Object System.Drawing.Size(320, 20)
$form.Controls.Add($resultLabel)

$button.Add_Click({
    $svcName = $textBox.Text.Trim()
    if ($svcName) {
        $svc = Get-Service -Name $svcName -ErrorAction SilentlyContinue
        if ($svc) {
            $resultLabel.Text = "Status: $($svc.Status)"
            $resultLabel.ForeColor = if ($svc.Status -eq 'Running') { 'Green' } else { 'Red' }
        } else {
            $resultLabel.Text = "Service '$svcName' not found."
            $resultLabel.ForeColor = 'DarkRed'
        }
    }
})

$form.ShowDialog() | Out-Null

Input Validation in the Form

Validate user input inside event handlers before running any commands. Use MessageBox.Show() to display validation errors in a user-friendly popup. Return early from the handler if validation fails to prevent partial execution.

$button.Add_Click({
    $input = $textBox.Text.Trim()

    # Validate input is not empty
    if ([string]::IsNullOrWhiteSpace($input)) {
        [System.Windows.Forms.MessageBox]::Show(
            'Please enter a service name.',
            'Validation Error',
            [System.Windows.Forms.MessageBoxButtons]::OK,
            [System.Windows.Forms.MessageBoxIcon]::Warning
        )
        return
    }

    # Validate no special characters
    if ($input -match '[^a-zA-Z0-9_\-]') {
        [System.Windows.Forms.MessageBox]::Show(
            'Service name contains invalid characters.',
            'Validation Error',
            [System.Windows.Forms.MessageBoxButtons]::OK,
            [System.Windows.Forms.MessageBoxIcon]::Warning
        )
        return
    }

    # Proceed with valid input
    $svc = Get-Service -Name $input -ErrorAction SilentlyContinue
    $resultLabel.Text = if ($svc) { "Status: $($svc.Status)" } else { 'Not found' }
})

Show Message Box and File Dialog

For one-off confirmations or file selection, use MessageBox.Show() and OpenFileDialog / SaveFileDialog. These are standard Windows dialogs your users already know.

# Confirmation dialog
$confirm = [System.Windows.Forms.MessageBox]::Show(
    'Are you sure you want to restart the service?',
    'Confirm Action',
    [System.Windows.Forms.MessageBoxButtons]::YesNo,
    [System.Windows.Forms.MessageBoxIcon]::Question
)
if ($confirm -eq 'Yes') { Restart-Service -Name 'Spooler' }

# File picker dialog
$openDialog = New-Object System.Windows.Forms.OpenFileDialog
$openDialog.Filter = 'CSV files (*.csv)|*.csv|All files (*.*)|*.*'
$openDialog.Title  = 'Select input file'

if ($openDialog.ShowDialog() -eq 'OK') {
    $selectedFile = $openDialog.FileName
    Write-Host "Selected: $selectedFile"
}

Common Errors and Fixes

  • Form must call ShowDialog() or Show() to be visible. Creating form objects and setting their properties does nothing until you call ShowDialog() (modal, blocks execution) or Show() (non-modal). Most PowerShell GUIs use ShowDialog().
  • GUI and script run on same thread — event handlers must be fast. WinForms is single-threaded. If your button click handler runs a slow operation (like querying AD or connecting to a remote server), the entire UI freezes. For long operations, use a BackgroundWorker or System.Windows.Forms.Timer to keep the UI responsive.

Related Cmdlets / See Also

Wrapping Up

WinForms turns any PowerShell script into a point-and-click tool accessible to users who would never open a terminal. The pattern is always the same: load the assembly, create the form, add controls, wire up event handlers, call ShowDialog(). Keep event handlers fast and validate all input before acting on it.

Send-Item -To