PowerShell XAML GUI

13 Dec 2017
 

Hello! Today I wanted to write a bit about creating GUIs/forms as part of PowerShell scripts. Sometimes having a GUI rather than just a command line interface for scripts can be beneficial, they can often be more intuitive to use and can give the user more of a ‘bird’s eye view’ of the script’s controls than just a succession of questions or script parameters. Plus, who doesn’t want to spend hours adding shiny GUIs to their scripts anyway?

I recently set up a GUI for controlling our scripted build process at work (we will be moving to a CI server soon… hopefully). And, since most of the scripts are written in PowerShell and I don’t know C# I thought it made sense to put the GUI in PowerShell too.

Tools

As Windows PowerShell uses the full .Net framework, it has access to both Windows Forms and WPF. I chose to go down the WPF route as it allows you to write the GUI itself using XAML rather than fiddling with form objects.

There are tools available like Visual Studio that will let you create your XAML GUI using a GUI, which is probably a lot faster and just all around better than writing the XAML by hand… but I decided to use the program Kaxaml (at the time of writing this post their website is down, but you can find the project’s GitHub here). Kaxaml allows you to write XAML and then preview it… it’s free!

Adding the XAML

Once you have written your GUI in XAML you can add the code to your PowerShell script as an XML object using a here-string.

[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
[xml]$xamlCode = @'
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Test GUI" WindowStartupLocation="CenterScreen">
    <Grid Margin="0,5,0,0">
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
        </Grid.RowDefinitions>
            <Label Grid.Row="0" Content="Hello World!!!!!"/>
            <ComboBox Grid.Row="1" Name="testCB"/>
            <Button Grid.Row="2" Name="testBtn" Content="Test"/>
    </Grid>
</Window>
'@

You then need to have PowerShell read the XML object and create the GUI object from it.

$reader = (New-Object System.Xml.XmlNodeReader $xamlCode)
$GUI = [Windows.Markup.XamlReader]::Load($reader)

Connecting functionality

To actually do anything useful with your GUI you’ll want to make its component’s objects easily accessible in PowerShell. A couple of ways to do this are by storing the components you want to use as variables individually, or by parsing your XAML code and storing each of your GUI’s components automatically.

$xamlCode.SelectNodes("//*[@Name]") | ForEach-Object { Set-Variable -Name ($_.Name) -Value $GUI.FindName($_.Name) }

This creates a variable for each component using their names in the XAML. You can then use these variables to set/get each component’s properties.

$testCB.ItemsSource = "Option 1", "Option 2", "Option 3!!"

Or add events.

$testBtn.Add_Click({
    Write-Host $testCB.Text
})

Showing the GUI

Finally, to show your GUI, just call ShowDialog on its object.

$GUI.ShowDialog() | out-null

Tada!

Tada