PowerShell and WPF: Introduction and Building Your First Window

Building a user interface (UI) in PowerShell may sound like a difficult process and you may not even know where to begin to accomplish something like this. Luckily, there are a few choices on what you can use to create a simple or complex UI that can be handed off to an individual to use with little to no effort. Whether you use WinForms of WPF as your building block for your UI, you still have more options with each to help build a nice UI. For the sake of this series of articles, I am going to be focusing on WPF and will show some of the controls that you can use to build a nice application for others to use.

With WPF, you have options on what you want to use to build out the interface. ShowUI, XAML and .Net are the most common ways to accomplish this. Each has its own strengths to use, but I personally usually stick with using XAML and sometimes use .Net to build dynamic controls on the fly if needed. But whatever path you choose is completely up to you as either of these ways will get you to your goal.

My goal with this is to start from the beginning and try to cover some basics of working with WPF and PowerShell over time. This is by no means a comprehensive all in one WPF guide. That would take up too much time and there are many things that I still do not know at this time. With that, this means that am I an expert at this (I’m an ITPro, not Dev Smile) but I have been able to build a few UI’s (PoshPAIG, PoshChat, Profile Removal, etc…) and want to share my knowledge with others and also provide a reference for myself in the process!

XAML or .Net?

Both actually! I won’t actually show both ways to accomplish the same task, but I will start out with the XAML approach and make updates to the existing object using PowerShell/.Net.

Loading the XAML With PowerShell

In every initial example, you will see me take the XAML code and plug it into PowerShell to display. In order to do this, I need to cast the data as xml using [xml] and then loading up the xml into PowerShell using System.Xml.XmlNodeReader and [Windows.Markup.XamlReader]::Load.

#Build the GUI
[xml]$xaml = @"
<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Name="Window" Title="Initial Window" WindowStartupLocation = "CenterScreen" 
    Width = "800" Height = "600" ShowInTaskbar = "True">
</Window>
"@ 

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

The Initial Window

This really is the starting point for most of your WPF UIs. The Window itself. I will be using the same code as above to demonstrate the initial Window. After which, I will rely on using Powershell to adjust or add various things to the existing object. You can find a plethora of information here on the msdn page.

Because this is going to be a generic Window with nothing spectacular, I will make the Width 800 and the Height 600 while starting the window up in the Center screen by setting WindowStartupLocation to ‘CenterScreen’. I also chose the name the Window as ‘Windows’ which is done in XAML by specifying x:Name = ‘Window’. And yes, when you are working in XAML, the properties are Case Sensitive! After loading up the code into PowerShell as an object, you can start the Window up by using the ShowDialog() method. Do not use Show() unless you want to lock up your console or ISE!

So lets go ahead and check out this exciting Window!

$Window.ShowDialog()

image

As you can see, nothing that groundbreaking. The window did come up in the center screen along with the size being what I set. The title also shows up correctly. You can close the window simply by clicking on the red X. Also, when you close the window, you will see $False appear on the console.

Properties You May Find Useful

Now that we have the window up and running, we can look at few of the properties that you may wish to use to make the window look a little nicer. In case you are wondering why I said a few, it is because there are a ton of properties and many of those will go beyond the scope of what I plan to do here.

ToolTip

A tooltip provides a message to the user simply by hovering the mouse cursor over the object, in this case, the window.

$window.ToolTip = "This is a window. Cool, isn't it?"

image

Transparency

You can set the Window to be transparent if you’d like. A word of caution is that you will lose your ability to close the Window with the red X. You will need to configure an alternate means of closing the window (see the event for Click To Close later in this article). You will need to set the 3 items posted in the source code below. The Opacity must be a value between 0 and 1.0 in order to work properly

$Window.AllowsTransparency = $True
$Window.Opacity = .5
$window.WindowStyle = 'None'

image

As you can see, it is transparent with no visible way to close it!

Background Color

Setting the background color of your window is simple! Just use the Backgroundcolor property and give it a color!

$Window.Background = 'Green'

image

Adding Text To Your Window

