In this post I use PowerShell to Create a PowerShell WPF Template for a text editor project. You will also learn how to make generic templates for WPF scripts. Here is an example of what the PowerShell script will look like when completed.
When creating a template I like to already have notes built out at the top in a commented out fashion.
<# Title: By: Alan Newingham Date: 11/12/2020 Initial Commit - #>
Just a simple placeholder that looks the same throughout all scripts.
The next line is the WPF assembly references. I include two there are three, I seem to never need the last one.
Add-Type -AssemblyName PresentationCore, PresentationFramework
Next we make a simple array called $Xaml and fill it with nothing (yet).
$Xaml = @" "@
Next, we have to make the Windows.Markup.XamlReader parse the $Xaml array we just created.
$Window = [Windows.Markup.XamlReader]::Parse($Xaml)
The next two lines are all about building the variables based off the Name=””. It says find all “Name” in $Xaml and create a variable with the same name.
[xml]$xml = $Xaml $xml.SelectNodes("//*[@Name]") | ForEach-Object { Set-Variable -Name $_.Name -Value $Window.FindName($_.Name) }
All of the heavy lifting as it pertains to the WPF is completed. I plan on removing the title barn and white strip at the top of the window so we need to build a window that moves and can be drug around. Taking the $Window variable and adding a MouseLeftButtonDown event. Inside this event, we have the DragMove method. According to Microsoft: Allows a window to be dragged by a mouse with its left button down over an exposed area of the window’s client area.
$Window.Add_MouseLeftButtonDown({ $Window.DragMove() })
Next up is the close button. We begin by adding an add.Click event followed by the Close method.
$close_btn.add_Click({ $Window.Close(); })
The next button we need to create is the minimize button. To do this we add a new add.Click event. This time we modify the WindowState, or more succinctly the $Window.WindowState, and set it to minimize.
$minimize_btn.Add_Click({ $Window.WindowState = 'Minimized' })
The last button for my template is the maximize button. To do this we add yet another add.Click event. Inside the event, we do things a bit differently than we had with the other two buttons. That is inside this we use an if statement, so if it is not maxed it maximizes, but if it is maxed it goes back to the original size. How do we do that? Let us take a look.
$maximize_btn.Add_Click({ If ($Window.WindowState -eq 'Normal') { #Maximize the window $Window.WindowState = 'Maximized' } Else { #Put window back to its normal size if maximized already $Window.WindowState = 'Normal' } })
If the windowstate = Normal maximize that puppy. Otherwise set it to normal.
$Window.ShowDialog()
That is it. The template is complete. What does it look like all together? Let’s see.
Title: By: Alan Newingham Date: 11/12/2020 Initial Commit - Add-Type -AssemblyName PresentationCore, PresentationFramework $Xaml = @" "@ #-------------------------------------------------------------# $Window = [Windows.Markup.XamlReader]::Parse($Xaml) #-------------------------------------------------------------# #-------------------------------------------------------------# [xml]$xml = $Xaml $xml.SelectNodes("//*[@Name]") | ForEach-Object { Set-Variable -Name $_.Name -Value $Window.FindName($_.Name) } #-------------------------------------------------------------# #-------------------------------------------------------------# # ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Logic here ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ #-------------------------------------------------------------# #-------------------------------------------------------------# #Click and Drag WPF window without title bar (ChromeTab or whatever it is called) $Window.Add_MouseLeftButtonDown({ $Window.DragMove() }) #-------------------------------------------------------------# #-------------------------------------------------------------# #Custom Close Button $close_btn.add_Click({ $Window.Close(); }) #-------------------------------------------------------------# #-------------------------------------------------------------# #Custom Minimize Button $minimize_btn.Add_Click({ $Window.WindowState = 'Minimized' }) #-------------------------------------------------------------# #-------------------------------------------------------------# #Custom Maximize Button $maximize_btn.Add_Click({ If ($Window.WindowState -eq 'Normal') { #Maximize the window $Window.WindowState = 'Maximized' } Else { #Put window back to its normal size if maximized already $Window.WindowState = 'Normal' } }) #-------------------------------------------------------------# #-------------------------------------------------------------# #Show Window, without this, the script will never initialize the OSD of the WPF elements. $Window.ShowDialog() #-------------------------------------------------------------#
Below is the Xaml we are going to use for this test.
Window 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:TofuApp" Title="MainWindow" Height="450" Width="800" WindowStyle="None" ResizeMode="CanResize" AllowsTransparency="True" WindowStartupLocation="CenterScreen" Background="#FF333333" Foreground="Azure" FontFamily="Century Gothic" FontSize="12"> Window
Above is the beginning portion of the needed code for a WPF to be built. I custom add WindowStyle=”None” to remove the title bar at the top. I allow the user to resize the window. Setting AllowsTransparency=”True” removes the little white line at the top of the WPF after using WindowStyle=”None”. Setting the center of the screen to where the window starts. My signature Gray background with Azure font color and FontFamily=”Century Gothic”.
Next we are going to add the Grids, in this GUI there will only be three. Let’s flesh them out now. I started today with a Grid that encompases the entire window, and then placed the elements inside.
Grid Grid Grid Grid Grid Grid Grid Grid
Next we will make the outermost grid stretch Horizontally and Vertically to always fit the window.
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <Grid> </Grid> <Grid> </Grid> <Grid> </Grid> </Grid>
We now need to build out the bottom bar, for information and whatnot. I set the top grid to 30px high and told it to stretch Horizontally, align to the bottom vertically, and Visual Studio Code Purple fill the grid.
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <Grid Height="30" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Background="#FF68217A"> </Grid> <Grid> </Grid> <Grid> </Grid> </Grid>
For the top grid, we are going to add three buttons and a title. We set it almost identical to the bottom bar however, vertical alignment is set to Top and we fleshed out the StackPanel for the buttons and set the StackPanel to horizontal orientation.
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <Grid Height="30" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Background="#FF68217A"> </Grid> <Grid Height="30" HorizontalAlignment="Stretch" VerticalAlignment="Top" Background="#FF68217A"> <StackPanel Orientation="Horizontal"> </StackPanel> </Grid> <Grid> </Grid> </Grid>
On the last Grid we set it up to take the rest of the space in the window. To do that I set Horizontal and Vertical Alignment to Stretch I then made the top and bottom margins 30 pixels.
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <Grid Height="30" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Background="#FF68217A"> </Grid> <Grid Height="30" HorizontalAlignment="Stretch" VerticalAlignment="Top" Background="#FF68217A"> <StackPanel Orientation="Horizontal"> </StackPanel> </Grid> <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0 30 0 30" > </Grid> </Grid>
The grid is now built. Let us add a “Created By” to the bottom of the window. To do this we make a textblock assign the Text=, Set Horizontal and Vertical alignment to center. This centers inside the Grid it is inside of.
<TextBlock Text="By: iNet" Foreground="Azure" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="12"/>
Let’s build the buttons. They are almost identical. What I do is I get one set up and copy to the rest changing the margins and name and text. Everything else stays the same. At the same time, we will use another TextBlock to add a title to the WPF. We will place the TextBlock seven pixels from the right side of the window.
<Button Name="close_btn" Height="20" Width="20" Background="Transparent" Content="X" FontSize="14" Margin="10,0,0,0" FontWeight="Bold"/> <Button Name="minimize_btn" Height="20" Width="20" Background="Transparent" Content="-" FontSize="14" Margin="2 0 0 0" FontWeight="Bold"/> <Button Name="maximize_btn" Height="20" Width="20" Background="Transparent" Content="#" FontSize="14" Margin="2,0,0,0" FontWeight="Bold"/> <TextBlock Text="Title Of Application" Foreground="Azure" VerticalAlignment="Center" Margin="600,8,0,7"/>
We are one item away from finishing this Xaml! Congratulate yourself.
Lastly we have to create the textbox. The difference is I want the textbox to act more like a notepad note for this example. To do that we need a few variables. AcceptsReturn, AcceptsTab,HorizontalScrollBarVisibility, VerticalScrollBarVisibility, AutoWordSelection, TextWrapping, SpellCheck.IsEnabled.
<TextBox Name="tx_bx" AcceptsReturn="True" AcceptsTab="True" AllowDrop="True" Background="#FF1E1E1E" Foreground="Azure" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" AutoWordSelection="True" TextWrapping="NoWrap" SpellCheck.IsEnabled="True"/>
Now, let’s see what this looks like all together with the PowerShell Logic.
<# Title: By: Alan Newingham Date: 11/12/2020 Initial Commit - #> Add-Type -AssemblyName PresentationCore, PresentationFramework $Xaml = @" <Window 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:TofuApp" Title="MainWindow" Height="450" Width="800" WindowStyle="None" ResizeMode="CanResize" AllowsTransparency="True" WindowStartupLocation="CenterScreen" Background="#FF333333" Foreground="Azure" FontFamily="Century Gothic" FontSize="12"> <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <Grid Height="30" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Background="#FF68217A"> <TextBlock Text="By: Alan Newingham" Foreground="Azure" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="12"/> </Grid> <Grid Height="30" HorizontalAlignment="Stretch" VerticalAlignment="Top" Background="#FF68217A"> <StackPanel Orientation="Horizontal"> <Button Name="close_btn" Height="20" Width="20" Background="Transparent" Content="X" FontSize="14" Margin="10,0,0,0" FontWeight="Bold"/> <Button Name="minimize_btn" Height="20" Width="20" Background="Transparent" Content="-" FontSize="14" Margin="2 0 0 0" FontWeight="Bold"/> <Button Name="maximize_btn" Height="20" Width="20" Background="Transparent" Content="#" FontSize="14" Margin="2,0,0,0" FontWeight="Bold"/> <TextBlock Text="Title Of Application" Foreground="Azure" VerticalAlignment="Center" Margin="600,8,0,7"/> </StackPanel> </Grid> <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0 30 0 30" > <TextBox Name="tx_bx" AcceptsReturn="True" AcceptsTab="True" AllowDrop="True" FontFamily="Consolas" FontStyle="Normal" Background="#FF1E1E1E" Foreground="Azure" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" AutoWordSelection="True" TextWrapping="NoWrap" SpellCheck.IsEnabled="True"/> </Grid> </Grid> </Window> "@ #Parse Xaml $Window = [Windows.Markup.XamlReader]::Parse($Xaml) #Create Variables based off all "Name=" above. [xml]$xml = $Xaml $xml.SelectNodes("//*[@Name]") | ForEach-Object { Set-Variable -Name $_.Name -Value $Window.FindName($_.Name) } #Click and Drag WPF window without title bar (ChromeTab or whatever it is called) $Window.Add_MouseLeftButtonDown({ $Window.DragMove() }) #Custom Close Button $close_btn.add_Click({ $Window.Close(); }) #Custom Minimize Button $minimize_btn.Add_Click({ $Window.WindowState = 'Minimized' }) #Custom Maximize Button $maximize_btn.Add_Click({ If ($Window.WindowState -eq 'Normal') { #Maximize the window $Window.WindowState = 'Maximized' } Else { #Put window back to its normal size if maximized already $Window.WindowState = 'Normal' } }) #Show Window, without this, the script will never initialize the OSD of the WPF elements. $Window.ShowDialog()
Thank you for reading this far!
-iNet