Latest release and demo code available here
This project aims to offer easy to use and modern UI for Windows desktop devices during setup. You can use it from PowerShell scripts by writing commands in a file wich is watched by the app.
Samples:
This project is developped with the following libraries:
- Windows App SDK
- WinUI 3
- .NET 6
Target devices must have the following runtime installed:
- Windows App SDK 1.3
- .NET 6 (not required for self-contained build)
# Launch app
Start-Process -FilePath "./Windows Desktop Script UI.exe" -ArgumentList "--WatchPath=`"commands.txt`" --WindowTitle=`"Hello World!`" --WelcomeMessage=`"Hello Folks`"" -NoNewWindow | Out-Null
--WatchPath
: path of command file--WindowTitle
: title of widow (optional)--WelcomeMessage
: main text (optional)--Height
: window height (optional)--Width
: window width (optional)-FullScreen
: fullscreen flag (optional)-AlwaysOnTop
: always on top flag (optional)-Debug
: debug flag (optional)
Note: if you are running in system context you must launch it in user context (with KelvinTegelaar/RunAsUser or Microsoft deployment toolkit ServiceUI.exe for example)
The app listen to commands written in a file, you just have to write your own function to write to the command file.
# Define file path
$FILE = "demo.txt"
# Remove file if already exist
if (Test-Path $FILE) {
Remove-Item $FILE | Out-Null
}
# Create file
New-Item $FILE | Out-Null
## Create writting function for clearer code
function UI {
param(
[Parameter(Position=0)]
[string] $command
)
Write-Host($command)
$command | out-file -append $FILE
}
UI "Terminate"
UI "MainText --Text='Hello John'"
--Text
: text to display
UI "SubText --Text='We are setting up BitLocker to protect your data.'"
--Text
: text to display
UI "MainImage --Source='$PSScriptRoot/Windows_logo.png' --Height=200 -Width=200"
--Source
: image source--Height
: image height (optional)--Width
: image width (optional)
UI "Load --Text='Please wait..'"
--Text
: text behind progresss ring (optional, use \n for line break)-Hide
: hide progress ring (optional)
UI "Input --Type=Password --PlaceHolder='Florent NOSARI' --Header='Type BitLocker password' --Button=OK --Out=input.txt"
--Type
: input type (see details below)--PlaceHolder
: placeholder for supported types (optional)--Value
: default value for supported types(optional)--AllwowedValues
: allowed values for supported types (optional)--Header
: header for supported types (optional)--Button
: submit button text (optional)--Out
: file path where user input is store after submit (optional)--Height
: input height (optional)--Width
: input width (optional)
User input is stored in a file (file is empty if value is not provided on input is simple button), you have to wait for file change before continuig you script, here is a short example.
## Function towait for file change
function Wait-FileChange {
param(
[string]$File
)
$FilePath = Split-Path $File -Parent
$FileName = Split-Path $File -Leaf
$global:FileChanged = $false
$Watcher = New-Object IO.FileSystemWatcher $FilePath, $FileName -Property @{
IncludeSubdirectories = $false
EnableRaisingEvents = $true
}
Unregister-Event -SourceIdentifier "filechanged" -ErrorAction SilentlyContinue
Register-ObjectEvent $Watcher Changed -Action {$global:FileChanged = $true} -SourceIdentifier "filechanged" | Out-Null
while ($global:FileChanged -eq $false){
Start-Sleep -Milliseconds 100
}
Unregister-Event -SourceIdentifier "filechanged"
}
....
# Define file path
$INPUTFILE = "./out"
# Request input
UI "Input --Type=Password --Header='Type BitLocker password' --Button=OK --Out=$INPUTFILE"
# Wait for file to be created
Wait-FileChange -File $INPUTFILE
## Get content
$PIN = $(Get-Content -Path $INPUTFILE)
# Remove file
Remove-Item $INPUTFILE
The following input types are supported:
Type | Description | WINUI 3 Object | Supported options |
---|---|---|---|
Text |
Basic text field | TextBox |
Header PlaceHolder Value |
Password |
Password text field | PasswordBox |
Header |
ComboBox |
Predefined value selector | ComboBox |
Header AllowedValues (separated by "|") Value (represent the default value) |
ImageChooser |
Grid with images as available values | GridView |
Header AllowedValues (separated by "|") |
ButtonImage |
Button with image to display | Image |
|
ButtonVideo |
Button with video | MediaPlayerElement |
Autoplay ShowControl SoundOn |
ButtonText |
Button with rich text | RichTextBlock |
# Text
UI "Input --Type=Text --Header='Enter value' --Button=OK --Out=$INPUTFILE"
# Password
UI "Input --Type=Password --Header='Enter password' --Button=OK --Out=$INPUTFILE"
# ComboBox
UI "Input --Type=ComboBox --Header='Select option' --AllowedValues='One|Two|Three' --Value='Two' --Button=OK --Out=$INPUTFILE"
# ImageChooser
UI "Input --Type=ImageChooser --Header='Select option' --AllowedValues='$PSScriptRoot/One.png|$PSScriptRoot/Two.png|$PSScriptRoot/Three.png' --Button=OK --Out=$INPUTFILE"
# ButtonImage
UI "Input --Type=ButtonImage --Value='$PSScriptRoot/Image.png' --Button='Next' --Out=$INPUTFILE"
# ButtonVideo
UI "Input --Type=ButtonImage --Value='$PSScriptRoot/Video.mp4' --Button='Next' --Out=$INPUTFILE"
# ButtonText
$content="<Paragraph>Lorem ipsum dolor sit amet.</Paragraph>" # be aware of " and ' interpretation
UI "Input --Type=ButtonImage --Value='$content' --Button='Accept' --Out=$INPUTFILE"
ButtonText value must be valid XAML wich can be included in RichTextBlock
(See documentation). Find an example below:
<Paragraph TextAlignment="Center">
<InlineUIContainer>
<Image
Source="C:\temp\contoso.png"
Width="100"/>
</InlineUIContainer>
</Paragraph>
<Paragraph>
Lorem ipsum dolor sit amet, consectetur
adipiscing elit. Duis sit amet nisi eget ex gravida molestie. Nulla varius leo at nulla
molestie, sit amet ultricies nisi efficitur. Proin non massa eros. Fusce convallis maximus risus
ac aliquam. Fusce non tempus orci, at dignissim velit. Nulla at sollicitudin arcu. Proin arcu
mi, gravida at suscipit a, gravida eu lorem. Nulla commodo, mi sit amet tincidunt pharetra,
justo ipsum malesuada mi, eget malesuada est elit at ipsum.
</Paragraph>
<Paragraph>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis sit amet nisi eget ex
gravida molestie. Nulla varius leo at nulla molestie, sit amet ultricies nisi efficitur. Proin
non massa eros. Fusce convallis maximus risus ac aliquam. Fusce non tempus orci, at dignissim
velit. Nulla at sollicitudin arcu. Proin arcu mi, gravida at suscipit a, gravida eu lorem. Nulla
commodo, mi sit amet tincidunt pharetra, justo ipsum malesuada mi, eget malesuada est elit at
ipsum.
</Paragraph>
$FILE = "demo.txt"
# Remove file if already exist
if (Test-Path $FILE) {
Remove-Item $FILE | Out-Null
}
# Create file
New-Item $FILE | Out-Null
function UI {
param(
[Parameter(Position=0)]
[string] $command
)
Write-Host($command)
$command | out-file -append $FILE
}
function Wait-FileChange {
param(
[string]$File
)
$FilePath = Split-Path $File -Parent
$FileName = Split-Path $File -Leaf
$global:FileChanged = $false
$Watcher = New-Object IO.FileSystemWatcher $FilePath, $FileName -Property @{
IncludeSubdirectories = $false
EnableRaisingEvents = $true
}
Unregister-Event -SourceIdentifier "filechanged" -ErrorAction SilentlyContinue
Register-ObjectEvent $Watcher Changed -Action {$global:FileChanged = $true} -SourceIdentifier "filechanged" | Out-Null
while ($global:FileChanged -eq $false){
Start-Sleep -Milliseconds 100
}
Unregister-Event -SourceIdentifier "filechanged"
}
# Define input file
$INPUTFILE = "$PSScriptRoot/out"
################################################################################
############################### UI Launch ######################################
################################################################################
# Launch app
Start-Process -FilePath "./UI/Windows Desktop Script UI.exe" -ArgumentList "--WatchPath=`"$PSScriptRoot/$FILE`" --WindowTitle=`"Hello World!`" --WelcomeMessage=`"`" -AlwaysOnTop -FullScreen -Debug" -NoNewWindow | Out-Null
################################################################################
############################### Startup UI #####################################
################################################################################
UI "MainText --Text=`"Hello $($env:UserName)`""
UI "MainImage --Source=`"$PSScriptRoot/windows.png`" --Height=150"
UI "SubText --Text=`"Welcome to Windows, let's setup your device.`""
UI "Input --Type=ButtonVideo --Value=`"$PSScriptRoot/Windows11.mp4`" --Button=`"Continue`" --Height=300 --Width=500 -Autoplay --Out=`"$INPUTFILE`""
Wait-FileChange -File $INPUTFILE
################################################################################
############################### Reboot #########################################
################################################################################
UI "MainText --Text=`"Finalization"
UI "MainImage --Source=`"$PSScriptRoot/restart.png`" --Height=150"
UI "SubText --Text=`"Your device is ready to go but needs a restart, please wait.`""
UI "Load --Type='Waiting for reboot...'"
Start-Sleep -Seconds 3
UI "Terminate"
- Window name
- Window icon
- User picture
- Main text
- Sub text
- Sub image
- ProgressBar inderterminate
- ProgressBar derterminate
- ProgressBar derterminate percentage
- ProgressBar hide
- Info Bar
- Input text
- Input password
- Input button (use submit button)
- Input combobox
- Input image chooser
- Input text
- Input image
- Input video
- Input toggle switch
- Input FlipView
- Input WebView
- Input FilePicker
- BitLocker PIN
- Dark/light mode chooser
- Prefered wallpaper
- Theme color
- Application installation (if user input is needed)
The following tool are required to edit:
- Visual Studio with Windows App SDK components (instructions)
This project was inspired by Mactroll/DEPNotify (macOS)