This post will take a look at providing Powershell script output within a WPF GUI. The code will display a log of messages that would usually be sent to the console, but are easily missed by users when interacting with a GUI. This also makes it simple to display output from other runspaces when using multiple sessions.
An Example GUI
A previous post looked at how to get a WPF GUI working with Powershell. We will modify the code created back then by adding a section to display the log messages for this example. If you are not familiar with data binding in WPF it would be helpful to take a look at the overview here.
WPF data binding
To make this work all that is needed is an ObservableCollection
of type string
with a single item that will hold the log messages. When this is bound to a TextBox
control any updates to the value of the string will trigger a CollectionChanged
event, which WPF controls listen for by default and dynamically update their content.
Below is a simplified section from the full script which creates and binds the ObservableCollection
.
$Sync.LogDataContext = New-Object -TypeName System.Collections.ObjectModel.ObservableCollection[string]
$Sync.LogDataContext.Add('')
$Sync.Gui.LogTextBox.DataContext = $Sync.LogDataContext
# append to the first item of the ObservableCollection to write to the TextBox
$Sync.LogDataContext[0] += "Message one.`r`n"
By setting the DataContext
for the text box control we can reference the first item of the collection with just [0]
in the XAML definition. This completes the data binding.
<TextBox x:Name="LogTextBox" Text="{Binding [0], Mode=OneWay}" />
Displaying the log messages
As new log messages are added to the TextBox
they should be always visible to the user. One way to do this is by making the ScrollViewer
control automatically scroll down when a message is added. However, if the user has intentionally scrolled up the auto-scroll will not happen, and allows previous sections of the log to be viewed. The ScrollChanged
event is used to run the code which handles this behaviour.
Below is the section from the full script.
# save the state of $AutoScroll outside the event context so it is not reset on every event
$global:AutoScroll = $true
$Sync.Gui.LogScrollViewer.add_ScrollChanged({
if ($_.ExtentHeightChange -eq 0)
{
if ($Sync.Gui.LogScrollViewer.VerticalOffset -eq $Sync.Gui.LogScrollViewer.ScrollableHeight)
{
# if the ScrollViewer is scrolled to the end/bottom enable "auto-scroll"
$global:AutoScroll = $true
}
else
{
$global:AutoScroll = $false
}
}
if ($AutoScroll -eq $true -and $_.ExtentHeightChange -ne 0)
{
# scroll the ScrollViewer to the end/bottom
$Sync.Gui.LogScrollViewer.ScrollToVerticalOffset($Sync.Gui.LogScrollViewer.ExtentHeight)
}
})
And here is an example of the ScrollViewer
behaviour.
Comments
comments powered by Disqus