Skip to content

Instantly share code, notes, and snippets.

@steviecoaster
Created July 2, 2025 17:18
Show Gist options
  • Save steviecoaster/bbde7a62fb357060586f824121da055c to your computer and use it in GitHub Desktop.
Save steviecoaster/bbde7a62fb357060586f824121da055c to your computer and use it in GitHub Desktop.
Example NavigationPane app using WinUIShell
using namespace WinUIShell
[CmdletBinding()]
Param()
begin {}
process {
if (-not (Get-Module WinUIShell)) {
Import-Module WinUIShell
}
$resources = [Application]::Current.Resources
$win = [Window]::new()
$win.Title = 'Navigation View'
$win.SystemBackdrop = [DesktopAcrylicBackdrop]::new()
$win.AppWindow.TitleBar.PreferredTheme = 'UseDefaultAppMode'
$win.AppWindow.ResizeClient(1000, 520)
$frame = [Frame]::new()
$navigationView = [NavigationView]::new()
$navigationView.Content = $frame
$navigationView.PaneTitle = 'Menu'
$navigationView.ExpandedModeThresholdWidth = 800
$navigationView.CompactModeThresholdWidth = 400
$convertPageOnLoaded = {
param ($pageName, $page, $e)
if ($page.Content) {
# Cached instance is used so we reuse the content.
return
}
$title = [TextBlock]::new()
$title.Text = "This is $pageName"
$title.Style = $resources['TitleTextBlockStyle']
$pressToClose = $false
$button = [Button]::new()
$button.HorizontalAlignment = 'Right'
$button.Content = 'Convert'
$button.Style = $resources['AccentButtonStyle']
$button.AddClick({
if ($script:pressToClose) {
$win.Close()
return
}
<#
# Don't let people click the button while a conversion is happening
$button.IsEnabled = $false
# Do the conversion
$convert = @('pkcs12', '-export', '-in', $('{0}' -f $txtCert.Text), '-inkey', $('{0}' -f $txtKey.Text), '-out', "$('{0}' -f $txtPfx.Text)", '-passout', "pass:$(([System.Net.NetworkCredential]::new($null,$($password.Password)).Password))")
try {
Start-Transcript C:\temp\attempt.log
& openssl @convert
# Update our status
$status.Text = '🎉 Done!'
Stop-Transcript
}
catch {
$status.Text = $error[0].Exception
Stop-Transcript
}
#>
$status.Text = 'You clicked!'
$button.Content = 'Close'
$script:pressToClose = $true
$button.IsEnabled = $true
})
# Add a textbox for our original certificate file
$txtCert = [TextBox]::new()
$txtCert.Header = 'Certificate File (.cer or .cert)'
$txtCert.PlaceHolderText = '.cer or .cert accepted'
$txtCert.Margin = [Thickness]::new(0, 0, 0, 6)
#And one for the key file
$txtKey = [TextBox]::new()
$txtKey.Header = 'Key File'
$txtKey.PlaceHolderText = '.key file accepted'
$txtKey.Margin = [Thickness]::new(0, 0, 0, 6)
# Save the pfx file to here
$txtPfx = [TextBox]::New()
$txtPfx.Header = 'Pfx File'
$txtPfx.PlaceholderText = 'Save to...'
$txtPfx.Margin = [Thickness]::new(0, 0, 0, 6)
# The password field
$password = [PasswordBox]::new()
$password.Header = 'Export Password'
$password.Description = 'This will be the password on the private key for the pfx file.'
$password.Margin = [Thickness]::new(0, 0, 0, 6)
$status = [TextBlock]::new()
$status.Text = ''
$status.Margin = [Thickness]::new(0, 0, 0, 2)
# A stack panel to display everything in
$panel = [StackPanel]::new()
$panel.Margin = 16
$panel.Spacing = 6
#$panel.CanVerticallyScroll = $true
$panel.Children.Add($title)
$panel.Children.Add($txtCert)
$panel.Children.Add($txtKey)
$panel.Children.Add($txtPfx)
$panel.Children.Add($password)
$panel.Children.Add($status)
$panel.Children.Add($button)
$page.Content = $panel
}
$samplePageOnLoaded = {
$pressToClose = $false
$button = [Button]::new()
$button.HorizontalAlignment = 'Right'
$button.Content = 'Convert'
$button.Style = $resources['AccentButtonStyle']
$button.AddClick({
if ($script:pressToClose) {
$win.Close()
return
}
$status.Text = 'You clicked!'
$button.Content = 'Close'
$script:pressToClose = $true
$button.IsEnabled = $true
})
$status = [TextBlock]::new()
$status.Text = 'This should change'
$status.Margin = [Thickness]::new(0, 0, 0, 2)
# A stack panel to display everything in
$panel = [StackPanel]::new()
$panel.Children.Add($status)
$panel.Children.Add($button)
$page.Content = $panel
}
$contentPageOnLoaded = {
param ($pageName, $page, $e)
if ($page.Content) {
# Cached instance is used so we reuse the content.
return
}
$title = [TextBlock]::new()
$title.Text = "This is $pageName"
$title.Style = $resources['TitleTextBlockStyle']
$text = [TextBlock]::new()
$text.Text = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ac mi ipsum. Phasellus vel malesuada mauris. Donec pharetra, enim sit amet mattis tincidunt, felis nisi semper lectus, vel porta diam nisi in augue. Pellentesque lacus tortor, aliquam et faucibus id, rhoncus ut justo. Sed id lectus odio, eget pulvinar diam. Suspendisse eleifend ornare libero, in luctus purus aliquet non. Sed interdum, sem vitae rutrum rhoncus, felis ligula ultrices sem, in eleifend eros ante id neque.'
$text.Style = $resources['BodyTextBlockStyle']
$button = [Button]::new()
$button.HorizontalAlignment = 'Right'
$button.Content = 'Click Me'
$button.Style = $resources['AccentButtonStyle']
# Add a textbox for our original certificate file
$txtCert = [TextBox]::new()
$txtCert.Header = 'Certificate File (.cer or .cert)'
$txtCert.PlaceHolderText = '.cer or .cert accepted'
$txtCert.Margin = [Thickness]::new(0, 0, 0, 24)
#
$panel = [StackPanel]::new()
$panel.Margin = 32
$panel.Spacing = 16
$panel.Children.Add($title)
#$panel.Children.Add($text)
$panel.Children.Add($txtCert)
$panel.Children.Add($txtKey)
$panel.Children.Add($button)
$page.Content = $panel
}
$settingsPageOnLoaded = {
param ($pageName, $page, $e)
if ($page.Content) {
return
}
$toggle1 = [ToggleSwitch]::new()
$toggle1.IsOn = $true
$toggle1.Header = 'Badge Notification'
$toggle2 = [ToggleSwitch]::new()
$toggle2.Header = 'Banner Notification'
$toggle3 = [ToggleSwitch]::new()
$toggle3.Header = 'Brightness Control'
$panel = [StackPanel]::new()
$panel.Margin = 32
$panel.Spacing = 16
$panel.Children.Add($toggle1)
$panel.Children.Add($toggle2)
$panel.Children.Add($toggle3)
$page.Content = $panel
}
function Navigate($pageName) {
if ($frame.SourcePageName -eq $pageName) {
return
}
# $onLoaded = if ($pageName -eq 'Settings') { $settingsPageOnLoaded } else { $contentPageOnLoaded }
$onLoaded = switch ($pageName) {
'Settings' { $settingsPageOnLoaded }
'Convert' { $convertPageOnLoaded }
'Sample' { $samplePageOnLoaded}
}
$onLoadedArgumentList = $pageName
# Page instance is created only once per page name. Cached pages are used from the second navigation.
$cacheMode = [NavigationCacheMode]::Enabled
# You can change the transition animation by setting [DrillInNavigationTransitionInfo]::new() etc.
$transition = $e.RecommendedNavigationTransitionInfo
# Page instance is created internally and onLoaded script block is called.
$frame.Navigate($pageName, $transition, $cacheMode, $onLoaded, $onLoadedArgumentList) | Out-Null
}
# Called when NavigationViewItem is clicked.
$navigationView.AddItemInvoked({
param($argumentList, $s, $e)
if ($e.IsSettingsInvoked) {
$pageName = 'Settings'
}
else {
$pageName = $e.InvokedItemContainer.Tag
}
Navigate $pageName
})
# Called when Back button is clicked.
$navigationView.AddBackRequested({
if (-not ($frame.CanGoBack)) {
return
}
$frame.GoBack()
})
$frame.AddNavigated({
param($argumentList, $s, $e)
# Property accesses are slow as they require communication with the server.
# Use temporary variables to keep property access as few as possible.
$pageName = $frame.SourcePageName
# Navigation also happens when back button is pressed.
# That's why we set these properties here instead of in the ItemInvoked handler.
$navigationView.IsBackEnabled = $frame.CanGoBack
$navigationView.Header = $pageName
if ($pageName -eq 'Settings') {
$menuItem = $navigationView.SettingsItem
}
else {
$menuItem = $menuItemMap[$pageName]
}
$navigationView.SelectedItem = $menuItem
})
$separator = [NavigationViewItemSeparator]::new()
$navigationView.MenuItems.Add($separator)
$menuItemMap = @{}
$item1 = [NavigationViewItem]::new()
$item1.Content = 'Home'
$item1.Icon = [SymbolIcon]::new('Home')
$item1.Tag = 'Home'
$menuItemMap[$item1.Tag] = $item1
$navigationView.MenuItems.Add($item1)
$item2 = [NavigationViewItem]::new()
$item2.Content = 'Favorite'
$item2.Icon = [SymbolIcon]::new('Favorite')
$item2.Tag = 'Favorite'
$menuItemMap[$item2.Tag] = $item2
$navigationView.MenuItems.Add($item2)
$header = [NavigationViewItemHeader]::new()
$header.Content = 'Utilities'
$navigationView.MenuItems.Add($header)
$item3 = [NavigationViewItem]::new()
$item3.Content = 'Convert w/ OpenSSL'
$item3.Icon = [SymbolIcon]::new('XboxOneConsole')
$item3.Tag = 'Convert'
$menuItemMap[$item3.Tag] = $item3
$navigationView.MenuItems.Add($item3)
$item4 = [NavigationViewItem]::new()
$item4.Content = 'Sample'
$item4.Icon = [SymbolIcon]::new('XboxOneConsole')
$item4.Tag = 'Sample'
$menuItemMap[$item4.Tag] = $item4
$navigationView.MenuItems.Add($item4)
$win.Content = $navigationView
$win.Activate()
Navigate 'Home'
$win.WaitForClosed()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment