Note
This module is in its prototyping phase. Frequent breaking changes are expected until this notice is removed.
- PowerShell 7.4 or newer
- Windows(win-x64/win-arm64), macOS(osx-arm64) or Linux(linux-x64)
Install-PSResource -Name GliderUIOn macOS or Linux, call Enable-GLIExecution once after the installation or update using the same user account that installed the module:
# Adds execute permission to the server executable file.
Enable-GLIExecutionThe module includes server files for all supported platforms, totaling hundreds of megabytes. You can optionally call Remove-GLINonTargetPlatform to remove the files that are not needed on the current platform.
# Optionally removes server binaries for other platforms.
Remove-GLINonTargetPlatformThis code creates a Window that has a clickable button:
using namespace GliderUI.Avalonia.Controls
Import-Module GliderUI -Force
$win = [Window]::new()
$win.Title = 'Hello from PowerShell!'
$win.Width = 400
$win.Height = 200
$button = [Button]::new()
$button.Content = 'Click Me'
$button.HorizontalAlignment = 'Center'
$button.VerticalAlignment = 'Center'
$button.AddClick({
$button.Content = 'Clicked!'
})
$win.Content = $button
# Show() shows the window but does not block the script.
$win.Show()
$win.WaitForClosed()If you dot-source the script and comment out $win.WaitForClosed(), you can inspect UI objects or even modify them on the terminal:
PS> $button
ClickMode : Release
HotKey :
CommandParameter :
IsDefault : False
IsCancel : False
IsPressed : False
Flyout :
Content : Click Me
ContentTemplate :
Presenter : GliderUI.Avalonia.Controls.Presenters.ContentPresenter
:Since the API of GliderUI follows the Avalonia's API, you can read the Avalonia documentation to see what should be available. The documentation of the Button class is here for example.
You can also refer to the examples folder for script samples.
GliderUI launches a server process GliderUI.Server that provides all the UI functionalities. The GliderUI module communicates with the server through IPC (Inter-Process Communication) to create UI elements and handle events. No Avalonia's dlls are loaded in PowerShell.
This model simplifies the script structure. You can write long-running code in event handlers without blocking GUI. It's also allowed to access properties of UI elements directly on any thread without using Dispatchers.
This works:
$status = [TextBlock]::new()
$progressBar = [ProgressBar]::new()
$button.AddClick({
$button.IsEnabled = $false
$status.Text = 'Downloading...'
1..50 | ForEach-Object {
$progressBar.Value = $_
Start-Sleep -Milliseconds 50
}
$status.Text = 'Installing...'
51..100 | ForEach-Object {
$progressBar.Value = $_
Start-Sleep -Milliseconds 50
}
$status.Text = '🎉Done!'
$button.IsEnabled = $true
})All Avalonia controls and types become accessible by adding "GliderUI" as a prefix to their namespaces.
$button = [GliderUI.Avalonia.Controls.Button]::new()Most methods and properties are automatically generated by the source generator, but those that use the following types are not supported:
- Arrays
- Delegates
- Pointers
- Types with
reforoutmodifiers
The current supported Avalonia version is 12.0.1.
Event callbacks are script blocks that are invoked when UI events are fired. You can register them with Add{EventName} methods of UI elements. The typical example is the Click event of a button:
$button = [Button]::new()
$argumentList = 1, 2
$button.AddClick({
param ($argumentList, $s, $e)
Write-Host "ArgumentList: $argumentList"
Write-Host "Sender: $s"
Write-Host "EventArgs: $e"
}, $argumentList)The same code can also be written using the EventCallback class. It allows you to customize the callback behavior:
$button = [Button]::new()
$clickCallback = [EventCallback]::new()
$clickCallback.RunspaceMode = 'RunspacePoolAsyncUI'
$clickCallback.DisabledControlsWhileProcessing = $button
$clickCallback.ArgumentList = 1, 2
$clickCallback.ScriptBlock = {
param ($argumentList, $s, $e)
Write-Host "ArgumentList: $argumentList"
Write-Host "Sender: $s"
Write-Host "EventArgs: $e"
}
$button.AddClick($clickCallback)The RunspaceMode property controls where and how the script block runs.
$clickCallback.RunspaceMode = 'RunspacePoolAsyncUI'There are three runspace modes, MainRunspaceAsyncUI, MainRunspaceSyncUI, and RunspacePoolAsyncUI.
The default value of RunspaceMode is MainRunspaceAsyncUI which means that the script block runs in the runspace where the script block is created (Main runspace). The script block can be a bound script block and sees the global or script scope variables in the runspace.
AsyncUI means that the callbacks do not block the UI thread on the server side. Even if a callback takes long time to finish, the UI stays responsive. If a button is pressed while the previous callback is running, the new callback is queued and processed after the previous one completes. If this behavior is not desirable, you can specify controls that are disabled on the server side while the event callback is running:
$clickCallback.DisabledControlsWhileProcessing = $buttonIt is a good practice to set the DisabledControlsWhileProcessing for long-running callbacks to avoid unintuitive queuing.
There is one callback queue per runspace where GliderUI module is loaded, and callbacks in the queue are typically processed inside Window.WaitForClosed method or {AwaitableType}.WaitForCompleted method. Please see MultipleRunspaces.ps1 for an example of multi-runspace scenario.
Callbacks in MainRunspaceSyncUI mode run in the main runspace just like those with MainRunspaceAsyncUI, but they block the UI thread on the server side until they complete. Because they block the UI thread, it is guaranteed that no other events are triggered while the callback is running (No need to set DisabledControlsWhileProcessing).
Callbacks in RunspacePoolAsyncUI mode are handled in parallel by multiple runspaces in the runspace pool. This mode is ideal for long-running callbacks that must keep other callbacks responsive during execution. You can specify the number of runspaces in the pool and the script block that defines the global variables and functions in the runspaces:
Set-GLIRunspacePoolOption -RunspaceCount 5 -InitializationScript {
param ($ScriptRoot)
$globalVar = 'Global variable in the runspace.'
function GlobalFunction() {
'Global function in the runspace.'
}
} -InitializationScriptArgumentList $PSScriptRootNote that the GliderUI module is automatically loaded in each runspace.
Since the callbacks are executed in parallel, you should pass variables via ArgumentList and handle thread safety just as you would with Start-ThreadJob. See CancelLongRunningEventCallback.ps1 as a basic example, and MultipleProgressBars.ps1 as an example of multiple concurrent tasks.
Instead of creating UI elements by code, you can also create them by loading XAML similar to WPF in PowerShell. You can search for an UI element by the FindControl method of Control and add event handlers from PowerShell. Note that you can't use x:Class attributes or code-behind binding in XAML.
using namespace GliderUI.Avalonia.Markup.Xaml
$xamlString = @'
<Window
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Button x:Name="button" Content="Click Me" HorizontalAlignment="Center" />
</Window>
'@
$win = [AvaloniaRuntimeXamlLoader]::Parse($xamlString, $null)
$win.Width = 400
$win.Height = 200
$button = $win.FindControl('button')
$button.AddClick({
$button.Content = 'Clicked!'
})
$win.Show()
$win.WaitForClosed()Please read our Code of Conduct to foster a welcoming environment. By participating in this project, you are expected to uphold this code.
Please come to our Discussions page and avoid filing an issue to ask a question.
Please see our Contribution Guidelines.
GliderUI uses:
- Avalonia
https://github.com/AvaloniaUI/Avalonia - vs-StreamJsonRpc
https://github.com/microsoft/vs-streamjsonrpc

