I’ve had several people ask me how PowerBoots compares to PrimalForms, or ask for a visual designer for PowerBoots. I usually answer something along the lines of the fact that Microsoft has already created a very good WPF/XAML designer in Visual Studio (including the free Express editions), particularly in 2010, so I don’t see why I should duplicate their efforts. However, up until now I haven’t written or published any sort of walk through about how to make that work …
So crank up your Visual Studio Express Edition and make yourself some user interfaces!
Basically, you just create a WPF project in C# or VB.Net or whatever … I chose to name my project “XamlForBoots” — your project will start off with an empty MainWindow.xaml file which will look something like this:
After you drag a few more controls onto the form, and you’ve created a complete user interface, you should delete the x:Class attribute. You need to make sure you know the name of the controls you want to interact with, so you might end up with something like this (I used the property pane, which you can’t see in this shot, to name each textbox and the button):
At this point, we’re ready to drop into PowerShell and write some script. Now … there is one catch here. The first script I’m going to show you here is for PowerBoots 0.3 (which will be the first release candidate for a gold 1.0 release, and will be out soon™). However, I’ll post below some code to make it work on the current release, but it requires an external function.
So, in the next release, you can just do something like this:
Register-BootsEvent -InputObject $Calculate -EventName Click -Action {
$Total.Text = '${0:n2}' -f (($Miles.Text -as [Double]) / ($Mpg.Text -as [Double]) * ($Cost.Text -as [Double]))
}
}
The key thing you’re supposed to notice here is that the named controls in the XAML are automatically surfaced as variables in the event handlers, and all you have to do is write your logic and hook it up to the controls. We provide a Register-BootsEvent cmdlet which is like Register-ObjectEvent except that it executes the event handlers on the UI thread (so they can do things to the UI) instead of in a new runspace, but basically it’s like calling Add_Click. Of course, you can use Register-ObjectEvent if you just want to spin off PowerShell tasks that don’t read/write the UI.
Backwards compatibility
To get this to work in the current release, you need a function Export-NamedControl to create variables for each of those named controls as variables. Once you’ve defined that function, you can write code very much like I did before, but you have to call Export-NamedControl yourself, and you won’t have that Register-BootsEvent cmdlet. You don’t really need it for this anyway, so that’s not a big deal, at least in this case. Here’s the function. and the actual code to create the window. You can just paste this into a script file:
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$true, Position=1, Mandatory=$true)]
$Root = $BootsWindow
)
process {
Invoke-BootsWindow $Root {
$control = $BootsWindow
while($control) {
$control = $control | ForEach-Object {
$Element = $_
if(!$Element) { return }
Write-Verbose "This $($Element.GetType().Name) is $Element"
if($Element.Name) {
Write-Verbose "Defining $($Element.Name) = $Element"
Set-Variable "$($Element.Name)" $Element -Scope 2
}
## Return all the child controls ...
@($Element.Children) + @($Element.Child) + @($Element.Content) +
@($Element.Items) + @($Element.Inlines) + @($Element.Blocks)
}
}
}
}
}
####################################################################################################################
## Create the window and hook the click. Make sure to use full paths
New-BootsWindow {} -FileTemplate $pwd\MainWindow.xaml -On_Loaded {
Export-NamedControl -Root $Args[0]
$Calculate.Add_Click({
$Total.Text = '${0:n2}' -f (($Miles.Text -as [Double]) / ($Mpg.Text -as [Double]) * ($Cost.Text -as [Double]))
})
}
And in order to try my demo, you’re going to need that MainWindow.xaml file:
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Trip Cost">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="110" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Content="Miles" VerticalAlignment="Top" />
<TextBox Grid.Column="1" Height="23" Width="200" Name="miles" HorizontalAlignment="Stretch" />
<Label Grid.Row="1" Content="Miles per Gallon" VerticalAlignment="Top" />
<TextBox Grid.Column="1" Grid.Row="1" Height="23" Width="200" HorizontalAlignment="Stretch" Name="mpg" />
<Label Grid.Row="2" Content="Cost per Gallon" VerticalAlignment="Top" />
<TextBox Grid.Column="1" Grid.Row="2" Height="23" Width="200" HorizontalAlignment="Stretch" Name="cost" />
<Button Name="calculate" Grid.Row="3" Content="_Calculate" HorizontalAlignment="Center" />
<TextBlock Grid.Column="1" Grid.Row="3" Height="23" Width="200" HorizontalAlignment="Stretch" Name="total" />
</Grid>
</Window>
[...] Creating WPF UIs for PowerShell with PowerBoots and Visual Studio WPF Designer (Joel Bennett) [...]
[...] Creating WPF UIs for PowerShell with PowerBoots and Visual Studio WPF Designer – Joel Bennett creates a WPF GUI in Visual Studio and use the Xaml file in PowerShell PowerBoots [...]