Enhance UI for application installation options and add Winget status panel

This commit is contained in:
rbalsleyMSFT
2025-02-15 21:42:23 -08:00
parent 0669c64da1
commit 3875181d89
2 changed files with 330 additions and 7 deletions
+257
View File
@@ -27,6 +27,181 @@ function Get-USBDrives {
} }
} }
# --------------------------------------------------------------------------
# SECTION: Winget Management Functions
# --------------------------------------------------------------------------
function Test-WingetCLI {
[CmdletBinding()]
param()
$minVersion = [version]"1.8.1911"
# Check Winget CLI
$wingetCmd = Get-Command -Name winget -ErrorAction SilentlyContinue
if (-not $wingetCmd) {
return @{
Version = "Not installed"
Status = "Not installed - Install from Microsoft Store"
}
}
# Get and check version
$wingetVersion = & winget.exe --version
if ($wingetVersion -match 'v?(\d+\.\d+.\d+)') {
$version = [version]$matches[1]
if ($version -lt $minVersion) {
return @{
Version = $version.ToString()
Status = "Update required - Install from Microsoft Store"
}
}
return @{
Version = $version.ToString()
Status = $version.ToString()
}
}
return @{
Version = "Unknown"
Status = "Version check failed"
}
}
function Update-WingetVersionFields {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$wingetText,
[Parameter(Mandatory)]
[string]$moduleText
)
# Force UI update on the UI thread
$window.Dispatcher.Invoke([System.Windows.Threading.DispatcherPriority]::Normal, [Action]{
$script:txtWingetVersion.Text = $wingetText
$script:txtWingetModuleVersion.Text = $moduleText
# Force immediate UI refresh
[System.Windows.Forms.Application]::DoEvents()
})
}
function Install-WingetComponents {
[CmdletBinding()]
param(
[string]$currentWingetVersion = "Checking..."
)
$minVersion = [version]"1.8.1911"
try {
# Check and update PowerShell Module
$module = Get-InstalledModule -Name Microsoft.WinGet.Client -ErrorAction SilentlyContinue
if (-not $module -or $module.Version -lt $minVersion) {
Update-WingetVersionFields -wingetText $currentWingetVersion -moduleText "Installing..."
# Store and modify PSGallery trust setting temporarily if needed
$PSGalleryTrust = (Get-PSRepository -Name 'PSGallery').InstallationPolicy
if ($PSGalleryTrust -eq 'Untrusted') {
Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted
}
# Install/Update the module
Install-Module -Name Microsoft.WinGet.Client -Force -Repository 'PSGallery'
# Restore original PSGallery trust setting
if ($PSGalleryTrust -eq 'Untrusted') {
Set-PSRepository -Name 'PSGallery' -InstallationPolicy Untrusted
}
$module = Get-InstalledModule -Name Microsoft.WinGet.Client -ErrorAction Stop
}
return $module
}
catch {
Write-Error "Failed to install/update Winget PowerShell module: $_"
throw
}
}
# Winget Module Check Function
function Confirm-WinGetInstallation {
param(
[System.Windows.Controls.TextBlock]$txtWingetVersion,
[System.Windows.Controls.TextBlock]$txtWingetModuleVersion
)
$minVersion = [version]"1.8.1911"
$result = @{
Success = $false
Message = ""
RequiresRestart = $false
}
# Check if winget executable exists and is accessible
if (-not (Get-Command -Name winget -ErrorAction SilentlyContinue)) {
Update-VersionTextFields -wingetText "Not installed" -moduleText "Not installed"
$result.Message = "WinGet not found. Installing..."
$result.RequiresRestart = $true
return $result
}
# Get winget version
$wingetVersion = & winget.exe --version
if ($wingetVersion -match 'v?(\d+\.\d+.\d+)') {
$currentVersion = [version]$matches[1]
Update-VersionTextFields -wingetText $matches[1] -moduleText $txtWingetModuleVersion.Text
if ($currentVersion -lt $minVersion) {
Update-VersionTextFields -wingetText "Updating..." -moduleText $txtWingetModuleVersion.Text
$result.Message = "WinGet version $currentVersion is outdated. Minimum required version is $minVersion"
$result.RequiresRestart = $true
return $result
}
}
# Check if Winget PowerShell module is installed and up to date
$wingetModule = Get-InstalledModule -Name Microsoft.WinGet.Client -ErrorAction SilentlyContinue
if ($null -eq $wingetModule) {
Update-VersionTextFields -wingetText $txtWingetVersion.Text -moduleText "Installing..."
$result.Message = "Microsoft.WinGet.Client module needs to be installed..."
}
elseif ($wingetModule.Version -lt $minVersion) {
Update-VersionTextFields -wingetText $txtWingetVersion.Text -moduleText "Updating..."
$result.Message = "Microsoft.WinGet.Client module needs to be updated..."
}
else {
Update-VersionTextFields -wingetText $txtWingetVersion.Text -moduleText $wingetModule.Version.ToString()
$result.Success = $true
$result.Message = "Winget and its PowerShell module are installed and up to date."
return $result
}
# Install/Update module if needed
try {
# Check if PSGallery is trusted
$PSGalleryTrust = (Get-PSRepository -Name 'PSGallery').InstallationPolicy
if ($PSGalleryTrust -eq 'Untrusted') {
Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted
}
# Install/Update the module
Install-Module -Name Microsoft.WinGet.Client -Force -Repository 'PSGallery'
# Restore PSGallery trust setting if it was untrusted
if ($PSGalleryTrust -eq 'Untrusted') {
Set-PSRepository -Name 'PSGallery' -InstallationPolicy Untrusted
}
}
catch {
Update-VersionTextFields -wingetText $txtWingetVersion.Text -moduleText "Error"
throw
}
$result.RequiresRestart = $true
return $result
}
# Some default values # Some default values
$defaultISOPath = "" $defaultISOPath = ""
$defaultWindowsRelease = 11 # numeric $defaultWindowsRelease = 11 # numeric
@@ -184,6 +359,7 @@ function Get-UIConfig {
# --- Applications tab --- # --- Applications tab ---
$installApps = $window.FindName('chkInstallApps').IsChecked $installApps = $window.FindName('chkInstallApps').IsChecked
$installWingetApps = $window.FindName('chkInstallWingetApps').IsChecked
# Add USB drive selection to config # Add USB drive selection to config
$selectedUSBDrives = @{} $selectedUSBDrives = @{}
@@ -243,6 +419,8 @@ function Get-UIConfig {
WindowsVersion = $windowsVersion WindowsVersion = $windowsVersion
FFUPrefix = $ffuPrefix # <-- new option for VM Name Prefix FFUPrefix = $ffuPrefix # <-- new option for VM Name Prefix
USBDriveList = $selectedUSBDrives # Add USB drive selection USBDriveList = $selectedUSBDrives # Add USB drive selection
InstallApps = $installApps
InstallWingetApps = $installWingetApps
} }
# Sort the configuration hashtable alphabetically by key # Sort the configuration hashtable alphabetically by key
@@ -404,6 +582,23 @@ $script:UpdateInstallAppsBasedOnUpdates = {
} }
} }
# Create data context class for version binding
$script:versionData = [PSCustomObject]@{
WingetVersion = "Not checked"
ModuleVersion = "Not checked"
}
# Add observable property support
$script:versionData | Add-Member -MemberType ScriptMethod -Name NotifyPropertyChanged -Value {
param($PropertyName)
if ($this.PropertyChanged) {
$this.PropertyChanged.Invoke($this, [System.ComponentModel.PropertyChangedEventArgs]::new($PropertyName))
}
}
$script:versionData | Add-Member -MemberType NoteProperty -Name PropertyChanged -Value $null
$script:versionData | Add-Member -TypeName "System.ComponentModel.INotifyPropertyChanged"
$window.Add_Loaded({ $window.Add_Loaded({
$script:cmbWindowsRelease = $window.FindName('cmbWindowsRelease') $script:cmbWindowsRelease = $window.FindName('cmbWindowsRelease')
$script:cmbWindowsVersion = $window.FindName('cmbWindowsVersion') $script:cmbWindowsVersion = $window.FindName('cmbWindowsVersion')
@@ -718,6 +913,67 @@ $window.Add_Loaded({
$script:chkPromptExternalHardDiskMedia.IsEnabled = $false $script:chkPromptExternalHardDiskMedia.IsEnabled = $false
$script:chkPromptExternalHardDiskMedia.IsChecked = $false $script:chkPromptExternalHardDiskMedia.IsChecked = $false
}) })
# Add Winget panel visibility handler
$script:chkInstallApps = $window.FindName('chkInstallApps')
$script:chkInstallWingetApps = $window.FindName('chkInstallWingetApps')
$script:wingetPanel = $window.FindName('wingetPanel')
$script:btnCheckWingetModule = $window.FindName('btnCheckWingetModule')
$script:txtWingetVersion = $window.FindName('txtWingetVersion')
$script:txtWingetModuleVersion = $window.FindName('txtWingetModuleVersion')
# Hide Winget Apps checkbox initially if Install Apps is unchecked
$script:chkInstallWingetApps.Visibility = if ($script:chkInstallApps.IsChecked) { 'Visible' } else { 'Collapsed' }
# Show/Hide Winget Apps checkbox based on Install Apps state
$script:chkInstallApps.Add_Checked({
$script:chkInstallWingetApps.Visibility = 'Visible'
})
$script:chkInstallApps.Add_Unchecked({
$script:chkInstallWingetApps.IsChecked = $false
$script:chkInstallWingetApps.Visibility = 'Collapsed'
$script:wingetPanel.Visibility = 'Collapsed'
})
# Show/Hide Winget panel based on checkbox state
$script:chkInstallWingetApps.Add_Checked({ $script:wingetPanel.Visibility = 'Visible' })
$script:chkInstallWingetApps.Add_Unchecked({ $script:wingetPanel.Visibility = 'Collapsed' })
# Handle Winget component check/installation
$script:btnCheckWingetModule.Add_Click({
$this.IsEnabled = $false
$window.Cursor = [System.Windows.Input.Cursors]::Wait
# Show initial checking status
Update-WingetVersionFields -wingetText "Checking..." -moduleText "Checking..."
# Run checks in background to prevent UI freezing
$window.Dispatcher.Invoke([System.Windows.Threading.DispatcherPriority]::Background, [Action]{
try {
# Check Winget CLI first
$cliStatus = Test-WingetCLI
# Install/Update PowerShell module if needed
$module = Install-WingetComponents -currentWingetVersion $cliStatus.Status
# Update UI with final status
Update-WingetVersionFields -wingetText $cliStatus.Status -moduleText $module.Version
}
catch {
Update-WingetVersionFields -wingetText "Error" -moduleText "Error"
[System.Windows.MessageBox]::Show(
"Error checking winget components: $_",
"Error",
"OK",
"Error"
)
}
finally {
$this.IsEnabled = $true
$window.Cursor = $null
}
})
})
}) })
# Button: Build FFU # Button: Build FFU
@@ -855,6 +1111,7 @@ $btnLoadConfig.Add_Click({
$window.FindName('chkUpdatePreviewCU').IsChecked = $configContent.UpdatePreviewCU $window.FindName('chkUpdatePreviewCU').IsChecked = $configContent.UpdatePreviewCU
# Applications tab # Applications tab
$window.FindName('chkInstallApps').IsChecked = $configContent.InstallApps $window.FindName('chkInstallApps').IsChecked = $configContent.InstallApps
$window.FindName('chkInstallWingetApps').IsChecked = $configContent.InstallWingetApps
# Update USB Drive selection if present in config # Update USB Drive selection if present in config
if ($configContent.USBDriveList) { if ($configContent.USBDriveList) {
+73 -7
View File
@@ -264,9 +264,9 @@
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/> <ColumnDefinition Width="200"/>
@@ -526,13 +526,79 @@
<Grid Margin="10"> <Grid Margin="10">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/> <!-- Regular Applications Section -->
<ColumnDefinition Width="*"/> <StackPanel Grid.Row="0" Margin="0,0,0,10">
</Grid.ColumnDefinitions> <CheckBox x:Name="chkInstallApps"
<StackPanel Grid.Row="0" Grid.Column="0" Orientation="Horizontal" Margin="0,5"> Content="Install Applications"
<CheckBox x:Name="chkInstallApps" Content="Install Apps" Margin="0,0,5,0" ToolTip="When set to $true, the script will create an Apps.iso file from the $FFUDevelopmentPath\Apps folder. It will also create a VM, mount the Apps.iso, install the apps, sysprep, and capture the VM. When set to $false, the FFU is created from a VHDX file, and no VM is created."/> Margin="0,5"
ToolTip="Enable to install regular applications during the build process"/>
<!-- Winget Applications Section - Indented under Install Applications -->
<CheckBox x:Name="chkInstallWingetApps"
Content="Install Winget Applications"
Margin="20,5,0,5"
Visibility="Collapsed"
ToolTip="Enable to install applications using Windows Package Manager (winget)"/>
<!-- Winget Status Panel -->
<StackPanel x:Name="wingetPanel"
Visibility="Collapsed"
Margin="40,10,0,0">
<TextBlock Text="Winget Status"
FontWeight="Bold"
Margin="0,0,0,5"/>
<Grid Margin="0,0,0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Winget CLI Version Display -->
<TextBlock Text="Winget Version:"
Grid.Row="0"
Grid.Column="0"
VerticalAlignment="Center"
ToolTip="Current version of the Winget CLI installed on the system"/>
<TextBlock x:Name="txtWingetVersion"
Text="Not checked"
Grid.Row="0"
Grid.Column="1"
VerticalAlignment="Center"/>
<!-- Winget PowerShell Module Version Display -->
<TextBlock Text="Module Version:"
Grid.Row="1"
Grid.Column="0"
VerticalAlignment="Center"
ToolTip="Current version of the Microsoft.WinGet.Client PowerShell module"/>
<TextBlock x:Name="txtWingetModuleVersion"
Text="Not checked"
Grid.Row="1"
Grid.Column="1"
VerticalAlignment="Center"/>
<!-- Check/Install Button -->
<Button x:Name="btnCheckWingetModule"
Content="Check Winget Status"
Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="0,10,0,0"
Padding="10,5"
HorizontalAlignment="Left"
ToolTip="Check installation status and version of Winget CLI and PowerShell module. Will install or update if needed."/>
</Grid>
</StackPanel>
</StackPanel> </StackPanel>
</Grid> </Grid>
</TabItem> </TabItem>