Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Update a WPF GUI using Powershell Jobs

I've been trying to create responsive GUIs for my personal Powershell scripts. I've come up with a problem that is highly discussed online: Freezing GUI (since Powershell is single threaded).

Similar to this problem, but my case is specific to Powershell. I successfully implemented a Powershell based solution for creating GUIs relying on XAML form. Now, let's consider this code:

#EVENT Handler
$Somebutton.add_Click({
    $SomeLabel.Content = "Calculating..." 

    Start-Job -ScriptBlock {
        #Computation that takes time
        #...
        $SomeLabel.Content = "Calculated value" 
    }
})

#Show XAML GUI
$xamlGUI.ShowDialog() | out-null

xamlGUI is the form itself and $Somebutton/$SomeLabel are controls I was able to read from xaml and transform to Powershell variables.

I'm trying to understand why the Job that I start is not updating my label when the computation is done. It actually does nothing.

Im new to Powershell jobs and I'm wondering if there is something I'm missing.

like image 415
scharette Avatar asked Mar 03 '26 00:03

scharette


1 Answers

Here's a little boilerplate I use for reactive WPF forms in PowerShell:

# Hide yo console
$SW_HIDE, $SW_SHOW = 0, 5
$TypeDef = '[DllImport("User32.dll")]public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);'
Add-Type -MemberDefinition $TypeDef -Namespace Win32 -Name Functions
$hWnd = (Get-Process -Id $PID).MainWindowHandle
$Null = [Win32.Functions]::ShowWindow($hWnd,$SW_HIDE)

# Define your app + form
Add-Type -AssemblyName PresentationFramework
$App = [Windows.Application]::new() # or New-Object -TypeName Windows.Application
$Form = [Windows.Markup.XamlReader]::Load(
    [Xml.XmlNodeReader]::new([xml]@'
WPF form definition goes here
'@)
)
# or ::Load((New-Object -TypeName Xml.XmlNodeReader -ArgumentList ([xml]@'
#wpfdef
#'@))
#)

# Fixes the "freeze" problem
function Update-Gui {
    # Basically WinForms Application.DoEvents()
    $App.Dispatcher.Invoke([Windows.Threading.DispatcherPriority]::Background, [action]{})
}

# Event handlers go here
$Form.add_Closing({
    $Form.Close()
    $App.Shutdown()
    Stop-Process -Id $PID # or return your console: [Win32.Functions]::ShowWindow($hWnd,$SW_SHOW)
})

# Finally
$App.Run($Form)

Remember to clean up when your app is shutting down:

$Form.Close()
$App.Shutdown()
Stop-Process -Id $PID

Whenever you need your changes to the GUI to be reflected, call the Update-Gui function.

like image 107
Maximilian Burszley Avatar answered Mar 04 '26 15:03

Maximilian Burszley



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!