Quantcast
Channel: The Official Scripting Guys Forum! forum
Viewing all articles
Browse latest Browse all 15028

WPF UI running in seperate runspace - able to set/get controls via synchronized hash table, but referencing the control via the hash table from within an event handler causes both runspaces to hang.

$
0
0

I am trying to build a proof of concept where a WPF form is hosted in a seperate runspace and updates are handled from the primary shell/runspace. I have had some success thanks to a great article by Boe Prox, but I am having an issue I wanted to open up to see if anyone had a suggestion.

My goals are as follows:

1.) Set control properties from the primary runspace (Completed)
2.) Get control properties from the primary runspace (Completed)
3.) Respond to WPF form events in the UI runspace from the primary runspace (Kind of broken).

I have the ability to read/write values to the form, but I am having difficulty with events. Specifically, I can fire and handle events, but the minute I try to reference the $SyncHash from within the event it appears to cause a blocking condition hanging both runspaces. As a result, I am unable to update the form based on an event being fired by a control.

In the example below, the form is loaded and the following steps occur:

1.) Update-Combobox is called and it populates the combobox with a list of service names and selects the first item.
2.) update-textbox is called and sets the Text property of the textbox.
3.) The Text value of the textbox is read by the function read-textbox and output using write-host.
4.) An event handle is registered for the SelectionChanged event for the combobox to call the update-textbox function used earlier.
5.) If you change the selection on the combobox, the shell and UI hangs as soon as $SyncHash is referenced. I suspect this is causing some sort of blocking condition from multiple threads trying to access the synchronized nature of the hash table, but I am unsure as to why / how to work around it. If you comment out the line "$SyncHash.TXT_Output.Dispatcher.Invoke("Send",[action]{$SyncHash.TXT_Output.Text = $Value})" within update-textbox the event handler will execute/complete.

$UI_JobScript = 
{   
    try{
        Function New-Form ([XML]$XAML_Form){
            $XML_Node_Reader=(New-Object System.Xml.XmlNodeReader $XAML_Form)
            [Windows.Markup.XamlReader]::Load($XML_Node_Reader)
        }
        try{
            Add-Type –AssemblyName PresentationFramework
            Add-Type –AssemblyName PresentationCore
            Add-Type –AssemblyName WindowsBase
        }
        catch{
            Throw "Unable to load the requisite Windows Presentation Foundation assemblies. Please verify that the .NET Framework 3.5 Service Pack 1 or later is installed on this system."
        }
        $Form = New-Form -XAML_Form $SyncHash.XAML_Form
        $SyncHash.Form = $Form

            
        $SyncHash.CMB_Services = $SyncHash.Form.FindName("CMB_Services")
        $SyncHash.TXT_Output = $SyncHash.Form.FindName("TXT_Output")


        $SyncHash.Form.ShowDialog() | Out-Null
        $SyncHash.Error = $Error
    }
    catch{
        write-host $_.Exception.Message
    }
}
#End UI_JobScript
#Begin Main
add-type -AssemblyName WindowsBase
[XML]$XAML_Form = @"<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"><Window.Resources><DataTemplate x:Key="DTMPL_Name"><TextBlock Text="{Binding Path=Name}" /></DataTemplate></Window.Resources><DockPanel LastChildFill="True"><StackPanel Orientation="Horizontal" DockPanel.Dock="Top"><Label Name="LBL_Services" Content="Services:" /><ComboBox Name="CMB_Services" ItemTemplate="{StaticResource DTMPL_Name}"/></StackPanel><TextBox Name="TXT_Output"/></DockPanel></Window>"@
$SyncHash = [hashtable]::Synchronized(@{})
$SyncHash.Add("XAML_Form",$XAML_Form)
$SyncHash.Add("InitialScript", $InitialScript)
$Normal = [System.Windows.Threading.DispatcherPriority]::Normal
$UI_Runspace =[RunspaceFactory]::CreateRunspace()
$UI_Runspace.ApartmentState = [System.Threading.ApartmentState]::STA
$UI_Runspace.ThreadOptions = [System.Management.Automation.Runspaces.PSThreadOptions]::ReuseThread
$UI_Runspace.Open()
$UI_Runspace.SessionStateProxy.SetVariable("SyncHash",$SyncHash)
$UI_Pipeline = [PowerShell]::Create()
$UI_Pipeline.Runspace=$UI_Runspace
$UI_Pipeline.AddScript($UI_JobScript) | out-Null
$Job = $UI_Pipeline.BeginInvoke()
$SyncHash.ServiceList = get-service | select name, status | Sort-Object -Property Name
Function Update-Combobox{
write-host "`nBegin Update-Combobox [$(get-date)]"
$SyncHash.CMB_Services.Dispatcher.Invoke($Normal,[action]{$SyncHash.CMB_Services.ItemsSource = $SyncHash.ServiceList})
$SyncHash.CMB_Services.Dispatcher.Invoke($Normal,[action]{$SyncHash.CMB_Services.SelectedIndex = 0})
write-host "`End Update-Combobox [$(get-date)]"
}
Function Update-Textbox([string]$Value){
    write-host "`nBegin Update-Textbox [$(get-date)]"
    $SyncHash.TXT_Output.Dispatcher.Invoke("Send",[action]{$SyncHash.TXT_Output.Text = $Value})
    write-host "End Update-Textbox [$(get-date)]"
}
Function Read-Textbox(){
write-host "`nBegin Read-Textbox [$(get-date)]"
$SyncHash.TXT_Output.Dispatcher.Invoke($Normal,[action]{$Global:Return = $SyncHash.TXT_Output.Text})
$Global:Return
remove-variable -Name Return -scope Global
write-host "End Read-Textbox [$(get-date)]"
}
#Give the form some time to load in the other runspace
$MaxWaitCycles = 5
while (($SyncHash.Form.IsInitialized -eq $Null)-and ($MaxWaitCycles -gt 0)){
Start-Sleep -Milliseconds 200
$MaxWaitCycles--
}
Update-ComboBox
Update-Textbox -Value $("Initial Load: $(get-date)")
Write-Host "Value Read From Textbox: $(Read-TextBox)"
Register-ObjectEvent -InputObject $SyncHash.CMB_Services -EventName SelectionChanged -SourceIdentifier "CMB_Services.SelectionChanged" -action {Update-Textbox -Value $("From Selection Changed Event: $(get-date)")}


Viewing all articles
Browse latest Browse all 15028

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>