A key advantage of WPF-based user interfaces is that they can be defined using XAML, an XML-based markup language. In the previous tip, we created a simple WPF user interface programmatically:
# load the required .NET assemblies (requires Windows operating system)
Add-Type -AssemblyName PresentationCore
Add-Type -AssemblyName PresentationFramework
# define content of user interface
$WindowTitle = 'Enter User Name'
$OkText = 'Log On'
$CancelText = 'Abort'
$Prompt = 'Please enter user name:'
$DefaultValue = $env:USERDOMAIN + '\' + $env:USERNAME
# create blank window
$form = [System.Windows.Window]::new()
$form.Title = $WindowTitle
$form.SizeToContent = [System.Windows.SizeToContent]::WidthAndHeight
$form.WindowStartupLocation = [System.Windows.WindowStartupLocation]::CenterScreen
# use a grid to position elements
$grid = [System.Windows.Controls.Grid]::new()
# use a uniform 10 units margin
$grid.Margin = 10
# Define rows
$row1 = [System.Windows.Controls.RowDefinition]::new()
$row2 = [System.Windows.Controls.RowDefinition]::new()
$grid.RowDefinitions.Add($row1)
$grid.RowDefinitions.Add($row2)
# Define columns
$col1 = [System.Windows.Controls.ColumnDefinition]::new()
$col2 = [System.Windows.Controls.ColumnDefinition]::new()
$grid.ColumnDefinitions.Add($col1)
$grid.ColumnDefinitions.Add($col2)
# create OK button
$OKButton = [System.Windows.Controls.Button]::new()
$OKButton.Width = 100
$OKButton.Content = $OkText
# this is the official OK button and can be operated via ENTER
$OKButton.add_click({ $form.DialogResult = $true })
# place button in grid cell 1/1
[System.Windows.Controls.Grid]::SetRow($OKButton, 1)
[System.Windows.Controls.Grid]::SetColumn($OKButton, 1)
$null = $grid.Children.Add($OKButton)
# create CANCEL button
$CancelButton = [System.Windows.Controls.Button]::new()
$CancelButton.Width = 100
$CancelButton.Content = $CancelText
# this is the official cancel button and can be operated via ESC
$CancelButton.IsCancel = $true
# place button in grid cell 1/0
[System.Windows.Controls.Grid]::SetRow($CancelButton, 1)
[System.Windows.Controls.Grid]::SetColumn($CancelButton, 0)
$null = $grid.Children.Add($CancelButton)
# create a label
$label = [System.Windows.Controls.Label]::new()
$label.Content = $Prompt
# place label in grid cell 0/0
[System.Windows.Controls.Grid]::SetRow($label, 0)
[System.Windows.Controls.Grid]::SetColumn($label, 0)
$null = $grid.Children.Add($label)
# create textbox to enter a text
$textBox = [System.Windows.Controls.TextBox]::new()
$textBox.Text = $DefaultValue
# use a small margin
$textBox.Margin = 5
# place textbox in grid cell 0/1
[System.Windows.Controls.Grid]::SetRow($textBox, 0)
[System.Windows.Controls.Grid]::SetColumn($textBox, 1)
$null = $grid.Children.Add($textBox)
# add grid to window
$form.Content = $grid
# make it top-most
$form.Topmost = $true
# when the form shows, select the textbox
$null = $textBox.Focus()
# show dialog in a modal dialog window
$result = $form.ShowDialog()
$hasCanceled = $result -ne $true
if ($hasCanceled)
{
throw 'User canceled dialog'
}
# read textbox content
$textBox.Text
The very same user interface can be defined without code, using XAML like this:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
SizeToContent="WidthAndHeight"
WindowStartupLocation="CenterScreen">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button Name="OKButton" Content="Button" Width="100" Grid.Column="1" Grid.Row="1" />
<Button Name="CancelButton" IsCancel="True" Content="Button" Width="100" Grid.Column="0" Grid.Row="1" />
<Label Name="label" Grid.Column="0" Grid.Row="0" />
<TextBox Name="textBox" Margin="5" Grid.Column="1" Grid.Row="0" />
</Grid>
</Window>
As you can see, the XAML defines the same object instances and properties as the earlier code-based approach. The Name property assigns a name to each UI element, allowing your code to reference it.
It is now relatively easy to make adjustments to the XAML, and you can use general-purpose XAML tools—such as the Visual Studio designer—to create and edit the markup.
In PowerShell, you now need a helper function to convert the XAML to real objects:
function Convert-XAMLtoWindow
{
param (
[Parameter(Mandatory = $true)]
[string] $XAML
)
Add-Type -AssemblyName PresentationFramework
$reader = [XML.XMLReader]::Create([IO.StringReader]$XAML)
$result = [Windows.Markup.XAMLReader]::Load($reader)
$reader.Close()
$reader = [XML.XMLReader]::Create([IO.StringReader]$XAML)
while ($reader.Read()) {
$name = $reader.GetAttribute('Name')
if (!$name) { $name = $reader.GetAttribute('x:Name') }
if ($name) {
$result | Add-Member NoteProperty -Name $name -Value $result.FindName($name) -Force
}
}
$reader.Close()
$result
}
Here is the complete script that uses a XAML definition instead of defining the UI by code:
# define interface values
$WindowTitle = 'Enter User Name'
$OkText = 'Log On'
$CancelText = 'Abort'
$Prompt = 'Please enter user name:'
$DefaultValue = $env:USERDOMAIN + '\' + $env:USERNAME
# XAML layout
$xaml = @'
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
SizeToContent="WidthAndHeight"
WindowStartupLocation="CenterScreen">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button Name="OKButton" Content="Button" Width="100" Grid.Column="1" Grid.Row="1" />
<Button Name="CancelButton" IsCancel="True" Content="Button" Width="100" Grid.Column="0" Grid.Row="1" />
<Label Name="label" Grid.Column="0" Grid.Row="0" />
<TextBox Name="textBox" Margin="5" Grid.Column="1" Grid.Row="0" />
</Grid>
</Window>
'@
$window = Convert-XAMLtoWindow -XAML $xaml
# configure window and UI elements
$window.Title = $WindowTitle
$window.label.Content = $Prompt
$window.textBox.Text = $DefaultValue
$window.CancelButton.Content = $CancelText
$window.OKButton.Content = $OkText
$window.OKButton.Add_Click({ $window.DialogResult = $true })
# Show Window
$result = $window.ShowDialog()
# evaluate result
if ($result -ne $true) {
throw 'User canceled dialog'
}
# read textbox content
$window.textBox.Text
Your ultimate PowerShell Cheat Sheet
Unleash the full potential of PowerShell with our handy poster. Whether you're a beginner or a seasoned pro, this cheat sheet is designed to be your go-to resource for the most important and commonly used cmdlets.The poster is available for download and in paper form.
Related links
- ScriptRunners Script libary provides ready-to-use PowerShell scripts.
- Try out ScriptRunner here
- ScriptRunner: Book a demo with our product experts