In Part 1 of the series, we saw how to install Visual Studio Community Edition and build some XAML code inside and add some controls. Now let’s see how to use this code in PowerShell
First of all – don’t judge me, I’m an ISE user. I know – VC Code is soooo much better, but I’m just so used to ISE (even more unforgivable – I use the light theme)
What you need to know about XAML Scripts is that the script just loads the form. Any functions afterwards must be started using the buttons. So we start with a region XAML
#region XAML
#Form Start
Add-Type -AssemblyName PresentationFramework, System.Drawing, System.Windows.Forms, WindowsFormsIntegration
[xml]$XAML= @'
This is where we put the XAML
'@ -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window' -replace 'x:Class="\S+"',''
#Read XAML
$reader=(New-Object System.Xml.XmlNodeReader $XAML)
$Form=[Windows.Markup.XamlReader]::Load($reader)
$XAML.SelectNodes("//*[@Name]") | %{Set-Variable -Name ($_.Name) -Value $Form.FindName($_.Name)}
#endregion
We can test if the form is loading properly, by just starting it with
$Form.ShowDialog()
If all goes well, our form will load just fine:

#region XAML
#Form Start
Add-Type -AssemblyName PresentationFramework, System.Drawing, System.Windows.Forms, WindowsFormsIntegration
[xml]$XAML= @'
<Window x:Class="Demo_App_1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Demo_App_1"
mc:Ignorable="d"
Title="Demo App" Height="335" Width="331">
<Grid Background="#FFFBFBFB">
<CheckBox x:Name="chbDemo1" Content="Demo Option 1" HorizontalAlignment="Left" Margin="38,55,0,0" VerticalAlignment="Top" IsChecked="{Binding ElementName=chbDemo2, Path=IsChecked}"/>
<CheckBox x:Name="chbDemo2" Content="Demo Option 2" HorizontalAlignment="Left" Margin="38,84,0,0" VerticalAlignment="Top"/>
<Button x:Name="button1" Content="Start" HorizontalAlignment="Left" Margin="38,176,0,0" VerticalAlignment="Top" Height="44" Width="102" Background="#FFE2D5ED"/>
<ComboBox x:Name="cmbBox1" HorizontalAlignment="Left" Margin="38,126,0,0" VerticalAlignment="Top" Width="120">
<ComboBoxItem Content="Option 1" IsSelected="True"/>
<ComboBoxItem Content="Option 2"/>
</ComboBox>
<TextBox x:Name="txbStatus" HorizontalAlignment="Left" Margin="38,248,0,0" TextWrapping="Wrap" Text="Script Not Started" VerticalAlignment="Top" Width="214"/>
</Grid>
</Window>
'@ -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window' -replace 'x:Class="\S+"',''
#Read XAML
$reader=(New-Object System.Xml.XmlNodeReader $XAML)
$Form=[Windows.Markup.XamlReader]::Load($reader)
$XAML.SelectNodes("//*[@Name]") | %{Set-Variable -Name ($_.Name) -Value $Form.FindName($_.Name)}
#endregion
$Form.ShowDialog()
Then we go on with the XAML Controls. The order is important. We should define all Controls, before loading the form
For the simple purpose of the Demo, we’ll just add some basic controls but these can be expanded a lot.
We have added 3 controls:
- Checkbox 1 checked action
- Checkbox 1 unchecked action
- Button 1 click action, which starts the “Main” function
The whole structure looks like this
#region Functions
Function Main{
$txbStatus.Text="Script Started"
$txbStatus.Background="#FFA5E8B2"
}
#endregion
#region XAML
#Form Start
Add-Type -AssemblyName PresentationFramework, System.Drawing, System.Windows.Forms, WindowsFormsIntegration
[xml]$XAML= @'
<Window x:Class="Demo_App_1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Demo_App_1"
mc:Ignorable="d"
Title="Demo App" Height="335" Width="331">
<Grid Background="#FFFBFBFB">
<CheckBox x:Name="chbDemo1" Content="Demo Option 1" HorizontalAlignment="Left" Margin="38,55,0,0" VerticalAlignment="Top" IsChecked="{Binding ElementName=chbDemo2, Path=IsChecked}"/>
<CheckBox x:Name="chbDemo2" Content="Demo Option 2" HorizontalAlignment="Left" Margin="38,84,0,0" VerticalAlignment="Top"/>
<Button x:Name="button1" Content="Start" HorizontalAlignment="Left" Margin="38,176,0,0" VerticalAlignment="Top" Height="44" Width="102" Background="#FFE2D5ED"/>
<ComboBox x:Name="cmbBox1" HorizontalAlignment="Left" Margin="38,126,0,0" VerticalAlignment="Top" Width="120">
<ComboBoxItem Content="Option 1" IsSelected="True"/>
<ComboBoxItem Content="Option 2"/>
</ComboBox>
<TextBox x:Name="txbStatus" HorizontalAlignment="Left" Margin="38,248,0,0" TextWrapping="Wrap" Text="Script Not Started" VerticalAlignment="Top" Width="214"/>
</Grid>
</Window>
'@ -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window' -replace 'x:Class="\S+"',''
#Read XAML
$reader=(New-Object System.Xml.XmlNodeReader $XAML)
$Form=[Windows.Markup.XamlReader]::Load($reader)
$XAML.SelectNodes("//*[@Name]") | %{Set-Variable -Name ($_.Name) -Value $Form.FindName($_.Name)}
#endregion
#region XAML Controlls
$chbDemo1.add_Checked({
$txbStatus.Background="#FFFFFFFF"
$txbStatus.Text="Checkbox 1 is Checked"
$cmbBox1.SelectedItem=$cmbBox1.Items[1]
})
$chbDemo1.add_UnChecked({
$txbStatus.Background="#FFFFFFFF"
$txbStatus.Text="Checkbox 1 is Unchecked"
$cmbBox1.SelectedItem=$cmbBox1.Items[0]
})
$button1.Add_Click({
Main
})
#endregion
$Form.ShowDialog()
Now when we click on the Checkbox 1, we see the Textbox reflecting the change

