In my first two posts on this subject, I was just getting started with learning responsive WPF form building. I’m here today to show you a better way to build a responsive WPF using runspaces that will do the exact same thing as my previous uploads showed. Just better.
This time, I won’t be putting the form into its own runspace. As I learnt you didn’t need to from JRV over on the TechNet forums. This has some benefits that I will very helpfully, briefly and probably incorrectly list below:
- Don’t have to use syncHash when updating the form
- One less runspace for the form
- Having a form in its own runspace creates additional overhead and possible errors
So to display the form I would use something like the below:
$xml = @"
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Title="Counter" Height="119" Width="351.5" ResizeMode="CanMinimize" WindowStartupLocation="CenterScreen">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<Label Name="Label" Content="0" HorizontalAlignment="Left" Margin="16.666,9.333,0,0" VerticalAlignment="Top" FontSize="18"/>
<Button Name="Button" Content="Start" HorizontalAlignment="Center" VerticalAlignment="Top" Width="75" Margin="123.25,63,123.25,0"/>
</Grid>
</Window>
"@
$Reader=(New-Object System.Xml.XmlNodeReader $xml)
$Window=[Windows.Markup.XamlReader]::Load($Reader)
$Label = $Window.FindName("Label")
$Button = $Window.FindName("Button")
$Window.ShowDialog() | Out-Null
Which will give us the below form:

But what if I want the button press to make the label number increase? I would use something like this on the button press:
$xml = @"
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Title="Counter" Height="119" Width="351.5" ResizeMode="CanMinimize" WindowStartupLocation="CenterScreen">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<Label Name="Label" Content="0" HorizontalAlignment="Left" Margin="16.666,9.333,0,0" VerticalAlignment="Top" FontSize="18"/>
<Button Name="Button" Content="Start" HorizontalAlignment="Center" VerticalAlignment="Top" Width="75" Margin="123.25,63,123.25,0"/>
</Grid>
</Window>
"@
$Reader=(New-Object System.Xml.XmlNodeReader $xml)
$Window=[Windows.Markup.XamlReader]::Load($Reader)
$Label = $Window.FindName("Label")
$Button = $Window.FindName("Button")
$Button.Add_Click({
$counter = 1
do{
Start-Sleep -Milliseconds 5
$label.content = $counter
[System.Windows.Forms.Application]::DoEvents()
$counter += 1
}while ($counter -le 5000)
})
$Window.ShowDialog() | Out-Null
This produces a form which increases the label up to 5000 when the button is pressed. You can see this below:
But what if I want to actually run something in a runspace. For example, test the connection to google.com? Then I would use the below code:
$xml = @"
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Title="Counter" Height="119" Width="351.5" ResizeMode="CanMinimize" WindowStartupLocation="CenterScreen">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<Label Name="Label" Content="0" HorizontalAlignment="Left" Margin="16.666,9.333,0,0" VerticalAlignment="Top" FontSize="18"/>
<Button Name="Button" Content="Start" HorizontalAlignment="Center" VerticalAlignment="Top" Width="75" Margin="123.25,63,123.25,0"/>
</Grid>
</Window>
"@
$Reader=(New-Object System.Xml.XmlNodeReader $xml)
$Window=[Windows.Markup.XamlReader]::Load($Reader)
$Label = $Window.FindName("Label")
$Button = $Window.FindName("Button")
$Button.Add_Click({
$syncHash = [hashtable]::Synchronized(@{})
$Runspace = [runspacefactory]::CreateRunspace()
$Runspace.ApartmentState = "STA"
$Runspace.ThreadOptions = "ReuseThread"
$Runspace.Open()
$Runspace.SessionStateProxy.SetVariable("syncHash",$syncHash)
$powershell = ::Create().AddScript({
$connection = Test-Connection -ComputerName google.com -Count 5
$syncHash.output = [math]::Round(($connection.ResponseTime | Measure-Object -Average).Average)
})
$powershell.Runspace = $Runspace
$Object = $powershell.BeginInvoke()
do {
Start-Sleep -Milliseconds 50
[System.Windows.Forms.Application]::DoEvents()
}while(!$Object.IsCompleted)
$powershell.EndInvoke($Object)
$powershell.Dispose()
$label.Content = $syncHash.output
})
$Window.ShowDialog() | Out-Null
All the above examples will stay responsive whilst the action is performed. There are a couple of different methods to do this as you can see above, for anything that takes some time to complete, a runspace is needed. But when you are updating the form quickly, like the counter, then no runspace is needed.
Enjoy!