Besides adding a background color, you can add text to your Window via the Content property. Besides that, you can customize your text using a variety of methods listed below in the code.

$Window.FontSize = 24
$Window.FontStyle = 'Italic' #"Normal", "Italic", or "Oblique
$Window.FontWeight = 'Bold' #http://msdn.microsoft.com/en-us/library/system.windows.fontweights
$Window.Foreground = 'Red'
$Window.Content = "This is a test!"

image

Events You May Find Useful

Events are designed to fire off when a certain action occurs. Whether it is a timed event that fires on a tick, or a mouse click or even if the Window is closing or opening. You can supply a script block to the action block of the event to perform whatever task it is you need done with the event. Be sure to include all of the events prior to the code that is opening the Window, otherwise you might be left wondering what is going on.

Click To Close

As seen in my demo of  a transparent window, there will be a time when you might need an alternate way to close the window. Luckily, that can be done with your mouse just by clicking on the window with either your left or right mouse button and using the $Window object’s Close() method. In this case, I am going to close the Window using the Right mouse button.

$Window.Add_MouseRightButtonDown({
    $Window.close()
})

Click To Drag

Same issues with using transparency means that you have no real way to move your window around. We get around that by once again using the MouseRight/LeftButton event and then using the DragMove() method.

$Window.Add_MouseRightButtonDown({
    $Window.DragMove()
})

Actions To Perform On Close

When your window closes, you may need to perform some additional tasks such as saving to a config file, cleanup, etc… Luckily we can use the Closed event with whatever actions that need to be taken to accomplish this.

$Window.Add_Closed({
    Write-Verbose "Performing cleanup"
})

Actions To Perform On Open

When your Window first opens up, you may need to perform some initializations before presenting everything to the user. For this approach, I typically use the OnLoaded event.

$Window.Add_Loaded({
    Write-Verbose "Starting up application"
})

Timer Event

Sometimes you need a timer to handle certain actions on each tick. The approach I usually take is to initialize my timer when the window is loaded and then supply the code that that will run on each tick of the timer.

$Window.Add_Loaded({
    $timer.Add_Tick({
        [Windows.Input.InputEventHandler]{ $Global:Window.UpdateLayout() }
        #Write-Verbose "Updating Window"
    })
    #Start timer
    Write-Verbose "Starting Timer"
    $timer.Start()
})

Wrapping Up

That concludes the initial article on WPF and building your first Window. Like I said at the beginning, this is just 1 step on a multi-step process to really create something cool. If you have anything that you would like to see about a specific control that I talk about, feel free to leave something in the comments. If there is something that you would like to see me tackle, let me know and I will see what I can do and hopefully we will learn something new together!

Next WPF Topic

Buttons

This entry was posted in powershell, WPF and tagged , , , . Bookmark the permalink.

