Files
FFU/FFUDevelopment/FFUUI.Core/FFUUI.Core.WindowsSettings.psm1
T
rbalsleyMSFT eaa3e1e6af Adds Windows 11 25H2 mapping
Extends supported Windows 11 releases to include 25H2 to keep version resolution current for upcoming media and configuration scenarios.
2025-10-01 13:23:46 -07:00

682 lines
26 KiB
PowerShell

<#
.SYNOPSIS
Manages the business logic for the 'Windows Settings' tab in the FFU Builder UI.
.DESCRIPTION
This module contains all the functions and data required to populate and manage the controls on the 'Windows Settings' tab. It handles dynamic updates for Windows Release, Version, SKU, and Architecture ComboBoxes based on user input, such as specifying an ISO path. It also includes logic for populating available languages, media types, and the grid of optional Windows features.
#>
# --------------------------------------------------------------------------
# SECTION: Module Variables (Static Data)
# --------------------------------------------------------------------------
$script:allowedFeatures = @(
"AppServerClient", "Client-DeviceLockdown", "Client-EmbeddedBootExp", "Client-EmbeddedLogon",
"Client-EmbeddedShellLauncher", "Client-KeyboardFilter", "Client-ProjFS", "Client-UnifiedWriteFilter",
"Containers", "Containers-DisposableClientVM", "Containers-HNS", "Containers-SDN", "DataCenterBridging",
"DirectoryServices-ADAM-Client", "DirectPlay", "HostGuardian", "HypervisorPlatform", "IIS-ApplicationDevelopment",
"IIS-ApplicationInit", "IIS-ASP", "IIS-ASPNET45", "IIS-BasicAuthentication", "IIS-CertProvider",
"IIS-CGI", "IIS-ClientCertificateMappingAuthentication", "IIS-CommonHttpFeatures", "IIS-CustomLogging",
"IIS-DefaultDocument", "IIS-DirectoryBrowsing", "IIS-DigestAuthentication", "IIS-ESP", "IIS-FTPServer",
"IIS-FTPExtensibility", "IIS-FTPSvc", "IIS-HealthAndDiagnostics", "IIS-HostableWebCore", "IIS-HttpCompressionDynamic",
"IIS-HttpCompressionStatic", "IIS-HttpErrors", "IIS-HttpLogging", "IIS-HttpRedirect", "IIS-HttpTracing",
"IIS-IPSecurity", "IIS-IIS6ManagementCompatibility", "IIS-IISCertificateMappingAuthentication",
"IIS-ISAPIExtensions", "IIS-ISAPIFilter", "IIS-LoggingLibraries", "IIS-ManagementConsole", "IIS-ManagementService",
"IIS-ManagementScriptingTools", "IIS-Metabase", "IIS-NetFxExtensibility", "IIS-NetFxExtensibility45",
"IIS-ODBCLogging", "IIS-Performance", "IIS-RequestFiltering", "IIS-RequestMonitor", "IIS-Security", "IIS-ServerSideIncludes",
"IIS-StaticContent", "IIS-URLAuthorization", "IIS-WebDAV", "IIS-WebServer", "IIS-WebServerManagementTools",
"IIS-WebServerRole", "IIS-WebSockets", "LegacyComponents", "MediaPlayback", "Microsoft-Hyper-V", "Microsoft-Hyper-V-All",
"Microsoft-Hyper-V-Hypervisor", "Microsoft-Hyper-V-Management-Clients", "Microsoft-Hyper-V-Management-PowerShell",
"Microsoft-Hyper-V-Services", "Microsoft-Windows-Subsystem-Linux", "MSMQ-ADIntegration", "MSMQ-Container", "MSMQ-DCOMProxy",
"MSMQ-HTTP", "MSMQ-Multicast", "MSMQ-Server", "MSMQ-Triggers", "MultiPoint-Connector", "MultiPoint-Connector-Services",
"MultiPoint-Tools", "NetFx3", "NetFx4-AdvSrvs", "NetFx4Extended-ASPNET45", "NFS-Administration", "Printing-Foundation-Features",
"Printing-Foundation-InternetPrinting-Client", "Printing-Foundation-LPDPrintService", "Printing-Foundation-LPRPortMonitor",
"Printing-PrintToPDFServices-Features", "Printing-XPSServices-Features", "SearchEngine-Client-Package",
"ServicesForNFS-ClientOnly", "SimpleTCP", "SMB1Protocol", "SMB1Protocol-Client", "SMB1Protocol-Deprecation",
"SMB1Protocol-Server", "SmbDirect", "TFTP", "TelnetClient", "TIFFIFilter", "VirtualMachinePlatform", "WAS-ConfigurationAPI",
"WAS-NetFxEnvironment", "WAS-ProcessModel", "WAS-WindowsActivationService", "WCF-HTTP-Activation", "WCF-HTTP-Activation45",
"WCF-MSMQ-Activation45", "WCF-MSMQ-Activation", "WCF-NonHTTP-Activation", "WCF-Pipe-Activation45", "WCF-Services45",
"WCF-TCP-Activation45", "WCF-TCP-PortSharing45", "Windows-Defender-ApplicationGuard",
"Windows-Defender-Default-Definitions", "Windows-Identity-Foundation", "WindowsMediaPlayer", "WorkFolders-Client"
)
$script:skuList = @(
'Home',
'Home N',
'Home Single Language',
'Education',
'Education N',
'Pro',
'Pro N',
'Pro Education',
'Pro Education N',
'Pro for Workstations',
'Pro N for Workstations',
'Enterprise',
'Enterprise N',
'Enterprise 2016 LTSB',
'Enterprise N 2016 LTSB',
'Enterprise LTSC',
'Enterprise N LTSC',
'IoT Enterprise LTSC',
'IoT Enterprise N LTSC',
'Standard',
'Standard (Desktop Experience)',
'Datacenter',
'Datacenter (Desktop Experience)'
)
$script:allowedLangs = @(
'ar-sa',
'bg-bg',
'cs-cz',
'da-dk',
'de-de',
'el-gr',
'en-gb',
'en-us',
'es-es',
'es-mx',
'et-ee',
'fi-fi',
'fr-ca',
'fr-fr',
'he-il',
'hr-hr',
'hu-hu',
'it-it',
'ja-jp',
'ko-kr',
'lt-lt',
'lv-lv',
'nb-no',
'nl-nl',
'pl-pl',
'pt-br',
'pt-pt',
'ro-ro',
'ru-ru',
'sk-sk',
'sl-si',
'sr-latn-rs',
'sv-se',
'th-th',
'tr-tr',
'uk-ua',
'zh-cn',
'zh-tw'
)
$script:allWindowsReleases = @(
[PSCustomObject]@{ Display = "Windows 10"; Value = 10 },
[PSCustomObject]@{ Display = "Windows 11"; Value = 11 },
[PSCustomObject]@{ Display = "Windows Server 2016"; Value = 2016 },
[PSCustomObject]@{ Display = "Windows Server 2019"; Value = 2019 },
[PSCustomObject]@{ Display = "Windows Server 2022"; Value = 2022 },
[PSCustomObject]@{ Display = "Windows Server 2025"; Value = 2025 },
[PSCustomObject]@{ Display = "Windows 10 LTSB 2016"; Value = 2016 }, # Changed Value from 1607
[PSCustomObject]@{ Display = "Windows 10 LTSC 2019"; Value = 2019 }, # Changed Value from 1809
[PSCustomObject]@{ Display = "Windows 10 LTSC 2021"; Value = 2021 },
[PSCustomObject]@{ Display = "Windows 11 LTSC 2024"; Value = 2024 }
)
$script:mctWindowsReleases = @(
[PSCustomObject]@{ Display = "Windows 10"; Value = 10 },
[PSCustomObject]@{ Display = "Windows 11"; Value = 11 }
)
$script:windowsVersionMap = @{
10 = @("22H2")
11 = @("22H2", "23H2", "24H2", "25H2")
2016 = @("1607") # Windows 10 LTSB 2016 & Server 2016
2019 = @("1809") # Windows 10 LTSC 2019 & Server 2019
# Note: Server 2016 and LTSB 2016 now share the key 2016, mapping to version "1607"
# Note: Server 2019 and LTSC 2019 now share the key 2019, mapping to version "1809"
2021 = @("21H2") # LTSC 2021
2022 = @("21H2") # Server 2022
2024 = @("24H2") # LTSC 2024
2025 = @("24H2") # Server 2025
}
# SKU Groups
$script:clientSKUs = @(
'Home',
'Home N',
'Home Single Language',
'Education',
'Education N',
'Pro',
'Pro N',
'Pro Education',
'Pro Education N',
'Pro for Workstations',
'Pro N for Workstations',
'Enterprise',
'Enterprise N'
)
$script:serverSKUs = @(
'Standard',
'Standard (Desktop Experience)',
'Datacenter',
'Datacenter (Desktop Experience)'
)
$script:ltsc2016SKUs = @(
'Enterprise 2016 LTSB',
'Enterprise N 2016 LTSB'
)
$script:ltscGenericSKUs = @( # For LTSC 2019, 2021, 2024
'Enterprise LTSC',
'Enterprise N LTSC'
)
$script:iotLtscSKUs = @(
'IoT Enterprise LTSC',
'IoT Enterprise N LTSC'
)
# Map Windows Release Values to their corresponding SKU lists
$script:windowsReleaseSkuMap = @{
10 = $script:clientSKUs # Windows 10 Client
11 = $script:clientSKUs # Windows 11 Client
2016 = $script:serverSKUs # Windows Server 2016 (LTSB 2016 handled by Get-AvailableSkusForRelease)
2019 = $script:serverSKUs # Windows Server 2019 (LTSC 2019 handled by Get-AvailableSkusForRelease)
2022 = $script:serverSKUs # Windows Server 2022
2025 = $script:serverSKUs # Windows Server 2025
2021 = $script:ltscGenericSKUs + $script:iotLtscSKUs # Windows 10 LTSC 2021
2024 = $script:ltscGenericSKUs + $script:iotLtscSKUs # Windows 10 LTSC 2024
# Note: LTSC 2016 and LTSC 2019 SKUs are now conditionally returned by Get-AvailableSkusForRelease
}
# --------------------------------------------------------------------------
# SECTION: Functions
# --------------------------------------------------------------------------
# Function to return the default settings and static lists
function Get-WindowsSettingsDefaults {
[CmdletBinding()]
param()
return [PSCustomObject]@{
DefaultISOPath = ""
DefaultWindowsArch = "x64"
DefaultWindowsLang = "en-us"
DefaultWindowsSKU = "Pro"
DefaultMediaType = "Consumer"
DefaultOptionalFeatures = ""
DefaultProductKey = ""
AllowedFeatures = $script:allowedFeatures
AllowedLanguages = $script:allowedLangs
AllowedArchitectures = @('x86', 'x64', 'arm64')
AllowedMediaTypes = @('Consumer', 'Business')
SkuList = $script:skuList
AllWindowsReleases = $script:allWindowsReleases
MctWindowsReleases = $script:mctWindowsReleases
WindowsVersionMap = $script:windowsVersionMap
ClientSKUs = $script:clientSKUs
ServerSKUs = $script:serverSKUs
Ltsc2016SKUs = $script:ltsc2016SKUs
LtscGenericSKUs = $script:ltscGenericSKUs
IotLtscSKUs = $script:iotLtscSKUs
WindowsReleaseSkuMap = $script:windowsReleaseSkuMap
}
}
# Function to get the appropriate list of Windows Releases based on ISO path
function Get-AvailableWindowsReleases {
[CmdletBinding()]
param(
[string]$IsoPath,
[Parameter(Mandatory = $true)]
[psobject]$State
)
if (-not [string]::IsNullOrEmpty($IsoPath) -and $IsoPath.EndsWith('.iso', [System.StringComparison]::OrdinalIgnoreCase)) {
return $State.Defaults.WindowsSettingsDefaults.AllWindowsReleases
}
else {
return $State.Defaults.WindowsSettingsDefaults.MctWindowsReleases
}
}
# Function to get available Windows Versions for a given release and ISO path
function Get-AvailableWindowsVersions {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[int]$SelectedRelease,
[string]$IsoPath,
[Parameter(Mandatory = $true)]
[psobject]$State
)
$result = [PSCustomObject]@{
Versions = @()
DefaultVersion = $null
IsEnabled = $false
}
if (-not $State.Defaults.WindowsSettingsDefaults.WindowsVersionMap.ContainsKey($SelectedRelease)) {
return $result
}
$validVersions = $State.Defaults.WindowsSettingsDefaults.WindowsVersionMap[$SelectedRelease]
if (-not [string]::IsNullOrEmpty($IsoPath) -and $IsoPath.EndsWith('.iso', [System.StringComparison]::OrdinalIgnoreCase)) {
# Logic for when an ISO is specified
$result.Versions = $validVersions
# Set default selection logic (e.g., latest for Win11)
if ($SelectedRelease -eq 11 -and $validVersions -contains "24H2") {
$result.DefaultVersion = "24H2"
}
elseif ($validVersions.Count -gt 0) {
$result.DefaultVersion = $validVersions[0]
}
$result.IsEnabled = $true
}
else {
# Logic for when no ISO is specified (MCT scenario)
switch ($SelectedRelease) {
10 { $result.DefaultVersion = "22H2" }
11 { $result.DefaultVersion = "24H2" }
# Server versions typically require an ISO, but handle just in case
2016 { $result.DefaultVersion = "1607" }
2019 { $result.DefaultVersion = "1809" }
2022 { $result.DefaultVersion = "21H2" }
2025 { $result.DefaultVersion = "24H2" }
default {
if ($validVersions.Count -gt 0) { $result.DefaultVersion = $validVersions[0] }
}
}
$result.Versions = @($result.DefaultVersion) # Only the default is available/relevant
$result.IsEnabled = $false # Combo should be disabled
}
return $result
}
# Function to get available SKUs for a given Windows Release value and display name
function Get-AvailableSkusForRelease {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[int]$SelectedReleaseValue,
[Parameter(Mandatory)]
[string]$SelectedReleaseDisplayName,
[Parameter(Mandatory = $true)]
[psobject]$State
)
WriteLog "Get-AvailableSkusForRelease: Getting SKUs for Release Value '$SelectedReleaseValue', Display Name '$SelectedReleaseDisplayName'."
# Handle LTSC 2016 specifically
if ($SelectedReleaseValue -eq 2016 -and $SelectedReleaseDisplayName -like '*LTSB*') {
WriteLog "Get-AvailableSkusForRelease: Matched LTSB 2016. Returning LTSC 2016 SKUs."
return $State.Defaults.WindowsSettingsDefaults.Ltsc2016SKUs
}
# Handle LTSC 2019 specifically
# Ensure "Server" is not in the display name to avoid matching "Windows Server 2019"
elseif ($SelectedReleaseValue -eq 2019 -and $SelectedReleaseDisplayName -like '*LTSC*' -and $SelectedReleaseDisplayName -notlike '*Server*') {
WriteLog "Get-AvailableSkusForRelease: Matched LTSC 2019. Returning generic LTSC SKUs (including IoT)."
# Assuming LTSC 2019 uses the generic LTSC SKUs + IoT LTSC SKUs
return ($State.Defaults.WindowsSettingsDefaults.LtscGenericSKUs + $State.Defaults.WindowsSettingsDefaults.IotLtscSKUs | Select-Object -Unique)
}
# For all other cases, use the main SKU map
elseif ($State.Defaults.WindowsSettingsDefaults.WindowsReleaseSkuMap.ContainsKey($SelectedReleaseValue)) {
$availableSkus = $State.Defaults.WindowsSettingsDefaults.WindowsReleaseSkuMap[$SelectedReleaseValue]
WriteLog "Get-AvailableSkusForRelease: Found $($availableSkus.Count) SKUs for Release '$SelectedReleaseValue' using standard map."
return $availableSkus
}
else {
WriteLog "Get-AvailableSkusForRelease: Warning - Release Value '$SelectedReleaseValue' not found in SKU map. Returning default client SKUs."
# Fallback to a default list (e.g., client SKUs) or an empty list
return $State.Defaults.WindowsSettingsDefaults.ClientSKUs
}
}
# Function to refresh the Windows Release ComboBox based on ISO path
function Update-WindowsReleaseCombo {
param(
[string]$isoPath,
[Parameter(Mandatory = $true)]
[psobject]$State
)
if (-not $State.Controls.cmbWindowsRelease) { return }
$oldSelectedItemValue = $null
if ($null -ne $State.Controls.cmbWindowsRelease.SelectedItem) {
$oldSelectedItemValue = $State.Controls.cmbWindowsRelease.SelectedItem.Value
}
# Get the appropriate list of releases from the helper module
$availableReleases = Get-AvailableWindowsReleases -IsoPath $isoPath -State $State
# Update the ComboBox ItemsSource
$State.Controls.cmbWindowsRelease.ItemsSource = $availableReleases
$State.Controls.cmbWindowsRelease.DisplayMemberPath = 'Display'
$State.Controls.cmbWindowsRelease.SelectedValuePath = 'Value'
# Try to re-select the previously selected item, or default
$itemToSelect = $availableReleases | Where-Object { $_.Value -eq $oldSelectedItemValue } | Select-Object -First 1
if ($null -ne $itemToSelect) {
$State.Controls.cmbWindowsRelease.SelectedItem = $itemToSelect
}
elseif ($availableReleases.Count -gt 0) {
# Default to Windows 11 if available, otherwise the first item
$defaultItem = $availableReleases | Where-Object { $_.Value -eq 11 } | Select-Object -First 1
if ($null -eq $defaultItem) {
$defaultItem = $availableReleases[0]
}
$State.Controls.cmbWindowsRelease.SelectedItem = $defaultItem
}
else {
# No items available (should not happen with current logic)
$State.Controls.cmbWindowsRelease.SelectedIndex = -1
}
}
# Function to refresh the Windows Version ComboBox based on selected release and ISO path
function Update-WindowsVersionCombo {
param(
[int]$selectedRelease,
[string]$isoPath,
[Parameter(Mandatory = $true)]
[psobject]$State
)
$combo = $State.Controls.cmbWindowsVersion
if (-not $combo) { return }
# Get available versions and default from the helper module
$versionData = Get-AvailableWindowsVersions -SelectedRelease $selectedRelease -IsoPath $isoPath -State $State
# Update the ComboBox ItemsSource and IsEnabled state
$combo.ItemsSource = $versionData.Versions
$combo.IsEnabled = $versionData.IsEnabled
# Set the selected item
if ($null -ne $versionData.DefaultVersion -and $versionData.Versions -contains $versionData.DefaultVersion) {
$combo.SelectedItem = $versionData.DefaultVersion
}
elseif ($versionData.Versions.Count -gt 0) {
$combo.SelectedIndex = 0
}
else {
$combo.SelectedIndex = -1 # No items available
}
}
# Function to refresh the Windows SKU ComboBox based on selected release
function Update-WindowsSkuCombo {
param(
[Parameter(Mandatory = $true)]
[psobject]$State
)
$skuCombo = $State.Controls.cmbWindowsSKU
if (-not $skuCombo) {
WriteLog "Update-WindowsSkuCombo: SKU ComboBox not found."
return
}
$releaseCombo = $State.Controls.cmbWindowsRelease
if (-not $releaseCombo -or $null -eq $releaseCombo.SelectedItem) {
WriteLog "Update-WindowsSkuCombo: Windows Release ComboBox not found or no item selected. Cannot update SKUs."
$skuCombo.ItemsSource = @() # Clear SKUs
$skuCombo.SelectedIndex = -1
return
}
$selectedReleaseItem = $releaseCombo.SelectedItem
$selectedReleaseValue = $selectedReleaseItem.Value
$selectedReleaseDisplayName = $selectedReleaseItem.Display
$previousSelectedSku = $null
if ($null -ne $skuCombo.SelectedItem) {
$previousSelectedSku = $skuCombo.SelectedItem
}
WriteLog "Update-WindowsSkuCombo: Updating SKUs for Release Value '$selectedReleaseValue' (Display: '$selectedReleaseDisplayName')."
# Call Get-AvailableSkusForRelease with both Value and DisplayName
$availableSkus = Get-AvailableSkusForRelease -SelectedReleaseValue $selectedReleaseValue -SelectedReleaseDisplayName $selectedReleaseDisplayName -State $State
$skuCombo.ItemsSource = $availableSkus
WriteLog "Update-WindowsSkuCombo: Set ItemsSource with $($availableSkus.Count) SKUs."
# Attempt to re-select the previous SKU, or "Pro", or the first available
if ($null -ne $previousSelectedSku -and $availableSkus -contains $previousSelectedSku) {
$skuCombo.SelectedItem = $previousSelectedSku
WriteLog "Update-WindowsSkuCombo: Re-selected previous SKU '$previousSelectedSku'."
}
elseif ($availableSkus -contains "Pro") {
$skuCombo.SelectedItem = "Pro"
WriteLog "Update-WindowsSkuCombo: Selected default SKU 'Pro'."
}
elseif ($availableSkus.Count -gt 0) {
$skuCombo.SelectedIndex = 0
WriteLog "Update-WindowsSkuCombo: Selected first available SKU '$($skuCombo.SelectedItem)'."
}
else {
$skuCombo.SelectedIndex = -1 # No SKUs available
WriteLog "Update-WindowsSkuCombo: No SKUs available for Release '$selectedReleaseValue' (Display: '$selectedReleaseDisplayName')."
}
}
# Function to refresh the Windows Architecture ComboBox based on selected release, version, and SKU
function Update-WindowsArchCombo {
param(
[Parameter(Mandatory = $true)]
[psobject]$State
)
$archCombo = $State.Controls.cmbWindowsArch
if (-not $archCombo) {
WriteLog "Update-WindowsArchCombo: Architecture ComboBox not found."
return
}
$previousSelectedArch = $archCombo.SelectedItem
# Start with a safe, common default
$availableArchitectures = @('x64')
$releaseItem = $State.Controls.cmbWindowsRelease.SelectedItem
$versionItem = $State.Controls.cmbWindowsVersion.SelectedItem
$skuItem = $State.Controls.cmbWindowsSKU.SelectedItem
if ($null -eq $releaseItem) {
WriteLog "Update-WindowsArchCombo: No release selected. Defaulting to x64."
$archCombo.ItemsSource = $availableArchitectures
$archCombo.SelectedItem = 'x64'
return
}
$releaseDisplay = $releaseItem.Display
$versionValue = if ($null -ne $versionItem) { $versionItem } else { "" }
$skuValue = if ($null -ne $skuItem) { $skuItem } else { "" }
if ($releaseDisplay -like 'Windows Server*') {
# All servers are x64 only
$availableArchitectures = @('x64')
}
elseif ($releaseDisplay -like 'Windows 11*') {
if ($releaseDisplay -like '*LTSC*') {
# Windows 11 LTSC 2024
if ($skuValue -like 'IoT*') {
$availableArchitectures = @('x64', 'arm64')
}
else {
$availableArchitectures = @('x64')
}
}
else {
# Standard Windows 11
if ($versionValue -eq '24H2') {
$availableArchitectures = @('x64', 'arm64')
}
else {
# 22H2, 23H2
$availableArchitectures = @('x64')
}
}
}
elseif ($releaseDisplay -like 'Windows 10*') {
if ($releaseDisplay -like '*LTSB*' -or $releaseDisplay -like '*LTSC*') {
# Windows 10 LTSB 2016, LTSC 2019, LTSC 2021
if ($releaseDisplay -like '*2021*' -and $skuValue -like 'IoT*') {
$availableArchitectures = @('x64', 'arm64')
}
else {
# LTSB 2016, LTSC 2019, LTSC 2021 (non-IoT)
$availableArchitectures = @('x86', 'x64')
}
}
else {
# Standard Windows 10 (22H2)
$availableArchitectures = @('x86', 'x64')
}
}
$archCombo.ItemsSource = $availableArchitectures
if ($availableArchitectures -contains $previousSelectedArch) {
$archCombo.SelectedItem = $previousSelectedArch
}
elseif ($availableArchitectures -contains 'x64') {
$archCombo.SelectedItem = 'x64'
}
elseif ($availableArchitectures.Count -gt 0) {
$archCombo.SelectedIndex = 0
}
else {
$archCombo.SelectedIndex = -1
}
WriteLog "Update-WindowsArchCombo: Updated available architectures to ($($availableArchitectures -join ', ')). Selected: $($archCombo.SelectedItem)"
}
# Combined function to initialize the Release, Version, and SKU combos
function Get-WindowsSettingsCombos {
param(
[string]$isoPath,
[Parameter(Mandatory = $true)]
[psobject]$State
)
# Determine visibility for download-specific controls based on ISO path
$visibility = if (-not [string]::IsNullOrEmpty($isoPath) -and $isoPath.EndsWith('.iso', [System.StringComparison]::OrdinalIgnoreCase)) {
'Collapsed'
}
else {
'Visible'
}
# Set visibility for Language and Media Type controls
$State.Controls.WindowsLangStackPanel.Visibility = $visibility
$State.Controls.cmbWindowsLang.Visibility = $visibility
$State.Controls.MediaTypeStackPanel.Visibility = $visibility
$State.Controls.cmbMediaType.Visibility = $visibility
# Update Release combo first
Update-WindowsReleaseCombo -isoPath $isoPath -State $State
# Get the newly selected release value
$selectedReleaseValue = 11 # Default to 11 if selection is null
if ($null -ne $State.Controls.cmbWindowsRelease.SelectedItem) {
$selectedReleaseValue = $State.Controls.cmbWindowsRelease.SelectedItem.Value
}
# Update Version combo based on the selected release
Update-WindowsVersionCombo -selectedRelease $selectedReleaseValue -isoPath $isoPath -State $State
# Update SKU combo based on the selected release (now derives values internally)
Update-WindowsSkuCombo -State $State
# Finally, update the Architecture combo to match the initial state
Update-WindowsArchCombo -State $State
}
# Dynamic checkboxes for optional features in Windows Settings tab
function UpdateOptionalFeaturesString {
param(
[psobject]$State
)
$checkedFeatures = @()
foreach ($entry in $State.Controls.featureCheckBoxes.GetEnumerator()) {
if ($entry.Value.IsChecked) { $checkedFeatures += $entry.Key }
}
}
function BuildFeaturesGrid {
param (
[Parameter(Mandatory)]
[System.Windows.FrameworkElement]$parent,
[Parameter(Mandatory)]
[array]$allowedFeatures, # Pass the list of features explicitly
[Parameter(Mandatory = $true)]
[psobject]$State
)
$parent.Children.Clear()
$State.Controls.featureCheckBoxes.Clear() # Clear the tracking hashtable
$sortedFeatures = $allowedFeatures | Sort-Object
$rows = 10 # Define number of rows for layout
$columns = [math]::Ceiling($sortedFeatures.Count / $rows)
$featuresGrid = New-Object System.Windows.Controls.Grid
$featuresGrid.Margin = "0,5,0,5"
$featuresGrid.ShowGridLines = $false
# Define grid rows
for ($r = 0; $r -lt $rows; $r++) {
$rowDef = New-Object System.Windows.Controls.RowDefinition
$rowDef.Height = [System.Windows.GridLength]::Auto
$featuresGrid.RowDefinitions.Add($rowDef) | Out-Null
}
# Define grid columns
for ($c = 0; $c -lt $columns; $c++) {
$colDef = New-Object System.Windows.Controls.ColumnDefinition
$colDef.Width = [System.Windows.GridLength]::Auto
$featuresGrid.ColumnDefinitions.Add($colDef) | Out-Null
}
# Populate grid with checkboxes
for ($i = 0; $i -lt $sortedFeatures.Count; $i++) {
$featureName = $sortedFeatures[$i]
$colIndex = [int]([math]::Floor($i / $rows))
$rowIndex = $i % $rows
$chk = New-Object System.Windows.Controls.CheckBox
$chk.Content = $featureName
$chk.Margin = "5"
$chk.Add_Checked({
param($eventSource, $e)
$window = [System.Windows.Window]::GetWindow($eventSource)
if ($null -ne $window) {
UpdateOptionalFeaturesString -State $window.Tag
}
})
$chk.Add_Unchecked({
param($eventSource, $e)
$window = [System.Windows.Window]::GetWindow($eventSource)
if ($null -ne $window) {
UpdateOptionalFeaturesString -State $window.Tag
}
})
$State.Controls.featureCheckBoxes[$featureName] = $chk # Track the checkbox
[System.Windows.Controls.Grid]::SetRow($chk, $rowIndex)
[System.Windows.Controls.Grid]::SetColumn($chk, $colIndex)
$featuresGrid.Children.Add($chk) | Out-Null
}
$parent.Children.Add($featuresGrid) | Out-Null
}
# --------------------------------------------------------------------------
# SECTION: Module Export
# --------------------------------------------------------------------------
Export-ModuleMember -Function *