as well as if we unselect it

The third control reflects the action if we click on the “Start” button. Note that this control is actually triggered by the Main function, which is how you can initiate different portions of the script.

Before proceeding to Part 3, I’ll just add a bonus. If you don’t like the small PowerShell logo on top, you can change it with a custom logo of yours, by converting the image into B64 format
You can select an Icon, which you want to convert, so let’s take the ConfigRoar Logo 🙂

You can convert it to Base 64 like this:
$path="Path to my file.png"
[convert]::ToBase64String((get-content $path -encoding byte)) | Out-File "Path to Out File\B64Image.txt"
Then we add the following in the controls (Replace the B64 string with yours)
#region XAML Controlls
[string]$IconB64=@"
Base 64 String from the Out File
"@
$bitmap=New-Object System.Windows.Media.Imaging.BitmapImage
$bitmap.BeginInit()
$bitmap.StreamSource=[System.IO.MemoryStream][System.Convert]::FromBase64String($IconB64)
$bitmap.EndInit()
$bitmap.Freeze()
$Form.Icon=$bitmap
So let’s put all together 🙂
#region Functions
Function Main{
$txbStatus.Text="Script Started"
$txbStatus.Background="#FFA5E8B2"
}
#endregion
#region XAML
#Form Start
Add-Type -AssemblyName PresentationFramework, System.Drawing, System.Windows.Forms, WindowsFormsIntegration
[xml]$XAML= @'
<Window x:Class="Demo_App_1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Demo_App_1"
mc:Ignorable="d"
Title="Demo App" Height="335" Width="331">
<Grid Background="#FFFBFBFB">
<CheckBox x:Name="chbDemo1" Content="Demo Option 1" HorizontalAlignment="Left" Margin="38,55,0,0" VerticalAlignment="Top" IsChecked="{Binding ElementName=chbDemo2, Path=IsChecked}"/>
<CheckBox x:Name="chbDemo2" Content="Demo Option 2" HorizontalAlignment="Left" Margin="38,84,0,0" VerticalAlignment="Top"/>
<Button x:Name="button1" Content="Start" HorizontalAlignment="Left" Margin="38,176,0,0" VerticalAlignment="Top" Height="44" Width="102" Background="#FFE2D5ED"/>
<ComboBox x:Name="cmbBox1" HorizontalAlignment="Left" Margin="38,126,0,0" VerticalAlignment="Top" Width="120">
<ComboBoxItem Content="Option 1" IsSelected="True"/>
<ComboBoxItem Content="Option 2"/>
</ComboBox>
<TextBox x:Name="txbStatus" HorizontalAlignment="Left" Margin="38,248,0,0" TextWrapping="Wrap" Text="Script Not Started" VerticalAlignment="Top" Width="214"/>
</Grid>
</Window>
'@ -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window' -replace 'x:Class="\S+"',''
#Read XAML
$reader=(New-Object System.Xml.XmlNodeReader $XAML)
$Form=[Windows.Markup.XamlReader]::Load($reader)
$XAML.SelectNodes("//*[@Name]") | %{Set-Variable -Name ($_.Name) -Value $Form.FindName($_.Name)}
#endregion
#region XAML Controlls
[string]$IconB64=@"
iVBORw0KGgoAAAANSUhEUgAAACkAAAApCAYAAACoYAD2AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQ
AAAAhdEVYdENyZWF0aW9uIFRpbWUAMjAyMjowNzoyMSAxMDozNzo0MS3UbCQAAAORSURBVFhH7ZdNTxNBGMcfkJLWFmQrUt6E8BIxARKiKJjIiY
O9mHhUT8QzB4/c9QN48TP4AbwQE71wQWMIxhrFFAiGIhTSIrQUWwTnP7tTtrvT7W5ZBJP+ks3M7MLuf563eVp1xKBzTrU2nmsqIt2iItIt/guRr
pWgbO6QtnezfN4U9PLRLU4kMpU5oO8rO7S6uUfpzB/troqnppram3zU1Rqg0AlFly1y7luCFn7saitrIHakv5FqPeVFl2ORcOu7j+uU3M3xtaem
iom4yC1Wy6znYULSzMJw/QKzcnpftTAsOz4cIqW+lq+dYFskXIsPRxa38wKvddTRYE+DpYWWYimaW0hQ7uAoL9Tvq+Hvshu7JUWuxve4ReLJ39o
dlZH+y9TdFtBW1iR3svSWWR9CjSh1HrrWWU/dzBPFsBT5mVktsvhLW6nAvTf6grYFCiB0evantjLT1eqn0YFGbVVIUZF6gQ1st3ArYu8kwCu48B
64PndwyMMhtpnhzxE+N68H+VyPVORGYp8lxwafW+3QLWYjW7S8lubz+2NtFGAxq0ca8ctrKT76vRe4a08bfANhBGLM0kaKiFR3NVAic90C31Dq1
NKUZSFgxKQAAS4Q/3jWmETqd1JO4S0H1GBR4vzewngEJpF66yGB/gU4oQSy8DLdwR8hYQDKhRNSrMl4+nKRHj77StGYWlbsgAbE6ptm2YwurVAj
gfQxaoUQOB9N03oix+ZLjoQiSYFIWj1SkX0d9bwkoNjOfIqXFCoERmPH4aHesy9Uf44bw0wqEi4fH27mc/SJOM5QcGUxKhMocCLUWMD1SEUCZHZ
4tCUfK3ADTiFkoh69wKlH7XwEkw9aKOCtti0ULWAxiooEXOidVnYiKPkTAR2RYD6aKhAYvn18OvW2+ejFZE9e6PSHpPZEjj5hjPXZUiSA6/tYK4
ULwKJi10O9AZq4F6LnTzoLBAqE0PAthT1XtLtm8L7I0jafo1cwlqGSIgXovAGS6f2XLT4HE+EQ3R28pK3MQOjU46t8lCE6ffEbCUlrxLZIBDbcD
lbjGd7KlYOI6ThLQrxjenYt3+nj/bJTzrZIAJfDHcBu/dSD6vB6Jkav3qywTn2D96uwIOIdnb4IKSOORAL0lmNDV4r2mM2Kh48Bn1oVrIA4bBrJ
adXp2/4hZhdkMq7moLw5gbtxViODZee0DNdFngaO3X0WVES6RUWkW1REugPRX3NCtksrrYhIAAAAAElFTkSuQmCC
"@
$bitmap=New-Object System.Windows.Media.Imaging.BitmapImage
$bitmap.BeginInit()
$bitmap.StreamSource=[System.IO.MemoryStream][System.Convert]::FromBase64String($IconB64)
$bitmap.EndInit()
$bitmap.Freeze()
$Form.Icon=$bitmap
$chbDemo1.add_Checked({
$txbStatus.Background="#FFFFFFFF"
$txbStatus.Text="Checkbox 1 is Checked"
$cmbBox1.SelectedItem=$cmbBox1.Items[1]
})
$chbDemo1.add_UnChecked({
$txbStatus.Background="#FFFFFFFF"
$txbStatus.Text="Checkbox 1 is Unchecked"
$cmbBox1.SelectedItem=$cmbBox1.Items[0]
})
$button1.Add_Click({
Main
})
#endregion
$Form.ShowDialog()
And voila – Now we have a Script with GUI

In part 3 we’ll take a look at some of the differences and principles we need to be aware of, when writing PowerShell with XAML
So join me in Part 3 🙂