23 Responses to PowerShell and WPF: Introduction and Building Your First Window

  1. Tom says:

    Hi.I am total newbby in this. I used to start learning how to make hta files when i found out that that way is dropped and no one is doing anything this way any more. Can anyone explain me how it work. I mean what is the extension of file i put code into?

  2. jim beam says:

    Does this have to be done in a powershell x86 window? I have created wpf apps in the past using powershell x86, today i copied your initial window code into powershellise x64 and keep getting object reference not set to an instance errors when trying to showdialog

  3. Ryan Strope says:

    Any chance you have an idea how to handle a XAML that has frames with Page elements?

    So If i have this

    MainWindow.xaml:

    and this

    Deployment Settings.xaml:

    <Page x:Class="SCCM_Tools.Deployment_Settings"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      mc:Ignorable="d" 
      d:DesignHeight="300"
    Title="Deployment Settings" Width="494" >
    <Page.Resources>
        <Style TargetType="CheckBox" x:Key="ShowHideChekboxStyle">
            <Style.Triggers>
                <DataTrigger Binding="{Binding ElementName=PurposeComboBox, Path=SelectedIndex}" Value="0">
                    <Setter Property="Visibility" Value="Hidden"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding ElementName=PurposeComboBox, Path=SelectedIndex}" Value="1">
                    <Setter Property="Visibility" Value="Visible"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
        <Style TargetType="ComboBox" x:Key="ComboBoxStyle">
            <Setter Property="Height" Value="20"/>
            <Setter Property="SelectedIndex" Value="0"/>
            <Setter Property="IsEnabled" Value="True"/>
        </Style>
        <Style BasedOn="{StaticResource ComboBoxStyle}" x:Key="PurposeComboBoxStyle" TargetType="ComboBox">
            <Style.Triggers>
                <DataTrigger Binding="{Binding ElementName=ActionComboBox, Path=SelectedIndex}" Value="1">
                    <Setter Property="SelectedIndex" Value="1"/>
                    <Setter Property="IsEnabled" Value="False" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Page.Resources>
    <Grid>
        <Label Content="Specify settings to control how this software is deployed" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" FontSize="16" >
            <Label.Foreground>
                <SolidColorBrush Color="{DynamicResource TitleBlue}"/>
            </Label.Foreground>
        </Label>
        <Grid Margin="0,86,0,127">
            <Label Content="Action:" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" />
            <ComboBox  Style="{StaticResource ComboBoxStyle}" Name="ActionComboBox" Margin="107,10,112,51" >
                <ComboBoxItem Name="InstallComboBoxItem">Install</ComboBoxItem>
                <ComboBoxItem Name="UninstallComboBoxItem">Uninstall</ComboBoxItem>
            </ComboBox>
            <Label Content="Purpose:" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10,49,0,0"/>
            <ComboBox Style="{StaticResource PurposeComboBoxStyle}" Name="PurposeComboBox" Margin="107,49,112,12" >
                <ComboBoxItem Name="AvailableComboBoxItem">Available</ComboBoxItem>
                <ComboBoxItem Name="RequiredComboBoxItem">Required</ComboBoxItem>
            </ComboBox>            
        </Grid>
        <Grid Margin="0,178,0,10">
            <CheckBox Style="{StaticResource ShowHideChekboxStyle}" Name="PreDeployCheckBox" Content="Pre-Deploy spftware to the user's primary device" Margin="0,0,0,79"/>
            <CheckBox Style="{StaticResource ShowHideChekboxStyle}" Name="WOLCheckBox" Content="Send wake-up packets" Margin="0,38,0,41"/>
            <CheckBox Style="{StaticResource ShowHideChekboxStyle}" Name="DownloadOverMeteredCheckBox" Margin="0,76,0,0">
                <TextBlock>
                    <AccessText TextWrapping="Wrap" Text="Allow clients on a metered Internet connection to download content after the installation deadline, which might incur additional costs" Width="417"/>
                </TextBlock>
            </CheckBox>
        </Grid>
    </Grid>
    

    How do I load MainWindow.xaml and have it load ALL the XAML including the Pages

    Thanks!

  4. Daniel Petcher says:

    I love the pure PowerShell approach. I don’t want to learn techniques that will make me dependent on a third-party codebase that might become obsolete or unsupported ever again. (PowerGUI, PrimalScript community edition, etc.)

    I’m wondering what that last bit with the timer is supposed to do, though. The code as-written does not seem to work in PowerShell v5:

    PS> HelloWorld.ps1
    You cannot call a method on a null-valued expression.
    At C:\Users\me\HelloWorld.ps1:26 char:5
    + $timer.Add_Tick({
    + ~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    VERBOSE: Starting Timer
    You cannot call a method on a null-valued expression.
    At C:\Users\me\HelloWorld.ps1:32 char:5
    + $timer.Start()
    + ~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    VERBOSE: Performing cleanup
    False

    • Boe Prox says:

      Interesting. I’ll have to run the code and see what might be going on with it.

    • jim beam says:

      Also getting this error in powershell v5 as well
      Cant call a method on null valued expression

    • PJKwon says:

      Missing line before 02. plz insert 01 line

      01] $timer = New-Object System.Windows.Threading.DispatcherTimer
      02] $timer.Add_Tick({
      [Windows.Input.InputEventHandler] {$Global:Window.UpdateLayout()}
      Write-Host “Updating Window”
      })

  5. Peter Kriegel says:

    Hi Boe!

    I like to introduce my Asynchronous WPF GUI work to you.

    My example show: How to create a non blocking Graphical User Interface (GUI) with Windows Presentation Foundation (WPF) and how to write Data with PowerShell to GUI controls correctly from a different thread (Runspace) over the Dispatcher.

    Take a Look at: http://poshcode.org/6321

    Greets
    Peter Kriegel

  6. Pingback: Getting started building GUIs for your PowerShell scripts - 4sysops

  7. Leofox says:

    Hi, This tutorial was my base knowledge when making powershell + wpf application, thanks for this post it gives me a place to start learning it. Right now I am in the middle of creating an application (ps + wpf). I wanna turn my form into Metro theme style which i did when i was using singe xaml page. But how to do it when using resource dictionary? (External xaml source file) Here is my code:

    #build the GUI
    Clear-host
    cd “D:\powershell\Lesson\metro”
    Add-Type –assemblyName PresentationFramework
    Add-Type –assemblyName PresentationCore
    Add-Type –assemblyName WindowsBase
    [xml]$xaml = @”

    <Window.Resources>
        <ResourceDictionary>
    
            <ResourceDictionary.MergedDictionaries>
    
                        <ResourceDictionary Source="Metro.MSControls.Core.Implicit.xaml" />
                        <ResourceDictionary Source="Metro.MSControls.SDK.Implicit.xaml" />
                        <ResourceDictionary Source="Metro.MSControls.Toolkit.Implicit.xaml" />
            </ResourceDictionary.MergedDictionaries>
    
        </ResourceDictionary>
    </Window.Resources>
    

    “@

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

    $Window.ShowDialog()

    ******i hope someone will email me Lumus_maxima@rocketmail.com

  8. KERR says:

    “Unable to find type [Windows.Markup.XamlReader]” – Any thoughts?

    • Kreloc says:

      Launch Powershell -sta
      Add [void][System.Reflection.Assembly]::LoadWithPartialName(‘presentationframework’) before creating the window. I was getting the same error until I performed those steps.

  9. Stefan says:

    Hey Guys, I found an even easier way to load a XML-String:

    $window = [Windows.Markup.XamlReader]::Parse( ([string] @”xamlcontent”@) )

    Saves another few lines …

  10. Gernot says:

    I have a third party application using xaml do open dialog windows. The dialofg windows require to be closed by ok or cancel buttons and can be customized to a large extent eg import xaml code.
    BUT i need to close these windows upon external events e.g. a time point.
    Can I use Power shell to close such a window :

    based on the window tile?
    Thanks, Gernot

  11. Pingback: Episode 202 – Antoine Habert talks about PoshBoard « PowerScripting Podcast

  12. Great post Boe. Just this first XAML post has convinced me to take another look at using XAML for some future projects instead of WinForms. Looking forward to see more in this series. When you started learning XAML, did you use a GUI-building app similar to PowerShell Studio for WinForms?

    • Boe Prox says:

      Thanks, Rich! I just starting looking at some other sites on working with XAML or that had examples that I could reference and just started to put things together. I know that you can use Visual Studio where you can build out the UI and then copy the XAML code (with a couple modifications) and start using that in your PowerShell code.

  13. Peter Bishop says:

    Boe,
    Another great post. I too had tried the ShowUI after watching a James Brundage webcast.
    The documentation was a little lite – but your explanations are helping to fill in the gaps.
    Keep em coming please.
    Peter

  14. Gyz says:

    Thanks for this great article. I already tried ShowUI but find it too difficult to start with. Your tips gave me a good start even for someone with no experience. Could you maybe show how to create a File menu or how to add controls like buttons,radio or check boxes. Thanks in advance.

    • Boe Prox says:

      Hi Gyz. Glad you enjoyed the article! Buttons are next on my list and I will be doing radio and check boxes at some point in the future as well. I will also touch on Menus and will show what I did for a File menu in one of my projects.

Leave a Reply to Kreloc Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s