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.

Get your poster here!

Related links