2057 lines
100 KiB
PowerShell
2057 lines
100 KiB
PowerShell
function Set-DeployCenterButtonStyle {
|
|
param(
|
|
[Parameter(Mandatory=$true)][System.Windows.Forms.Button]$Button,
|
|
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
|
|
[ValidateSet('Button','PrimaryButton','DangerButton')][string]$Role = 'Button'
|
|
)
|
|
$style = $Theme[$Role]
|
|
$Button.BackColor = $style.Background
|
|
$Button.ForeColor = $style.Foreground
|
|
$Button.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
|
|
$Button.UseVisualStyleBackColor = $false
|
|
$Button.FlatAppearance.BorderSize = 1
|
|
$Button.FlatAppearance.BorderColor = $style.Border
|
|
$Button.FlatAppearance.MouseOverBackColor = $style.HoverBackground
|
|
$Button.FlatAppearance.MouseDownBackColor = $style.PressedBackground
|
|
if (-not $Button.Enabled) {
|
|
$Button.BackColor = $style.DisabledBackground
|
|
$Button.ForeColor = $style.DisabledForeground
|
|
}
|
|
}
|
|
|
|
function New-DeployCenterComboBox {
|
|
param(
|
|
[Parameter(Mandatory=$true)][string]$Name,
|
|
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
|
|
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font,
|
|
[Parameter(Mandatory=$true)][AllowEmptyCollection()][string[]]$Items
|
|
)
|
|
|
|
$comboBox = New-Object System.Windows.Forms.ComboBox
|
|
$comboBox.Name = $Name
|
|
$comboBox.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList
|
|
$comboBox.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
|
|
$comboBox.BackColor = $Theme.PanelAlt
|
|
$comboBox.ForeColor = $Theme.Text
|
|
$comboBox.Font = $Font
|
|
$comboBox.IntegralHeight = $false
|
|
$comboBox.MaxDropDownItems = 12
|
|
foreach ($item in $Items) { [void]$comboBox.Items.Add((Get-DeployCenterText -Text $item)) }
|
|
return $comboBox
|
|
}
|
|
|
|
function Set-DeployCenterComboBoxTheme {
|
|
param(
|
|
[Parameter(Mandatory=$true)][System.Windows.Forms.ComboBox]$ComboBox,
|
|
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme
|
|
)
|
|
$ComboBox.BackColor = $Theme.PanelAlt
|
|
$ComboBox.ForeColor = $Theme.Text
|
|
}
|
|
|
|
function New-DeployCenterPartitionProfileSelector {
|
|
param(
|
|
[Parameter(Mandatory=$true)][System.Windows.Forms.GroupBox]$Parent,
|
|
[Parameter(Mandatory=$true)][string[]]$ProfileNames,
|
|
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
|
|
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font
|
|
)
|
|
|
|
$label = New-Object System.Windows.Forms.Label
|
|
$label.Text = Get-DeployCenterText -Text 'Partitioning profile'
|
|
$label.Size = New-Object System.Drawing.Size(240, 17)
|
|
$label.ForeColor = $Theme.Text
|
|
$label.BackColor = [System.Drawing.Color]::Transparent
|
|
$label.Font = $Font
|
|
|
|
$comboBox = New-DeployCenterComboBox -Name 'cmbPartitionProfile' -Theme $Theme -Font $Font -Items $ProfileNames
|
|
$Parent.Controls.Add($label)
|
|
$Parent.Controls.Add($comboBox)
|
|
Set-DeployCenterPartitionProfileSelectorLayout -Label $label -ComboBox $comboBox -Width ($Parent.ClientSize.Width - 24)
|
|
|
|
return [pscustomobject]@{ Label = $label; ComboBox = $comboBox }
|
|
}
|
|
|
|
function Set-DeployCenterPartitionProfileSelectorLayout {
|
|
param(
|
|
[Parameter(Mandatory=$true)][System.Windows.Forms.Label]$Label,
|
|
[Parameter(Mandatory=$true)][System.Windows.Forms.ComboBox]$ComboBox,
|
|
[Parameter(Mandatory=$true)][int]$Width
|
|
)
|
|
$Label.Location = New-Object System.Drawing.Point(12, 143)
|
|
$ComboBox.Location = New-Object System.Drawing.Point(12, 161)
|
|
$ComboBox.Size = New-Object System.Drawing.Size([Math]::Max(250, $Width), 26)
|
|
}
|
|
|
|
function New-DeployCenterDeploymentControls {
|
|
param(
|
|
[Parameter(Mandatory=$true)][System.Windows.Forms.GroupBox]$Parent,
|
|
[Parameter(Mandatory=$true)][string[]]$ProfileNames,
|
|
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
|
|
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font
|
|
)
|
|
|
|
$diskLabel = New-Object System.Windows.Forms.Label
|
|
$diskLabel.Text = Get-DeployCenterText -Text 'Physical disk'
|
|
$diskLabel.Location = New-Object System.Drawing.Point(12, 20)
|
|
$diskLabel.Size = New-Object System.Drawing.Size(220, 18)
|
|
$diskLabel.Font = $Font
|
|
$diskLabel.ForeColor = $Theme.Text
|
|
$diskLabel.BackColor = [System.Drawing.Color]::Transparent
|
|
|
|
$diskCombo = New-DeployCenterComboBox -Name 'cmbDisk' -Theme $Theme -Font $Font -Items @()
|
|
$diskCombo.Location = New-Object System.Drawing.Point(12, 39)
|
|
$diskCombo.Size = New-Object System.Drawing.Size([Math]::Max(250, $Parent.ClientSize.Width - 24), 26)
|
|
$diskCombo.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right
|
|
|
|
$systemLabel = New-Object System.Windows.Forms.Label
|
|
$systemLabel.Text = Get-DeployCenterText -Text 'System to install'
|
|
$systemLabel.Location = New-Object System.Drawing.Point(12, 71)
|
|
$systemLabel.Size = New-Object System.Drawing.Size(240, 18)
|
|
$systemLabel.Font = $Font
|
|
$systemLabel.ForeColor = $Theme.Text
|
|
$systemLabel.BackColor = [System.Drawing.Color]::Transparent
|
|
|
|
$chooseSystemButton = New-Object System.Windows.Forms.Button
|
|
$chooseSystemButton.Name = 'btnChooseWindows'
|
|
$chooseSystemButton.Text = Get-DeployCenterText -Text 'Browse…'
|
|
$chooseSystemButton.Size = New-Object System.Drawing.Size(132, 28)
|
|
$chooseSystemButton.Location = New-Object System.Drawing.Point([Math]::Max(12, $Parent.ClientSize.Width - 144), 67)
|
|
$chooseSystemButton.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Right
|
|
Set-DeployCenterButtonStyle -Button $chooseSystemButton -Theme $Theme -Role 'Button'
|
|
|
|
$selectedSystemLabel = New-Object System.Windows.Forms.Label
|
|
$selectedSystemLabel.Name = 'lblSelectedWindows'
|
|
$selectedSystemLabel.Text = Get-DeployCenterText -Text 'No system selected'
|
|
$selectedSystemLabel.Location = New-Object System.Drawing.Point(12, 98)
|
|
$selectedSystemLabel.Size = New-Object System.Drawing.Size([Math]::Max(250, $Parent.ClientSize.Width - 24), 22)
|
|
$selectedSystemLabel.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right
|
|
$selectedSystemLabel.Font = $Font
|
|
$selectedSystemLabel.AutoEllipsis = $true
|
|
$selectedSystemLabel.ForeColor = $Theme.Secondary
|
|
$selectedSystemLabel.BackColor = [System.Drawing.Color]::Transparent
|
|
|
|
$driverStatusLabel = New-Object System.Windows.Forms.Label
|
|
$driverStatusLabel.Name = 'lblDriverStatus'
|
|
$driverStatusLabel.Text = Get-DeployCenterText -Text 'Drivers: select a Windows edition to search for a driver pack.'
|
|
$driverStatusLabel.Location = New-Object System.Drawing.Point(12, 119)
|
|
$driverStatusLabel.Size = New-Object System.Drawing.Size([Math]::Max(250, $Parent.ClientSize.Width - 24), 18)
|
|
$driverStatusLabel.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right
|
|
$driverStatusLabel.Font = $Font
|
|
$driverStatusLabel.AutoEllipsis = $true
|
|
$driverStatusLabel.ForeColor = $Theme.Secondary
|
|
$driverStatusLabel.BackColor = [System.Drawing.Color]::Transparent
|
|
$driverToolTip = New-Object System.Windows.Forms.ToolTip
|
|
$driverToolTip.SetToolTip($driverStatusLabel, $driverStatusLabel.Text)
|
|
|
|
$profileSelector = New-DeployCenterPartitionProfileSelector -Parent $Parent -ProfileNames $ProfileNames -Theme $Theme -Font $Font
|
|
$profileSelector.ComboBox.Enabled = $false
|
|
|
|
$unattendLabel = New-Object System.Windows.Forms.Label
|
|
$unattendLabel.Name = 'lblUnattendProfile'
|
|
$unattendLabel.Text = Get-DeployCenterText -Text 'First-run configuration'
|
|
$unattendLabel.Location = New-Object System.Drawing.Point(12, 194)
|
|
$unattendLabel.Size = New-Object System.Drawing.Size(320, 17)
|
|
$unattendLabel.Font = $Font
|
|
$unattendLabel.ForeColor = $Theme.Text
|
|
$unattendLabel.BackColor = [System.Drawing.Color]::Transparent
|
|
|
|
$unattendCombo = New-DeployCenterComboBox -Name 'cmbUnattendProfile' -Theme $Theme -Font $Font `
|
|
-Items @('Use OOBE')
|
|
$unattendCombo.Location = New-Object System.Drawing.Point(12, 211)
|
|
$unattendCombo.Size = New-Object System.Drawing.Size([Math]::Max(250, $Parent.ClientSize.Width - 24), 26)
|
|
$unattendCombo.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right
|
|
$unattendCombo.SelectedIndex = 0
|
|
|
|
$hostLabel = New-Object System.Windows.Forms.Label
|
|
$hostLabel.Name = 'lblHostName'
|
|
$hostLabel.Text = Get-DeployCenterText -Text "Computer name"
|
|
$hostLabel.Location = New-Object System.Drawing.Point(12, 244)
|
|
$hostLabel.Size = New-Object System.Drawing.Size(220, 24)
|
|
$hostLabel.Font = $Font
|
|
$hostLabel.ForeColor = $Theme.Text
|
|
$hostLabel.BackColor = [System.Drawing.Color]::Transparent
|
|
|
|
$hostTextBox = New-Object System.Windows.Forms.TextBox
|
|
$hostTextBox.Name = 'txtHostName'
|
|
$hostTextBox.Location = New-Object System.Drawing.Point([Math]::Max(244, $Parent.ClientSize.Width - 202), 241)
|
|
$hostTextBox.Size = New-Object System.Drawing.Size(190, 26)
|
|
$hostTextBox.MaxLength = 15
|
|
$hostTextBox.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Right
|
|
$hostTextBox.BackColor = $Theme.PanelAlt
|
|
$hostTextBox.ForeColor = $Theme.Text
|
|
$hostTextBox.Font = $Font
|
|
|
|
$hostLabel.Visible = $false
|
|
$hostTextBox.Visible = $false
|
|
$Parent.Controls.AddRange(@($diskLabel, $diskCombo, $systemLabel, $chooseSystemButton, $selectedSystemLabel, $driverStatusLabel, $unattendLabel, $unattendCombo, $hostLabel, $hostTextBox))
|
|
return [pscustomobject]@{
|
|
DiskComboBox = $diskCombo
|
|
ChooseSystemButton = $chooseSystemButton
|
|
SelectedSystemLabel = $selectedSystemLabel
|
|
DriverStatusLabel = $driverStatusLabel
|
|
DriverToolTip = $driverToolTip
|
|
PartitionProfileLabel = $profileSelector.Label
|
|
PartitionProfileComboBox = $profileSelector.ComboBox
|
|
UnattendProfileLabel = $unattendLabel
|
|
UnattendProfileComboBox = $unattendCombo
|
|
HostNameLabel = $hostLabel
|
|
HostNameTextBox = $hostTextBox
|
|
}
|
|
}
|
|
|
|
function Set-DeployCenterUnattendHostNameVisibility {
|
|
param([Parameter(Mandatory=$true)]$Controls, [Parameter(Mandatory=$true)][bool]$ShowHostName)
|
|
$Controls.HostNameLabel.Visible = $ShowHostName
|
|
$Controls.HostNameTextBox.Visible = $ShowHostName
|
|
}
|
|
|
|
function Set-DeployCenterUnattendProfileChoices {
|
|
param(
|
|
[Parameter(Mandatory=$true)][System.Windows.Forms.ComboBox]$ComboBox,
|
|
[Parameter(Mandatory=$true)][AllowEmptyCollection()][object[]]$Profiles,
|
|
[AllowNull()][string]$EditionName,
|
|
[Parameter(Mandatory=$true)][string]$OobeChoice
|
|
)
|
|
|
|
$isServerEdition = -not [string]::IsNullOrWhiteSpace($EditionName) -and
|
|
$EditionName.IndexOf('server', [StringComparison]::OrdinalIgnoreCase) -ge 0
|
|
$target = if ($isServerEdition) { 'Server' } else { 'Workstation' }
|
|
$choices = @($OobeChoice)
|
|
if (-not [string]::IsNullOrWhiteSpace($EditionName)) {
|
|
$choices += @($Profiles | Where-Object { [string]$_.Target -eq $target } | ForEach-Object { [string]$_.Name })
|
|
}
|
|
|
|
$previousChoice = [string]$ComboBox.SelectedItem
|
|
$ComboBox.BeginUpdate()
|
|
try {
|
|
$ComboBox.Items.Clear()
|
|
foreach ($choice in $choices) { [void]$ComboBox.Items.Add((Get-DeployCenterText -Text ([string]$choice))) }
|
|
$selectionIndex = $ComboBox.Items.IndexOf($previousChoice)
|
|
if ($selectionIndex -lt 0) { $selectionIndex = 0 }
|
|
$ComboBox.SelectedIndex = $selectionIndex
|
|
} finally {
|
|
$ComboBox.EndUpdate()
|
|
}
|
|
}
|
|
|
|
function Set-DeployCenterDeploymentControlsLayout {
|
|
param(
|
|
[Parameter(Mandatory=$true)][System.Windows.Forms.GroupBox]$Parent,
|
|
[Parameter(Mandatory=$true)]$Controls
|
|
)
|
|
$width = [Math]::Max(250, $Parent.ClientSize.Width - 24)
|
|
$Controls.DiskComboBox.Size = New-Object System.Drawing.Size($width, 26)
|
|
$Controls.ChooseSystemButton.Location = New-Object System.Drawing.Point([Math]::Max(12, $Parent.ClientSize.Width - 144), 67)
|
|
$Controls.SelectedSystemLabel.Size = New-Object System.Drawing.Size($width, 22)
|
|
$Controls.DriverStatusLabel.Size = New-Object System.Drawing.Size($width, 18)
|
|
Set-DeployCenterPartitionProfileSelectorLayout -Label $Controls.PartitionProfileLabel -ComboBox $Controls.PartitionProfileComboBox -Width $width
|
|
$Controls.UnattendProfileComboBox.Size = New-Object System.Drawing.Size($width, 26)
|
|
$Controls.HostNameTextBox.Location = New-Object System.Drawing.Point([Math]::Max(244, $Parent.ClientSize.Width - 202), 241)
|
|
$Controls.HostNameTextBox.Size = New-Object System.Drawing.Size(190, 26)
|
|
}
|
|
|
|
function New-DeployCenterLogWindow {
|
|
param(
|
|
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
|
|
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font
|
|
)
|
|
$form = New-Object System.Windows.Forms.Form
|
|
$form.Text = Get-DeployCenterText -Text 'DeployCenter log'
|
|
$form.StartPosition = 'CenterParent'
|
|
$form.Size = New-Object System.Drawing.Size(900, 500)
|
|
$form.MinimumSize = New-Object System.Drawing.Size(560, 300)
|
|
$form.ShowInTaskbar = $false
|
|
$form.BackColor = $Theme.Background
|
|
$form.ForeColor = $Theme.Text
|
|
$form.Font = $Font
|
|
|
|
$textBox = New-Object System.Windows.Forms.TextBox
|
|
$textBox.Name = 'txtLog'
|
|
$textBox.Location = New-Object System.Drawing.Point(12, 12)
|
|
$textBox.Size = New-Object System.Drawing.Size(860, 440)
|
|
$textBox.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right
|
|
$textBox.Multiline = $true
|
|
$textBox.ReadOnly = $true
|
|
$textBox.WordWrap = $false
|
|
$textBox.ScrollBars = [System.Windows.Forms.ScrollBars]::Both
|
|
$textBox.BackColor = $Theme.PanelAlt
|
|
$textBox.ForeColor = $Theme.Text
|
|
$textBox.BorderStyle = [System.Windows.Forms.BorderStyle]::FixedSingle
|
|
$textBox.Font = [System.Drawing.Font]::new('Consolas', [single]9, [System.Drawing.FontStyle]::Regular)
|
|
$form.Controls.Add($textBox)
|
|
|
|
$form.Add_FormClosing({ if ($_.CloseReason -eq [System.Windows.Forms.CloseReason]::UserClosing) { $_.Cancel = $true; $form.Hide() } }.GetNewClosure())
|
|
return [pscustomobject]@{ Form = $form; TextBox = $textBox }
|
|
}
|
|
|
|
function Show-DeployCenterPartitionSizingDialog {
|
|
param(
|
|
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Profile,
|
|
[Parameter(Mandatory=$true)][double]$AvailableGB,
|
|
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
|
|
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font,
|
|
[System.Windows.Forms.Form]$Owner = $null,
|
|
[System.Collections.IDictionary]$InitialValues = @{}
|
|
)
|
|
|
|
$inputs = @($Profile.Partitions | Where-Object { [string]$_.Size.Policy -eq 'UserInputGB' })
|
|
$remainder = @($Profile.Partitions | Where-Object { [string]$_.Size.Policy -eq 'RemainderAfterInputs' })[0]
|
|
if ($inputs.Count -eq 0 -or $null -eq $remainder) { return $null }
|
|
|
|
$minimumRemainder = [double]$remainder.Size.MinimumGB
|
|
$minimumInputs = 0.0
|
|
foreach ($inputDefinition in $inputs) { $minimumInputs += [double]$inputDefinition.Size.MinimumGB }
|
|
if ($AvailableGB -lt ($minimumInputs + $minimumRemainder)) {
|
|
Show-DeployCenterMessage `
|
|
-Message "The available space on this disk is insufficient for profile '$($Profile.Name)'." `
|
|
-Title 'Partitioning unavailable' -Icon Warning -Owner $Owner -Theme $Theme -Font $Font
|
|
return $null
|
|
}
|
|
|
|
$dialog = New-Object System.Windows.Forms.Form
|
|
$dialog.Text = Get-DeployCenterText -Text 'Partition sizes'
|
|
$dialog.StartPosition = if ($Owner) { 'CenterParent' } else { 'CenterScreen' }
|
|
$dialog.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
|
|
$dialog.ShowInTaskbar = $false
|
|
$dialog.MinimizeBox = $false
|
|
$dialog.MaximizeBox = $false
|
|
$dialog.BackColor = $Theme.Background
|
|
$dialog.ForeColor = $Theme.Text
|
|
$dialog.Font = $Font
|
|
$dialog.ClientSize = New-Object System.Drawing.Size(470, (152 + ($inputs.Count * 42)))
|
|
|
|
$capacityLabel = New-Object System.Windows.Forms.Label
|
|
$capacityLabel.Location = New-Object System.Drawing.Point(18, 16)
|
|
$capacityLabel.Size = New-Object System.Drawing.Size(430, 34)
|
|
$recoveryPartition = @($Profile.Partitions | Where-Object { [string]$_.Role -eq 'Recovery' }) | Select-Object -First 1
|
|
$winreSizeMB = if ($recoveryPartition) { [int]$recoveryPartition.Size.Value } else { 1024 }
|
|
$capacityLabel.Text = (Get-DeployCenterText -Text 'Estimated capacity after EFI, MSR and WinRE reservations: {0:N1} GB.`r`nConfigured WinRE size: {1} MB.') -f $AvailableGB, $winreSizeMB
|
|
$capacityLabel.ForeColor = $Theme.Text
|
|
$capacityLabel.BackColor = [System.Drawing.Color]::Transparent
|
|
$capacityLabel.Font = $Font
|
|
$dialog.Controls.Add($capacityLabel)
|
|
|
|
$inputControls = [System.Collections.Generic.List[object]]::new()
|
|
for ($i = 0; $i -lt $inputs.Count; $i++) {
|
|
$definition = $inputs[$i]
|
|
$rowY = 60 + ($i * 42)
|
|
|
|
$label = New-Object System.Windows.Forms.Label
|
|
$label.Location = New-Object System.Drawing.Point(18, ($rowY + 4))
|
|
$label.Size = New-Object System.Drawing.Size(250, 26)
|
|
$label.Text = Get-DeployCenterText -Text ([string]$definition.Size.Label)
|
|
$label.ForeColor = $Theme.Text
|
|
$label.BackColor = [System.Drawing.Color]::Transparent
|
|
$label.Font = $Font
|
|
$dialog.Controls.Add($label)
|
|
|
|
$otherMinimums = 0.0
|
|
for ($j = 0; $j -lt $inputs.Count; $j++) {
|
|
if ($j -ne $i) { $otherMinimums += [double]$inputs[$j].Size.MinimumGB }
|
|
}
|
|
$maximum = [math]::Floor($AvailableGB - $minimumRemainder - $otherMinimums)
|
|
$minimum = [double]$definition.Size.MinimumGB
|
|
$numeric = New-Object System.Windows.Forms.NumericUpDown
|
|
$numeric.Location = New-Object System.Drawing.Point(292, $rowY)
|
|
$numeric.Size = New-Object System.Drawing.Size(150, 28)
|
|
$numeric.Minimum = [decimal]$minimum
|
|
$numeric.Maximum = [decimal]$maximum
|
|
$numeric.DecimalPlaces = 0
|
|
$numeric.Increment = 1
|
|
$numeric.BackColor = $Theme.PanelAlt
|
|
$numeric.ForeColor = $Theme.Text
|
|
$numeric.Font = $Font
|
|
|
|
$initialValue = $null
|
|
if ($InitialValues.Contains([string]$definition.Role)) { $initialValue = [double]$InitialValues[[string]$definition.Role] }
|
|
if ($null -eq $initialValue) {
|
|
$initialValue = [math]::Floor($AvailableGB * ([double]$definition.Size.DefaultPercent / 100))
|
|
}
|
|
$initialValue = [math]::Max($minimum, [math]::Min($maximum, $initialValue))
|
|
$numeric.Value = [decimal]$initialValue
|
|
$inputControls.Add([pscustomobject]@{ Role = [string]$definition.Role; Control = $numeric })
|
|
$dialog.Controls.Add($numeric)
|
|
}
|
|
|
|
$remainderLabel = New-Object System.Windows.Forms.Label
|
|
$remainderLabel.Location = New-Object System.Drawing.Point(18, (62 + ($inputs.Count * 42)))
|
|
$remainderLabel.Size = New-Object System.Drawing.Size(430, 26)
|
|
$remainderLabel.ForeColor = $Theme.Secondary
|
|
$remainderLabel.BackColor = [System.Drawing.Color]::Transparent
|
|
$remainderLabel.Font = $Font
|
|
$dialog.Controls.Add($remainderLabel)
|
|
|
|
$buttonPanel = New-Object System.Windows.Forms.Panel
|
|
$buttonPanel.Dock = [System.Windows.Forms.DockStyle]::Bottom
|
|
$buttonPanel.Height = 58
|
|
$buttonPanel.BackColor = $Theme.PanelAlt
|
|
$dialog.Controls.Add($buttonPanel)
|
|
|
|
$cancelButton = New-Object System.Windows.Forms.Button
|
|
$cancelButton.Text = Get-DeployCenterText -Text 'Cancel'
|
|
$cancelButton.Location = New-Object System.Drawing.Point(258, 10)
|
|
$cancelButton.Size = New-Object System.Drawing.Size(92, 36)
|
|
$cancelButton.Font = $Font
|
|
$cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
|
|
Set-DeployCenterButtonStyle -Button $cancelButton -Theme $Theme -Role 'Button'
|
|
$buttonPanel.Controls.Add($cancelButton)
|
|
|
|
$validateButton = New-Object System.Windows.Forms.Button
|
|
$validateButton.Text = Get-DeployCenterText -Text 'Select'
|
|
$validateButton.Location = New-Object System.Drawing.Point(358, 10)
|
|
$validateButton.Size = New-Object System.Drawing.Size(92, 36)
|
|
$validateButton.Font = $Font
|
|
Set-DeployCenterButtonStyle -Button $validateButton -Theme $Theme -Role 'PrimaryButton'
|
|
$buttonPanel.Controls.Add($validateButton)
|
|
|
|
$dialog.Tag = [pscustomobject]@{ InputControls = $inputControls; AvailableGB = $AvailableGB }
|
|
$updateRemainder = {
|
|
$sum = 0.0
|
|
foreach ($inputControl in $dialog.Tag.InputControls) {
|
|
$inputValue = [double]$inputControl.Control.Value
|
|
$typedValue = 0.0
|
|
if ([double]::TryParse([string]$inputControl.Control.Text, [ref]$typedValue)) { $inputValue = $typedValue }
|
|
$sum += $inputValue
|
|
}
|
|
$remainingGB = $dialog.Tag.AvailableGB - $sum
|
|
$remainderDrive = [string]$remainder.DriveLetter
|
|
$remainderName = if ($remainderDrive) { "$remainderDrive`:" } else { [string]$remainder.Name }
|
|
$remainderLabel.Text = (Get-DeployCenterText -Text '{0}: {1:N1} GB (remaining space)') -f $remainderName, $remainingGB
|
|
$remainderLabel.ForeColor = if ($remainingGB -lt $minimumRemainder) { $Theme.Danger } else { $Theme.Secondary }
|
|
$validateButton.Enabled = ($remainingGB -ge $minimumRemainder)
|
|
}.GetNewClosure()
|
|
foreach ($inputControl in $inputControls) {
|
|
$inputControl.Control.Add_ValueChanged($updateRemainder)
|
|
$inputControl.Control.Add_TextChanged($updateRemainder)
|
|
}
|
|
& $updateRemainder
|
|
|
|
$validateButton.Add_Click({
|
|
$inputValues = @{}
|
|
foreach ($inputControl in $dialog.Tag.InputControls) { $inputValues[$inputControl.Role] = [double]$inputControl.Control.Value }
|
|
$sum = 0.0
|
|
foreach ($value in $inputValues.Values) { $sum += [double]$value }
|
|
$dialog.Tag = [pscustomobject]@{
|
|
Inputs = $inputValues
|
|
RemainderRole = [string]$remainder.Role
|
|
RemainderGB = [math]::Round(($dialog.Tag.AvailableGB - $sum), 1)
|
|
AvailableGB = $dialog.Tag.AvailableGB
|
|
}
|
|
$dialog.DialogResult = [System.Windows.Forms.DialogResult]::OK
|
|
$dialog.Close()
|
|
})
|
|
$dialog.AcceptButton = $validateButton
|
|
$dialog.CancelButton = $cancelButton
|
|
|
|
if ($Owner) { $result = $dialog.ShowDialog($Owner) } else { $result = $dialog.ShowDialog() }
|
|
$selection = if ($result -eq [System.Windows.Forms.DialogResult]::OK) { $dialog.Tag } else { $null }
|
|
$dialog.Dispose()
|
|
return $selection
|
|
}
|
|
|
|
function Show-DeployCenterMessage {
|
|
param(
|
|
[Parameter(Mandatory=$true)][string]$Message,
|
|
[Parameter(Mandatory=$true)][string]$Title,
|
|
[ValidateSet('Information','Warning','Error')][string]$Icon = 'Information',
|
|
[System.Windows.Forms.Form]$Owner = $null,
|
|
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
|
|
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font
|
|
)
|
|
|
|
$messageForm = New-Object System.Windows.Forms.Form
|
|
$messageForm.Text = Get-DeployCenterText -Text $Title
|
|
$messageForm.StartPosition = if ($Owner) { 'CenterParent' } else { 'CenterScreen' }
|
|
$messageForm.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
|
|
$messageForm.ShowInTaskbar = $false
|
|
$messageForm.MinimizeBox = $false
|
|
$messageForm.MaximizeBox = $false
|
|
$messageForm.BackColor = $Theme.Background
|
|
$messageForm.ForeColor = $Theme.Text
|
|
$messageForm.Font = $Font
|
|
|
|
$messageWidth = 390
|
|
$textSize = [System.Windows.Forms.TextRenderer]::MeasureText(
|
|
$Message,
|
|
$Font,
|
|
(New-Object System.Drawing.Size($messageWidth, 0)),
|
|
[System.Windows.Forms.TextFormatFlags]::WordBreak
|
|
)
|
|
$messageHeight = [Math]::Max(42, $textSize.Height + 8)
|
|
$messageForm.ClientSize = New-Object System.Drawing.Size(470, ($messageHeight + 94))
|
|
|
|
$iconLabel = New-Object System.Windows.Forms.Label
|
|
$iconLabel.Location = New-Object System.Drawing.Point(18, 24)
|
|
$iconLabel.Size = New-Object System.Drawing.Size(34, 34)
|
|
$iconLabel.Text = if ($Icon -eq 'Information') { 'i' } else { '!' }
|
|
$iconLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
|
|
$iconLabel.Font = New-Object System.Drawing.Font('Segoe UI', [single]16, [System.Drawing.FontStyle]::Bold)
|
|
$iconLabel.ForeColor = if ($Icon -eq 'Error') { $Theme.Danger } else { $Theme.Accent }
|
|
$iconLabel.BackColor = [System.Drawing.Color]::Transparent
|
|
$messageForm.Controls.Add($iconLabel)
|
|
|
|
$messageLabel = New-Object System.Windows.Forms.Label
|
|
$messageLabel.Location = New-Object System.Drawing.Point(64, 20)
|
|
$messageLabel.Size = New-Object System.Drawing.Size($messageWidth, $messageHeight)
|
|
$messageLabel.Text = Get-DeployCenterText -Text $Message
|
|
$messageLabel.ForeColor = $Theme.Text
|
|
$messageLabel.BackColor = [System.Drawing.Color]::Transparent
|
|
$messageLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleLeft
|
|
$messageLabel.Font = $Font
|
|
$messageForm.Controls.Add($messageLabel)
|
|
|
|
$buttonPanel = New-Object System.Windows.Forms.Panel
|
|
$buttonPanel.Dock = [System.Windows.Forms.DockStyle]::Bottom
|
|
$buttonPanel.Height = 58
|
|
$buttonPanel.BackColor = $Theme.PanelAlt
|
|
$messageForm.Controls.Add($buttonPanel)
|
|
|
|
$okButton = New-Object System.Windows.Forms.Button
|
|
$okButton.Text = Get-DeployCenterText -Text 'OK'
|
|
$okButton.Location = New-Object System.Drawing.Point(362, 10)
|
|
$okButton.Size = New-Object System.Drawing.Size(92, 36)
|
|
$okButton.Font = $Font
|
|
$okButton.Cursor = [System.Windows.Forms.Cursors]::Hand
|
|
$okButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
|
|
Set-DeployCenterButtonStyle -Button $okButton -Theme $Theme -Role 'PrimaryButton'
|
|
$buttonPanel.Controls.Add($okButton)
|
|
$messageForm.AcceptButton = $okButton
|
|
$messageForm.CancelButton = $okButton
|
|
|
|
if ($Owner) {
|
|
[void]$messageForm.ShowDialog($Owner)
|
|
} else {
|
|
[void]$messageForm.ShowDialog()
|
|
}
|
|
$messageForm.Dispose()
|
|
}
|
|
|
|
function Show-DeployCenterDeploymentComplete {
|
|
param(
|
|
[Parameter(Mandatory=$true)][string]$Message,
|
|
[Parameter(Mandatory=$true)][System.Windows.Forms.Form]$Owner,
|
|
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
|
|
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font,
|
|
[bool]$RestartByDefault = $true
|
|
)
|
|
|
|
$dialog = New-Object System.Windows.Forms.Form
|
|
$dialog.Text = Get-DeployCenterText -Text 'Deployment complete'
|
|
$dialog.StartPosition = 'CenterParent'
|
|
$dialog.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
|
|
$dialog.ShowInTaskbar = $false
|
|
$dialog.MinimizeBox = $false
|
|
$dialog.MaximizeBox = $false
|
|
$dialog.BackColor = $Theme.Background
|
|
$dialog.ForeColor = $Theme.Text
|
|
$dialog.Font = $Font
|
|
|
|
$messageWidth = 390
|
|
$textSize = [System.Windows.Forms.TextRenderer]::MeasureText(
|
|
$Message, $Font, (New-Object System.Drawing.Size($messageWidth, 0)),
|
|
[System.Windows.Forms.TextFormatFlags]::WordBreak
|
|
)
|
|
$messageHeight = [Math]::Max(42, $textSize.Height + 8)
|
|
$dialog.ClientSize = New-Object System.Drawing.Size(470, ($messageHeight + 126))
|
|
|
|
$iconLabel = New-Object System.Windows.Forms.Label
|
|
$iconLabel.Location = New-Object System.Drawing.Point(18, 24)
|
|
$iconLabel.Size = New-Object System.Drawing.Size(34, 34)
|
|
$iconLabel.Text = 'i'
|
|
$iconLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
|
|
$iconLabel.Font = New-Object System.Drawing.Font('Segoe UI', [single]16, [System.Drawing.FontStyle]::Bold)
|
|
$iconLabel.ForeColor = $Theme.Accent
|
|
$iconLabel.BackColor = [System.Drawing.Color]::Transparent
|
|
$dialog.Controls.Add($iconLabel)
|
|
|
|
$messageLabel = New-Object System.Windows.Forms.Label
|
|
$messageLabel.Location = New-Object System.Drawing.Point(64, 20)
|
|
$messageLabel.Size = New-Object System.Drawing.Size($messageWidth, $messageHeight)
|
|
$messageLabel.Text = Get-DeployCenterText -Text $Message
|
|
$messageLabel.ForeColor = $Theme.Text
|
|
$messageLabel.BackColor = [System.Drawing.Color]::Transparent
|
|
$messageLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleLeft
|
|
$messageLabel.Font = $Font
|
|
$dialog.Controls.Add($messageLabel)
|
|
|
|
$restartCheckBox = New-Object System.Windows.Forms.CheckBox
|
|
$restartCheckBox.Text = Get-DeployCenterText -Text 'Restart this computer when closing'
|
|
$restartCheckBox.Location = New-Object System.Drawing.Point(64, ($messageHeight + 34))
|
|
$restartCheckBox.Size = New-Object System.Drawing.Size(380, 26)
|
|
$restartCheckBox.Checked = $RestartByDefault
|
|
$restartCheckBox.ForeColor = $Theme.Text
|
|
$restartCheckBox.BackColor = [System.Drawing.Color]::Transparent
|
|
$restartCheckBox.Font = $Font
|
|
$dialog.Controls.Add($restartCheckBox)
|
|
|
|
$buttonPanel = New-Object System.Windows.Forms.Panel
|
|
$buttonPanel.Dock = [System.Windows.Forms.DockStyle]::Bottom
|
|
$buttonPanel.Height = 58
|
|
$buttonPanel.BackColor = $Theme.PanelAlt
|
|
$dialog.Controls.Add($buttonPanel)
|
|
|
|
$okButton = New-Object System.Windows.Forms.Button
|
|
$okButton.Text = Get-DeployCenterText -Text 'OK'
|
|
$okButton.Location = New-Object System.Drawing.Point(362, 10)
|
|
$okButton.Size = New-Object System.Drawing.Size(92, 36)
|
|
$okButton.Font = $Font
|
|
$okButton.Cursor = [System.Windows.Forms.Cursors]::Hand
|
|
$okButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
|
|
Set-DeployCenterButtonStyle -Button $okButton -Theme $Theme -Role 'PrimaryButton'
|
|
$buttonPanel.Controls.Add($okButton)
|
|
$dialog.AcceptButton = $okButton
|
|
$dialog.CancelButton = $okButton
|
|
|
|
try {
|
|
$result = $dialog.ShowDialog($Owner)
|
|
return ($result -eq [System.Windows.Forms.DialogResult]::OK -and $restartCheckBox.Checked)
|
|
} finally {
|
|
$dialog.Dispose()
|
|
}
|
|
}
|
|
|
|
function Show-DeployCenterConfirmation {
|
|
param(
|
|
[Parameter(Mandatory=$true)][string]$Title,
|
|
[Parameter(Mandatory=$true)][string]$Message,
|
|
[Parameter(Mandatory=$true)][System.Windows.Forms.Form]$Owner,
|
|
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
|
|
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font
|
|
)
|
|
|
|
$dialog = New-Object System.Windows.Forms.Form
|
|
$dialog.Text = Get-DeployCenterText -Text $Title
|
|
$dialog.StartPosition = 'CenterParent'
|
|
$dialog.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
|
|
$dialog.ShowInTaskbar = $false
|
|
$dialog.MinimizeBox = $false
|
|
$dialog.MaximizeBox = $false
|
|
$dialog.BackColor = $Theme.Background
|
|
$dialog.ForeColor = $Theme.Text
|
|
$dialog.Font = $Font
|
|
|
|
$dialogWidth = 570
|
|
$dialogMargin = 18
|
|
$labelTop = 18
|
|
$labelWidth = $dialogWidth - (2 * $dialogMargin)
|
|
$labelHeight = [Math]::Max(120, [System.Windows.Forms.TextRenderer]::MeasureText(
|
|
$Message,
|
|
$Font,
|
|
(New-Object System.Drawing.Size($labelWidth, 0)),
|
|
[System.Windows.Forms.TextFormatFlags]::WordBreak
|
|
).Height + 12)
|
|
$buttonTop = $labelTop + $labelHeight + 18
|
|
$dialog.ClientSize = New-Object System.Drawing.Size($dialogWidth, ($buttonTop + 36 + 16))
|
|
|
|
$label = New-Object System.Windows.Forms.Label
|
|
$label.Location = New-Object System.Drawing.Point($dialogMargin, $labelTop)
|
|
$label.Size = New-Object System.Drawing.Size($labelWidth, $labelHeight)
|
|
$label.Text = Get-DeployCenterText -Text $Message
|
|
$label.ForeColor = $Theme.Text
|
|
$label.BackColor = [System.Drawing.Color]::Transparent
|
|
$label.Font = $Font
|
|
$dialog.Controls.Add($label)
|
|
|
|
$cancel = New-Object System.Windows.Forms.Button
|
|
$cancel.Text = Get-DeployCenterText -Text 'Cancel'
|
|
$cancelWidth = 100
|
|
$confirmWidth = 134
|
|
$buttonGap = 10
|
|
$confirmLeft = $dialogWidth - $dialogMargin - $confirmWidth
|
|
$cancel.Location = New-Object System.Drawing.Point(($confirmLeft - $buttonGap - $cancelWidth), $buttonTop)
|
|
$cancel.Size = New-Object System.Drawing.Size($cancelWidth, 36)
|
|
$cancel.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
|
|
Set-DeployCenterButtonStyle -Button $cancel -Theme $Theme -Role 'Button'
|
|
$dialog.Controls.Add($cancel)
|
|
|
|
$confirm = New-Object System.Windows.Forms.Button
|
|
$confirm.Text = Get-DeployCenterText -Text 'Erase and install'
|
|
$confirm.Location = New-Object System.Drawing.Point($confirmLeft, $buttonTop)
|
|
$confirm.Size = New-Object System.Drawing.Size($confirmWidth, 36)
|
|
$confirm.DialogResult = [System.Windows.Forms.DialogResult]::Yes
|
|
Set-DeployCenterButtonStyle -Button $confirm -Theme $Theme -Role 'DangerButton'
|
|
$dialog.Controls.Add($confirm)
|
|
$dialog.CancelButton = $cancel
|
|
$dialog.AcceptButton = $confirm
|
|
|
|
return ($dialog.ShowDialog($Owner) -eq [System.Windows.Forms.DialogResult]::Yes)
|
|
}
|
|
|
|
function Add-DeployCenterApplicationHeader {
|
|
param(
|
|
[Parameter(Mandatory=$true)][System.Windows.Forms.Form]$TargetForm,
|
|
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
|
|
[Parameter(Mandatory=$true)][System.Drawing.Font]$TitleFont,
|
|
[Parameter(Mandatory=$true)][System.Drawing.Font]$SubtitleFont,
|
|
[Parameter(Mandatory=$true)][System.Drawing.Font]$ThemeFont,
|
|
[AllowNull()][System.Drawing.Image]$LogoImage,
|
|
[string[]]$ThemeNames = @(),
|
|
[string]$SelectedTheme = '',
|
|
[bool]$ShowThemeSelector = $true,
|
|
[bool]$ThemeSelectorEnabled = $true,
|
|
[bool]$CloneLogo = $false
|
|
)
|
|
|
|
$logo = $null
|
|
$titleX = 30
|
|
if ($null -ne $LogoImage) {
|
|
$logo = New-Object System.Windows.Forms.PictureBox
|
|
$logo.Location = New-Object System.Drawing.Point(30, 20)
|
|
$logo.Size = New-Object System.Drawing.Size(64, 64)
|
|
$logo.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::Zoom
|
|
$logo.BackColor = [System.Drawing.Color]::Transparent
|
|
$logo.Image = if ($CloneLogo) { $LogoImage.Clone() } else { $LogoImage }
|
|
$TargetForm.Controls.Add($logo)
|
|
$titleX = 110
|
|
}
|
|
|
|
$title = New-Label -Text 'DeployCenter' -X $titleX -Y 17 -Width 600 -Height 42 `
|
|
-Theme $Theme -Font $TitleFont -Color $Theme.Text
|
|
$subtitle = New-Label -Text (Get-DeployCenterText -Text 'Windows deployment environment') `
|
|
-X ($titleX + 4) -Y 61 -Width 650 -Height 24 -Theme $Theme -Font $SubtitleFont -Color $Theme.Secondary
|
|
$TargetForm.Controls.Add($title)
|
|
$TargetForm.Controls.Add($subtitle)
|
|
|
|
$themeSelector = $null
|
|
if ($ShowThemeSelector) {
|
|
$themeSelector = New-Object System.Windows.Forms.ComboBox
|
|
$themeSelector.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList
|
|
$themeSelector.DrawMode = [System.Windows.Forms.DrawMode]::OwnerDrawFixed
|
|
$themeSelector.ItemHeight = 24
|
|
$themeSelector.Font = $ThemeFont
|
|
$themeSelector.Width = 190
|
|
$themeSelector.Height = 32
|
|
$themeSelector.Location = New-Object System.Drawing.Point([Math]::Max(30, ($TargetForm.ClientSize.Width - 220)), 29)
|
|
$themeSelector.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Right
|
|
foreach ($themeName in $ThemeNames) { [void]$themeSelector.Items.Add($themeName) }
|
|
$themeSelector.SelectedItem = $SelectedTheme
|
|
$themeSelector.BackColor = $Theme.Panel
|
|
$themeSelector.ForeColor = $Theme.Text
|
|
$themeSelector.Enabled = $ThemeSelectorEnabled
|
|
$themeSelector.Add_DrawItem({
|
|
param($sender, $e)
|
|
$itemText = if ($e.Index -lt 0) { [string]$sender.Text } else { [string]$sender.Items[$e.Index] }
|
|
$isSelected = (($e.State -band [System.Windows.Forms.DrawItemState]::Selected) -ne 0)
|
|
$backgroundColor = if ($isSelected) { $script:Theme.Accent } else { $script:Theme.Panel }
|
|
$foregroundColor = if ($isSelected) { [System.Drawing.Color]::White } else { $script:Theme.Text }
|
|
$backgroundBrush = New-Object System.Drawing.SolidBrush($backgroundColor)
|
|
try { $e.Graphics.FillRectangle($backgroundBrush, $e.Bounds) }
|
|
finally { $backgroundBrush.Dispose() }
|
|
if (-not [string]::IsNullOrEmpty($itemText)) {
|
|
$textBounds = $e.Bounds
|
|
$textBounds.X += 4
|
|
[System.Windows.Forms.TextRenderer]::DrawText(
|
|
$e.Graphics, $itemText, $sender.Font, $textBounds, $foregroundColor,
|
|
([System.Windows.Forms.TextFormatFlags]::Left -bor [System.Windows.Forms.TextFormatFlags]::VerticalCenter -bor [System.Windows.Forms.TextFormatFlags]::EndEllipsis)
|
|
)
|
|
}
|
|
$e.DrawFocusRectangle()
|
|
})
|
|
$TargetForm.Controls.Add($themeSelector)
|
|
$themeSelector.BringToFront()
|
|
}
|
|
|
|
return [pscustomobject]@{ Logo = $logo; Title = $title; Subtitle = $subtitle; ThemeSelector = $themeSelector }
|
|
}
|
|
|
|
function New-DeployCenterDeploymentProgress {
|
|
param(
|
|
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
|
|
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font,
|
|
[Parameter(Mandatory=$true)][System.Drawing.Font]$FooterFont,
|
|
[Parameter(Mandatory=$true)][AllowEmptyCollection()][string[]]$Steps,
|
|
[Parameter(Mandatory=$true)][System.Windows.Forms.Form]$Owner,
|
|
[AllowNull()][System.Drawing.Image]$LogoImage,
|
|
[Parameter(Mandatory=$true)][System.Drawing.Font]$TitleFont,
|
|
[Parameter(Mandatory=$true)][System.Drawing.Font]$SubtitleFont,
|
|
[bool]$Windowed = $false,
|
|
[AllowNull()][string]$IPv4,
|
|
[AllowNull()][string]$VncPassword
|
|
)
|
|
|
|
$form = New-Object System.Windows.Forms.Form
|
|
$form.Text = Get-DeployCenterText -Text 'Windows deployment'
|
|
$form.StartPosition = if ($Windowed) { 'CenterParent' } else { 'Manual' }
|
|
$form.FormBorderStyle = if ($Windowed) { [System.Windows.Forms.FormBorderStyle]::FixedDialog } else { [System.Windows.Forms.FormBorderStyle]::None }
|
|
$form.ControlBox = $false
|
|
$form.ShowInTaskbar = $false
|
|
$screen = [System.Windows.Forms.Screen]::FromControl($Owner)
|
|
$workingArea = $screen.WorkingArea
|
|
if ($Windowed) {
|
|
$clientWidth = [Math]::Min(($Owner.ClientSize.Width - 40), ($workingArea.Width - 48))
|
|
$clientHeight = [Math]::Min(($Owner.ClientSize.Height - 28), ($workingArea.Height - 28))
|
|
$clientWidth = [Math]::Max(420, $clientWidth)
|
|
$clientHeight = [Math]::Max(360, $clientHeight)
|
|
} else {
|
|
$clientWidth = $screen.Bounds.Width
|
|
$clientHeight = $screen.Bounds.Height
|
|
$form.Bounds = $screen.Bounds
|
|
}
|
|
$form.ClientSize = New-Object System.Drawing.Size($clientWidth, $clientHeight)
|
|
$form.BackColor = $Theme.Background
|
|
$form.ForeColor = $Theme.Text
|
|
$form.Font = $Font
|
|
|
|
[void](Add-DeployCenterApplicationHeader -TargetForm $form -Theme $Theme `
|
|
-TitleFont $TitleFont -SubtitleFont $SubtitleFont -ThemeFont $Font `
|
|
-LogoImage $LogoImage -ShowThemeSelector:$false -CloneLogo:$true)
|
|
|
|
$heading = New-Object System.Windows.Forms.Label
|
|
$heading.Location = New-Object System.Drawing.Point(30, 95)
|
|
$heading.Size = New-Object System.Drawing.Size(($clientWidth - 60), 22)
|
|
$heading.Text = Get-DeployCenterText -Text 'Deployment steps'
|
|
$heading.ForeColor = $Theme.Text
|
|
$heading.BackColor = [System.Drawing.Color]::Transparent
|
|
$heading.Font = $Font
|
|
$form.Controls.Add($heading)
|
|
|
|
$list = New-Object System.Windows.Forms.Panel
|
|
$list.Location = New-Object System.Drawing.Point(12, 120)
|
|
$footerHeight = 26
|
|
$list.Size = New-Object System.Drawing.Size(($clientWidth - 24), ($clientHeight - 120 - $footerHeight - 12))
|
|
$list.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right -bor [System.Windows.Forms.AnchorStyles]::Bottom
|
|
$list.AutoScroll = $true
|
|
$list.BackColor = $Theme.Background
|
|
$form.Controls.Add($list)
|
|
|
|
$rows = @()
|
|
$rowTop = 0
|
|
foreach ($step in $Steps) {
|
|
$rowPanel = New-Object System.Windows.Forms.Panel
|
|
$rowPanel.Location = New-Object System.Drawing.Point(0, $rowTop)
|
|
$listWidth = $list.ClientSize.Width
|
|
$rowPanel.Size = New-Object System.Drawing.Size(($listWidth - 8), 20)
|
|
$rowPanel.BackColor = $Theme.Background
|
|
|
|
$name = New-Object System.Windows.Forms.Label
|
|
$name.Location = New-Object System.Drawing.Point(6, 0)
|
|
$name.Size = New-Object System.Drawing.Size([Math]::Max(140, ($listWidth - 380)), 20)
|
|
$stepLabel = if ([string]$step -eq 'Applying Windows to ') {
|
|
Get-DeployCenterText -Text 'Applying Windows to target partition…'
|
|
} else {
|
|
Get-DeployCenterText -Text $step
|
|
}
|
|
$name.Text = $stepLabel
|
|
$name.ForeColor = $Theme.Text
|
|
$name.BackColor = [System.Drawing.Color]::Transparent
|
|
$name.Font = $Font
|
|
$name.AutoEllipsis = $true
|
|
$rowPanel.Controls.Add($name)
|
|
|
|
$state = New-Object System.Windows.Forms.Label
|
|
$state.Location = New-Object System.Drawing.Point(($listWidth - 158), 0)
|
|
$state.Size = New-Object System.Drawing.Size(145, 20)
|
|
$state.TextAlign = [System.Drawing.ContentAlignment]::MiddleRight
|
|
$state.ForeColor = $Theme.Secondary
|
|
$state.BackColor = [System.Drawing.Color]::Transparent
|
|
$state.Font = $Font
|
|
$state.Text = Get-DeployCenterText -Text 'Upcoming'
|
|
$rowPanel.Controls.Add($state)
|
|
|
|
$bar = New-Object System.Windows.Forms.ProgressBar
|
|
$bar.Location = New-Object System.Drawing.Point(($listWidth - 350), 4)
|
|
$bar.Size = New-Object System.Drawing.Size(180, 12)
|
|
$bar.Style = [System.Windows.Forms.ProgressBarStyle]::Continuous
|
|
$bar.Visible = $false
|
|
$rowPanel.Controls.Add($bar)
|
|
$list.Controls.Add($rowPanel)
|
|
$rows += [pscustomobject]@{ Key = [string]$step; Name = $name; Panel = $rowPanel; State = $state; Bar = $bar; Started = $null; Complete = $false }
|
|
$rowTop += 20
|
|
}
|
|
|
|
$completionPanel = New-Object System.Windows.Forms.Panel
|
|
$completionPanel.Dock = [System.Windows.Forms.DockStyle]::Bottom
|
|
$completionPanel.Height = 88
|
|
$completionPanel.BackColor = $Theme.PanelAlt
|
|
$completionPanel.Visible = $false
|
|
|
|
$completionLabel = New-Object System.Windows.Forms.Label
|
|
$completionLabel.Location = New-Object System.Drawing.Point(12, 5)
|
|
$completionLabel.Size = New-Object System.Drawing.Size(($clientWidth - 40), 34)
|
|
$completionLabel.ForeColor = $Theme.Text
|
|
$completionLabel.BackColor = [System.Drawing.Color]::Transparent
|
|
$completionLabel.Font = $Font
|
|
$completionLabel.AutoEllipsis = $true
|
|
$completionPanel.Controls.Add($completionLabel)
|
|
|
|
$restartCheckBox = New-Object System.Windows.Forms.CheckBox
|
|
$restartCheckBox.Text = Get-DeployCenterText -Text 'Restart this computer when closing'
|
|
$restartCheckBox.Location = New-Object System.Drawing.Point(12, 48)
|
|
$restartCheckBox.Size = New-Object System.Drawing.Size(($clientWidth - 160), 28)
|
|
$restartCheckBox.ForeColor = $Theme.Text
|
|
$restartCheckBox.BackColor = [System.Drawing.Color]::Transparent
|
|
$restartCheckBox.Font = $Font
|
|
$completionPanel.Controls.Add($restartCheckBox)
|
|
|
|
$okButton = New-Object System.Windows.Forms.Button
|
|
$okButton.Text = Get-DeployCenterText -Text 'OK'
|
|
$okButton.Location = New-Object System.Drawing.Point(($clientWidth - 110), 45)
|
|
$okButton.Size = New-Object System.Drawing.Size(92, 36)
|
|
$okButton.Font = $Font
|
|
$okButton.Cursor = [System.Windows.Forms.Cursors]::Hand
|
|
$okButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
|
|
Set-DeployCenterButtonStyle -Button $okButton -Theme $Theme -Role 'PrimaryButton'
|
|
$completionPanel.Controls.Add($okButton)
|
|
$form.Controls.Add($completionPanel)
|
|
$completionPanel.BringToFront()
|
|
Add-SessionFooter -TargetForm $form -Theme $Theme -Font $FooterFont `
|
|
-IPv4 $IPv4 -VncPassword $VncPassword -ShowPassword $true
|
|
$form.AcceptButton = $okButton
|
|
$form.CancelButton = $okButton
|
|
|
|
$progressState = [pscustomobject]@{
|
|
Form = $form
|
|
Theme = $Theme
|
|
ListPanel = $list
|
|
HeaderHeight = 120
|
|
FooterHeight = $footerHeight
|
|
Steps = $rows
|
|
CurrentIndex = -1
|
|
CompletionPanel = $completionPanel
|
|
CompletionLabel = $completionLabel
|
|
RestartCheckBox = $restartCheckBox
|
|
OkButton = $okButton
|
|
ElapsedTimer = New-Object System.Windows.Forms.Timer
|
|
UpdateAction = $null
|
|
Action = $null
|
|
}
|
|
|
|
$inProgressText = Get-DeployCenterText -Text 'In progress…'
|
|
$completedText = Get-DeployCenterText -Text 'Completed'
|
|
$formatElapsedTime = {
|
|
param([TimeSpan]$Duration)
|
|
if ($Duration.TotalHours -ge 1) { return $Duration.ToString('h\:mm\:ss') }
|
|
return $Duration.ToString('m\:ss')
|
|
}.GetNewClosure()
|
|
$progressState.ElapsedTimer.Interval = 500
|
|
$progressState.ElapsedTimer.Add_Tick({
|
|
if ($progressState.CurrentIndex -lt 0) { return }
|
|
$active = $progressState.Steps[$progressState.CurrentIndex]
|
|
if (-not $active.Started -or -not $active.Started.IsRunning) { return }
|
|
$elapsed = & $formatElapsedTime $active.Started.Elapsed
|
|
if ($active.Bar.Visible) {
|
|
$active.State.Text = '{0} % · {1}' -f $active.Bar.Value, $elapsed
|
|
} else {
|
|
$active.State.Text = '{0} {1}' -f $inProgressText, $elapsed
|
|
}
|
|
}.GetNewClosure())
|
|
$form.Add_Shown({ $progressState.ElapsedTimer.Start() }.GetNewClosure())
|
|
$form.Add_FormClosed({
|
|
$progressState.ElapsedTimer.Stop()
|
|
$progressState.ElapsedTimer.Dispose()
|
|
}.GetNewClosure())
|
|
$progressState.UpdateAction = {
|
|
param($Message, $Percent, $DisplayMessage)
|
|
|
|
if ($null -ne $Message) {
|
|
$nextIndex = -1
|
|
for ($index = 0; $index -lt $progressState.Steps.Count; $index++) {
|
|
if ([string]$Message -and ([string]$Message).StartsWith([string]$progressState.Steps[$index].Key, [StringComparison]::Ordinal)) {
|
|
$nextIndex = $index
|
|
break
|
|
}
|
|
}
|
|
if ($nextIndex -ge 0 -and $nextIndex -ne $progressState.CurrentIndex) {
|
|
if ($progressState.CurrentIndex -ge 0) {
|
|
$previous = $progressState.Steps[$progressState.CurrentIndex]
|
|
if ($previous.Started) {
|
|
$previous.Started.Stop()
|
|
$elapsed = if ($previous.Started.Elapsed.TotalHours -ge 1) { $previous.Started.Elapsed.ToString('h\:mm\:ss') } else { $previous.Started.Elapsed.ToString('m\:ss') }
|
|
$previous.State.Text = '{0}: {1}' -f $completedText, $elapsed
|
|
$previous.State.ForeColor = $progressState.Theme.Success
|
|
$previous.Bar.Visible = $false
|
|
$previous.Complete = $true
|
|
}
|
|
}
|
|
$progressState.CurrentIndex = $nextIndex
|
|
$current = $progressState.Steps[$nextIndex]
|
|
$current.Started = [System.Diagnostics.Stopwatch]::StartNew()
|
|
if (-not [string]::IsNullOrWhiteSpace([string]$DisplayMessage)) { $current.Name.Text = [string]$DisplayMessage }
|
|
$current.State.Text = $inProgressText
|
|
$current.State.ForeColor = $progressState.Theme.Success
|
|
}
|
|
}
|
|
|
|
if ($null -ne $Percent -and $progressState.CurrentIndex -ge 0) {
|
|
$active = $progressState.Steps[$progressState.CurrentIndex]
|
|
$active.Bar.Visible = $true
|
|
$active.Bar.Value = [int][Math]::Max(0, [Math]::Min(100, [int]$Percent))
|
|
$active.State.Text = '{0} % · {1}' -f $active.Bar.Value, (& $formatElapsedTime $active.Started.Elapsed)
|
|
}
|
|
[System.Windows.Forms.Application]::DoEvents()
|
|
}.GetNewClosure()
|
|
|
|
return $progressState
|
|
}
|
|
|
|
function Show-DeployCenterDeploymentCompletion {
|
|
param(
|
|
[Parameter(Mandatory=$true)]$Progress,
|
|
[Parameter(Mandatory=$true)][string]$Message,
|
|
[Parameter(Mandatory=$true)][System.Windows.Forms.Form]$Owner,
|
|
[bool]$RestartByDefault = $true
|
|
)
|
|
|
|
$Progress.CompletionLabel.Text = Get-DeployCenterText -Text $Message
|
|
$Progress.RestartCheckBox.Checked = $RestartByDefault
|
|
$Progress.CompletionPanel.Visible = $true
|
|
$Progress.ListPanel.Size = New-Object System.Drawing.Size($Progress.ListPanel.Width, ($Progress.Form.ClientSize.Height - $Progress.HeaderHeight - $Progress.CompletionPanel.Height - $Progress.FooterHeight - 12))
|
|
$Progress.CompletionPanel.BringToFront()
|
|
[System.Windows.Forms.Application]::DoEvents()
|
|
|
|
# Reopen the same progress window modally so its completed timings remain visible until confirmation.
|
|
$Progress.Form.Hide()
|
|
[void]$Progress.Form.ShowDialog($Owner)
|
|
$restart = [bool]$Progress.RestartCheckBox.Checked
|
|
if (-not $Progress.Form.IsDisposed) { $Progress.Form.Close() }
|
|
return $restart
|
|
}
|
|
|
|
function Set-DeployCenterDeploymentProgress {
|
|
param(
|
|
[Parameter(Mandatory=$true)]$Progress,
|
|
[AllowNull()][string]$Message,
|
|
[AllowNull()][object]$Percent
|
|
)
|
|
|
|
if ($null -ne $Message) {
|
|
$nextIndex = -1
|
|
for ($index = 0; $index -lt $Progress.Steps.Count; $index++) {
|
|
if ($Message.StartsWith([string]$Progress.Steps[$index].Key, [StringComparison]::Ordinal)) { $nextIndex = $index; break }
|
|
}
|
|
if ($nextIndex -ge 0 -and $nextIndex -ne $Progress.CurrentIndex) {
|
|
if ($Progress.CurrentIndex -ge 0) {
|
|
$previous = $Progress.Steps[$Progress.CurrentIndex]
|
|
if ($previous.Started) {
|
|
$previous.Started.Stop()
|
|
$previous.State.Text = '{0}: {1}' -f (Get-DeployCenterText -Text 'Completed'), (Format-DeployCenterElapsedTime -Duration $previous.Started.Elapsed)
|
|
$previous.State.ForeColor = $Progress.Theme.Success
|
|
$previous.Bar.Visible = $false
|
|
$previous.Complete = $true
|
|
}
|
|
}
|
|
$Progress.CurrentIndex = $nextIndex
|
|
$current = $Progress.Steps[$nextIndex]
|
|
$current.Started = [System.Diagnostics.Stopwatch]::StartNew()
|
|
$current.State.Text = Get-DeployCenterText -Text 'In progress…'
|
|
$current.State.ForeColor = $Progress.Theme.Success
|
|
}
|
|
}
|
|
|
|
if ($null -ne $Percent -and $Progress.CurrentIndex -ge 0) {
|
|
$active = $Progress.Steps[$Progress.CurrentIndex]
|
|
$active.Bar.Visible = $true
|
|
$active.Bar.Value = [int][Math]::Max(0, [Math]::Min(100, [int]$Percent))
|
|
$active.State.Text = '{0} % · {1}' -f $active.Bar.Value, (Format-DeployCenterElapsedTime -Duration $active.Started.Elapsed)
|
|
}
|
|
[System.Windows.Forms.Application]::DoEvents()
|
|
}
|
|
|
|
function Complete-DeployCenterDeploymentProgress {
|
|
param([Parameter(Mandatory=$true)]$Progress)
|
|
if ($Progress.CurrentIndex -ge 0) {
|
|
$active = $Progress.Steps[$Progress.CurrentIndex]
|
|
if ($active.Started -and $active.Started.IsRunning) { $active.Started.Stop() }
|
|
if ($active.Started) {
|
|
$active.State.Text = '{0}: {1}' -f (Get-DeployCenterText -Text 'Completed'), (Format-DeployCenterElapsedTime -Duration $active.Started.Elapsed)
|
|
}
|
|
$active.State.ForeColor = $Progress.Theme.Success
|
|
$active.Bar.Visible = $false
|
|
$active.Complete = $true
|
|
}
|
|
}
|
|
|
|
function Fail-DeployCenterDeploymentProgress {
|
|
param([Parameter(Mandatory=$true)]$Progress)
|
|
if ($Progress.CurrentIndex -ge 0) {
|
|
$active = $Progress.Steps[$Progress.CurrentIndex]
|
|
if ($active.Started -and $active.Started.IsRunning) { $active.Started.Stop() }
|
|
$active.State.Text = '{0}: {1}' -f (Get-DeployCenterText -Text 'Failed'), (Format-DeployCenterElapsedTime -Duration $active.Started.Elapsed)
|
|
$active.State.ForeColor = $Progress.Theme.DangerButton.Background
|
|
$active.Bar.Visible = $false
|
|
}
|
|
}
|
|
|
|
function Format-DeployCenterElapsedTime {
|
|
param([Parameter(Mandatory=$true)][TimeSpan]$Duration)
|
|
if ($Duration.TotalHours -ge 1) { return $Duration.ToString('h\:mm\:ss') }
|
|
return $Duration.ToString('m\:ss')
|
|
}
|
|
|
|
function Set-DeployCenterFormSizing {
|
|
param(
|
|
[Parameter(Mandatory=$true)][System.Windows.Forms.Form]$Form,
|
|
[switch]$Resizable
|
|
)
|
|
if ($Resizable) {
|
|
$Form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::Sizable
|
|
$Form.MaximizeBox = $true
|
|
} else {
|
|
$Form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
|
|
$Form.MaximizeBox = $false
|
|
}
|
|
}
|
|
|
|
function Add-SessionFooter {
|
|
param(
|
|
[Parameter(Mandatory=$true)][System.Windows.Forms.Form]$TargetForm,
|
|
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
|
|
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font,
|
|
[AllowNull()][string]$IPv4,
|
|
[AllowNull()][string]$VncPassword,
|
|
[bool]$ShowFooter = $true,
|
|
[bool]$ShowPassword = $true
|
|
)
|
|
if (-not $ShowFooter) { return }
|
|
|
|
$footer = New-Object System.Windows.Forms.Label
|
|
$footer.Name = 'DeployCenterSessionFooter'
|
|
$footer.Text = if ($IPv4) { (Get-DeployCenterText -Text 'Deployment IP:') + " $IPv4" } else { Get-DeployCenterText -Text 'Deployment IP unavailable' }
|
|
if ($ShowPassword -and -not [string]::IsNullOrWhiteSpace($VncPassword)) {
|
|
$footer.Text += " | " + (Get-DeployCenterText -Text 'VNC password:') + " $VncPassword"
|
|
}
|
|
$footer.Height = 26
|
|
$footer.Dock = [System.Windows.Forms.DockStyle]::Bottom
|
|
$footer.TextAlign = [System.Drawing.ContentAlignment]::MiddleLeft
|
|
$footer.Padding = New-Object System.Windows.Forms.Padding(12, 0, 0, 0)
|
|
$footer.BackColor = $Theme.PanelAlt
|
|
$footer.ForeColor = $Theme.Text
|
|
$footer.Font = $Font
|
|
$TargetForm.Controls.Add($footer)
|
|
$footer.BringToFront()
|
|
}
|
|
|
|
function New-Label {
|
|
param(
|
|
[Parameter(Mandatory=$true)][string]$Text,
|
|
[Parameter(Mandatory=$true)][int]$X,
|
|
[Parameter(Mandatory=$true)][int]$Y,
|
|
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
|
|
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font,
|
|
[int]$Width = 180,
|
|
[int]$Height = 24,
|
|
[AllowNull()]$Color = $null
|
|
)
|
|
|
|
$label = New-Object System.Windows.Forms.Label
|
|
$label.Text = Get-DeployCenterText -Text $Text
|
|
$label.Location = New-Object System.Drawing.Point($X, $Y)
|
|
$label.Size = New-Object System.Drawing.Size($Width, $Height)
|
|
$label.AutoEllipsis = $true
|
|
$label.TextAlign = [System.Drawing.ContentAlignment]::MiddleLeft
|
|
$label.BackColor = [System.Drawing.Color]::Transparent
|
|
$label.ForeColor = if ($null -eq $Color) { $Theme.Text } else { $Color }
|
|
$label.Font = $Font
|
|
return $label
|
|
}
|
|
|
|
function New-GroupBox {
|
|
param(
|
|
[Parameter(Mandatory=$true)][string]$Text,
|
|
[Parameter(Mandatory=$true)][int]$X,
|
|
[Parameter(Mandatory=$true)][int]$Y,
|
|
[Parameter(Mandatory=$true)][int]$Width,
|
|
[Parameter(Mandatory=$true)][int]$Height,
|
|
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
|
|
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font
|
|
)
|
|
$group = New-Object DeployCenter.ThemedGroupBox
|
|
$group.Text = Get-DeployCenterText -Text $Text
|
|
$group.Location = New-Object System.Drawing.Point($X, $Y)
|
|
$group.Size = New-Object System.Drawing.Size($Width, $Height)
|
|
$group.ForeColor = $Theme.Text
|
|
$group.BackColor = $Theme.Panel
|
|
$group.PanelColor = $Theme.Panel
|
|
$group.BorderColor = $Theme.Border
|
|
$group.TextColor = $Theme.Text
|
|
$group.Font = $Font
|
|
return $group
|
|
}
|
|
|
|
function New-Button {
|
|
param(
|
|
[Parameter(Mandatory=$true)][string]$Text,
|
|
[Parameter(Mandatory=$true)][int]$X,
|
|
[Parameter(Mandatory=$true)][int]$Y,
|
|
[Parameter(Mandatory=$true)][int]$Width,
|
|
[Parameter(Mandatory=$true)][int]$Height,
|
|
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Theme,
|
|
[Parameter(Mandatory=$true)][System.Drawing.Font]$Font,
|
|
[ValidateSet('Button','PrimaryButton','DangerButton')][string]$Role = 'Button'
|
|
)
|
|
$button = New-Object System.Windows.Forms.Button
|
|
$button.Text = Get-DeployCenterText -Text $Text
|
|
$button.Location = New-Object System.Drawing.Point($X, $Y)
|
|
$button.Size = New-Object System.Drawing.Size($Width, $Height)
|
|
$button.Tag = $Role
|
|
Set-DeployCenterButtonStyle -Button $button -Theme $Theme -Role $Role
|
|
$button.Font = $Font
|
|
$button.Cursor = [System.Windows.Forms.Cursors]::Hand
|
|
$button.AutoSize = $false
|
|
$button.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
|
|
return $button
|
|
}
|
|
|
|
function Get-DeployCenterLogoPathForTheme {
|
|
param(
|
|
[Parameter(Mandatory=$true)][string]$ThemeName,
|
|
[Parameter(Mandatory=$true)][string]$LogoPath,
|
|
[Parameter(Mandatory=$true)][string]$LogoDarkPath
|
|
)
|
|
if ($ThemeName -eq 'Dark' -and (Test-Path -LiteralPath $LogoDarkPath -PathType Leaf)) { return $LogoDarkPath }
|
|
if (Test-Path -LiteralPath $LogoPath -PathType Leaf) { return $LogoPath }
|
|
if (Test-Path -LiteralPath $LogoDarkPath -PathType Leaf) { return $LogoDarkPath }
|
|
return $null
|
|
}
|
|
|
|
function Update-DeployCenterLogo {
|
|
param(
|
|
[Parameter(Mandatory=$true)][AllowNull()][System.Windows.Forms.PictureBox]$PictureBox,
|
|
[Parameter(Mandatory=$true)][string]$ThemeName,
|
|
[Parameter(Mandatory=$true)][string]$LogoPath,
|
|
[Parameter(Mandatory=$true)][string]$LogoDarkPath
|
|
)
|
|
if ($null -eq $PictureBox) { return }
|
|
$selectedLogoPath = Get-DeployCenterLogoPathForTheme -ThemeName $ThemeName -LogoPath $LogoPath -LogoDarkPath $LogoDarkPath
|
|
if ([string]::IsNullOrWhiteSpace($selectedLogoPath)) { return }
|
|
|
|
try {
|
|
$newImage = [System.Drawing.Image]::FromFile($selectedLogoPath)
|
|
$oldImage = $PictureBox.Image
|
|
$PictureBox.Image = $newImage
|
|
if ($null -ne $oldImage) { $oldImage.Dispose() }
|
|
$PictureBox.Visible = $true
|
|
} catch {
|
|
# Keep the current logo if the new file is missing or invalid.
|
|
}
|
|
}
|
|
|
|
|
|
|
|
# Main UI workflows; dot-sourced to explicitly share DeployCenter session state.
|
|
function Set-DiskSelectorWidth {
|
|
if ($null -eq $script:cmbDisk -or $null -eq $script:groupDisks) { return }
|
|
Set-DeployCenterDeploymentControlsLayout -Parent $script:groupDisks -Controls $deploymentControls
|
|
}
|
|
|
|
function Set-ControlTheme {
|
|
param([System.Windows.Forms.Control]$Control)
|
|
|
|
# The main form background must also change.
|
|
if ($Control -is [System.Windows.Forms.Form]) {
|
|
$Control.BackColor = $script:Theme.Background
|
|
$Control.ForeColor = $script:Theme.Text
|
|
}
|
|
elseif ($Control -is [DeployCenter.ThemedGroupBox]) {
|
|
$Control.BackColor = $script:Theme.Panel
|
|
$Control.PanelColor = $script:Theme.Panel
|
|
$Control.BorderColor = $script:Theme.Border
|
|
$Control.TextColor = $script:Theme.Text
|
|
$Control.ForeColor = $script:Theme.Text
|
|
$Control.Invalidate()
|
|
}
|
|
elseif ($Control -is [System.Windows.Forms.Label]) {
|
|
$Control.ForeColor = $script:Theme.Text
|
|
if ($Control.Name -eq 'lblSelectedWindows') {
|
|
$Control.ForeColor = if ($script:SelectedWindowsEdition) { $script:Theme.Success } else { $script:Theme.Secondary }
|
|
}
|
|
if ($Control.Name -eq 'lblDriverStatus') {
|
|
$Control.ForeColor = if ($script:DriverPackSelection -and $script:DriverPackSelection.Found) { $script:Theme.Success } else { $script:Theme.Secondary }
|
|
}
|
|
# The VNC footer also follows the active theme palette.
|
|
if ($Control.Name -eq 'DeployCenterSessionFooter') {
|
|
$Control.BackColor = $script:Theme.PanelAlt
|
|
$Control.ForeColor = $script:Theme.Secondary
|
|
$Control.Invalidate()
|
|
}
|
|
}
|
|
elseif ($Control -is [System.Windows.Forms.ListView]) {
|
|
$Control.BackColor = $script:Theme.PanelAlt
|
|
$Control.ForeColor = $script:Theme.Text
|
|
}
|
|
elseif ($Control -is [System.Windows.Forms.ComboBox]) {
|
|
Set-DeployCenterComboBoxTheme -ComboBox $Control -Theme $script:Theme
|
|
}
|
|
elseif ($Control -is [System.Windows.Forms.TextBox]) {
|
|
$Control.BackColor = $script:Theme.PanelAlt
|
|
$Control.ForeColor = $script:Theme.Text
|
|
}
|
|
|
|
foreach ($child in $Control.Controls) {
|
|
Set-ControlTheme -Control $child
|
|
}
|
|
}
|
|
|
|
function Set-DeployCenterTheme {
|
|
param([Parameter(Mandatory=$true)][string]$ThemeName)
|
|
|
|
$availableThemes = @(Get-DeployCenterThemeNames -ThemesRoot $script:ThemesRoot)
|
|
if ($availableThemes -notcontains $ThemeName) { return }
|
|
try {
|
|
$script:Theme = Import-DeployCenterTheme -ThemesRoot $script:ThemesRoot -ThemeName $ThemeName
|
|
} catch {
|
|
& $script:SourceLogAction ("Unable to load theme '$ThemeName' : " + $_.Exception.Message) "ERROR"
|
|
return
|
|
}
|
|
$script:CurrentTheme = $ThemeName
|
|
$script:cmbTheme.BackColor = $script:Theme.Panel
|
|
$script:cmbTheme.ForeColor = $script:Theme.Text
|
|
$script:cmbTheme.Invalidate()
|
|
$script:cmbTheme.Refresh()
|
|
|
|
Set-ControlTheme -Control $script:form
|
|
Set-ControlTheme -Control $script:logForm
|
|
|
|
# Update the logo if a theme-specific variant exists.
|
|
Update-DeployCenterLogo -PictureBox $script:pictureLogo -ThemeName $script:CurrentTheme `
|
|
-LogoPath $script:LogoPath -LogoDarkPath $script:LogoDarkPath
|
|
|
|
$script:lblTitle.ForeColor = $script:Theme.Text
|
|
$script:lblSubtitle.ForeColor = $script:Theme.Secondary
|
|
|
|
Set-DeployCenterButtonStyle -Button $script:btnChooseWindows -Theme $script:Theme -Role 'Button'
|
|
Set-DeployCenterButtonStyle -Button $script:btnLog -Theme $script:Theme -Role 'Button'
|
|
Set-DeployCenterButtonStyle -Button $script:btnRefresh -Theme $script:Theme -Role 'PrimaryButton'
|
|
Set-DeployCenterButtonStyle -Button $script:btnDeploy -Theme $script:Theme -Role 'PrimaryButton'
|
|
Set-DeployCenterButtonStyle -Button $script:btnClose -Theme $script:Theme -Role 'DangerButton'
|
|
# Explicitly refresh the main footer when the theme changes.
|
|
foreach ($footer in $script:form.Controls.Find('DeployCenterSessionFooter', $true)) {
|
|
$footer.BackColor = $script:Theme.PanelAlt
|
|
$footer.ForeColor = $script:Theme.Secondary
|
|
$footer.Invalidate()
|
|
}
|
|
$script:cmbTheme.BackColor = $script:Theme.Panel
|
|
$script:cmbTheme.ForeColor = $script:Theme.Text
|
|
$script:cmbTheme.Invalidate()
|
|
$script:groupPC.Invalidate()
|
|
$script:groupDisks.Invalidate()
|
|
$script:cmbDisk.BackColor = $script:Theme.PanelAlt
|
|
$script:cmbDisk.ForeColor = $script:Theme.Text
|
|
$script:cmbDisk.Invalidate()
|
|
# Keep the button above the other header controls.
|
|
$script:cmbTheme.BringToFront()
|
|
|
|
# Force a repaint after changing the palette.
|
|
$script:form.SuspendLayout()
|
|
try {
|
|
foreach ($control in $script:form.Controls) {
|
|
$control.Invalidate($true)
|
|
}
|
|
$script:form.Invalidate($true)
|
|
$script:form.Refresh()
|
|
}
|
|
finally {
|
|
$script:form.ResumeLayout($true)
|
|
}
|
|
}
|
|
|
|
function Show-WindowsSourcesDialog {
|
|
$dlg = New-Object System.Windows.Forms.Form
|
|
$dlg.Text = Get-DeployCenterText -Text 'Windows sources - DeployCenter drive and SMB share'
|
|
$dlg.StartPosition = 'CenterParent'
|
|
$dlg.Size = New-Object System.Drawing.Size(1100, 700)
|
|
$dlg.MinimumSize = New-Object System.Drawing.Size(700, 450)
|
|
Set-DeployCenterFormSizing -Form $dlg -Resizable
|
|
# Open directly in fullscreen to use the entire available area.
|
|
$dlg.WindowState = [System.Windows.Forms.FormWindowState]::Maximized
|
|
$dlg.ShowInTaskbar = $false
|
|
$dlg.BackColor = $script:Theme.Background
|
|
$dlg.ForeColor = $script:Theme.Text
|
|
$dlg.Font = $script:FontRegular
|
|
$dlg.KeyPreview = $true
|
|
$dlg.Add_KeyDown({
|
|
param($sender, $eventArgs)
|
|
if ($eventArgs.KeyCode -eq [System.Windows.Forms.Keys]::Escape) {
|
|
$eventArgs.Handled = $true
|
|
$eventArgs.SuppressKeyPress = $true
|
|
$dlg.Close()
|
|
}
|
|
}.GetNewClosure())
|
|
|
|
$lblPath = New-Object System.Windows.Forms.Label
|
|
$lblPath.Text = Get-DeployCenterText -Text 'Paths to scan (separated by ;):'
|
|
$lblPath.Location = New-Object System.Drawing.Point(16, 16)
|
|
$lblPath.Size = New-Object System.Drawing.Size(260, 24)
|
|
$lblPath.ForeColor = $script:Theme.Text
|
|
$dlg.Controls.Add($lblPath)
|
|
|
|
$txtPath = New-Object System.Windows.Forms.TextBox
|
|
$txtPath.Location = New-Object System.Drawing.Point(16, 42)
|
|
$txtPath.Size = New-Object System.Drawing.Size(560, 26)
|
|
$txtPath.Anchor = 'Top,Left,Right'
|
|
$defaultSourceRoot = $script:DeployVolumeRoot
|
|
if ([string]::IsNullOrWhiteSpace($defaultSourceRoot)) { $defaultSourceRoot = $script:DeployRoot }
|
|
$configuredPaths = [string]$script:Config.SourcePaths
|
|
if ([string]::IsNullOrWhiteSpace($configuredPaths)) {
|
|
$txtPath.Text = $defaultSourceRoot
|
|
} else {
|
|
$txtPath.Text = $configuredPaths.Replace("%DEPLOY_VOLUME%", $defaultSourceRoot)
|
|
}
|
|
$txtPath.BackColor = $script:Theme.PanelAlt
|
|
$txtPath.ForeColor = $script:Theme.Text
|
|
$dlg.Controls.Add($txtPath)
|
|
|
|
$btnScan = New-Object System.Windows.Forms.Button
|
|
$btnScan.Text = Get-DeployCenterText -Text 'Search'
|
|
$btnScan.Location = New-Object System.Drawing.Point(650, 40)
|
|
$btnScan.Size = New-Object System.Drawing.Size(100, 30)
|
|
$btnScan.Anchor = 'Top,Right'
|
|
$btnScan.UseVisualStyleBackColor = $false
|
|
$btnScan.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
|
|
$btnScan.FlatAppearance.BorderColor = $script:Theme.Border
|
|
$btnScan.BackColor = $script:Theme.Accent
|
|
$btnScan.ForeColor = [System.Drawing.Color]::White
|
|
$dlg.Controls.Add($btnScan)
|
|
|
|
$btnEditions = New-Object System.Windows.Forms.Button
|
|
$btnEditions.Text = Get-DeployCenterText -Text 'Show editions'
|
|
$btnEditions.Location = New-Object System.Drawing.Point(760, 40)
|
|
$btnEditions.Size = New-Object System.Drawing.Size(150, 30)
|
|
$btnEditions.Anchor = 'Top,Right'
|
|
$btnEditions.UseVisualStyleBackColor = $false
|
|
$btnEditions.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
|
|
$btnEditions.FlatAppearance.BorderColor = $script:Theme.Border
|
|
$btnEditions.BackColor = $script:Theme.Panel
|
|
$btnEditions.ForeColor = $script:Theme.Text
|
|
$dlg.Controls.Add($btnEditions)
|
|
|
|
$list = New-Object System.Windows.Forms.ListView
|
|
$list.Location = New-Object System.Drawing.Point(16, 84)
|
|
$list.Size = New-Object System.Drawing.Size(770, 420)
|
|
$list.Anchor = 'Top,Bottom,Left,Right'
|
|
$list.View = 'Details'; $list.FullRowSelect = $true; $list.GridLines = $true
|
|
$list.MultiSelect = $false
|
|
$list.HideSelection = $true
|
|
$list.BackColor = $script:Theme.PanelAlt
|
|
$list.ForeColor = $script:Theme.Text
|
|
$list.OwnerDraw = $true
|
|
[void]$list.Columns.Add((Get-DeployCenterText -Text 'Type'), 135)
|
|
[void]$list.Columns.Add((Get-DeployCenterText -Text 'Name'), 260)
|
|
[void]$list.Columns.Add((Get-DeployCenterText -Text 'Size'), 80)
|
|
[void]$list.Columns.Add((Get-DeployCenterText -Text 'Path'), 250)
|
|
|
|
# The Name column has priority and absorbs the remaining space.
|
|
$resizeSourceColumns = {
|
|
if ($list.Columns.Count -ge 4) {
|
|
$availableWidth = $list.ClientSize.Width - $list.Columns[0].Width - $list.Columns[2].Width - $list.Columns[3].Width - 4
|
|
if ($list.Items.Count -gt 0 -and $list.Items.Count -gt [math]::Floor($list.ClientSize.Height / 20)) {
|
|
$availableWidth -= [System.Windows.Forms.SystemInformation]::VerticalScrollBarWidth
|
|
}
|
|
$list.Columns[1].Width = [Math]::Max(180, $availableWidth)
|
|
}
|
|
}
|
|
$list.Add_SizeChanged($resizeSourceColumns)
|
|
$list.Add_HandleCreated($resizeSourceColumns)
|
|
|
|
# Custom-draw headers and rows so the ListView
|
|
# follows the active theme instead of using the native white headers.
|
|
$list.Add_DrawColumnHeader({
|
|
param($sender, $e)
|
|
$headerColor = if ($script:Theme.Contains('TableHeader')) { $script:Theme.TableHeader } else { $script:Theme.PanelAlt }
|
|
$headerBrush = New-Object System.Drawing.SolidBrush($headerColor)
|
|
$textBrush = New-Object System.Drawing.SolidBrush($script:Theme.Text)
|
|
$borderPen = New-Object System.Drawing.Pen($script:Theme.Border)
|
|
try {
|
|
$e.Graphics.FillRectangle($headerBrush, $e.Bounds)
|
|
$e.Graphics.DrawRectangle($borderPen, $e.Bounds.X, $e.Bounds.Y, $e.Bounds.Width - 1, $e.Bounds.Height - 1)
|
|
$flags = [System.Windows.Forms.TextFormatFlags]::Left -bor [System.Windows.Forms.TextFormatFlags]::VerticalCenter -bor [System.Windows.Forms.TextFormatFlags]::EndEllipsis
|
|
[System.Windows.Forms.TextRenderer]::DrawText($e.Graphics, $e.Header.Text, $list.Font, $e.Bounds, $script:Theme.Text, $flags)
|
|
} finally {
|
|
$headerBrush.Dispose(); $textBrush.Dispose(); $borderPen.Dispose()
|
|
}
|
|
})
|
|
$list.Add_DrawItem({
|
|
param($sender, $e)
|
|
if ($list.View -ne [System.Windows.Forms.View]::Details) {
|
|
$e.DrawDefault = $true
|
|
}
|
|
})
|
|
$list.Add_DrawSubItem({
|
|
param($sender, $e)
|
|
$rowBack = if (($e.Item.Index % 2) -eq 0) { $script:Theme.TableRowEven } else { $script:Theme.TableRowOdd }
|
|
$rowFore = $script:Theme.Text
|
|
# Highlight only the selected item, never every row.
|
|
if ($e.Item.Selected -and $list.Focused) {
|
|
$rowBack = $script:Theme.Accent
|
|
$rowFore = [System.Drawing.Color]::White
|
|
}
|
|
$rowBrush = New-Object System.Drawing.SolidBrush($rowBack)
|
|
$textBrush = New-Object System.Drawing.SolidBrush($rowFore)
|
|
$borderPen = New-Object System.Drawing.Pen($script:Theme.Border)
|
|
try {
|
|
$e.Graphics.FillRectangle($rowBrush, $e.Bounds)
|
|
$e.Graphics.DrawRectangle($borderPen, $e.Bounds.X, $e.Bounds.Y, $e.Bounds.Width - 1, $e.Bounds.Height - 1)
|
|
$flags = [System.Windows.Forms.TextFormatFlags]::Left -bor [System.Windows.Forms.TextFormatFlags]::VerticalCenter -bor [System.Windows.Forms.TextFormatFlags]::EndEllipsis -bor [System.Windows.Forms.TextFormatFlags]::NoPrefix
|
|
[System.Windows.Forms.TextRenderer]::DrawText($e.Graphics, $e.SubItem.Text, $list.Font, $e.Bounds, $rowFore, $flags)
|
|
} finally {
|
|
$rowBrush.Dispose(); $textBrush.Dispose(); $borderPen.Dispose()
|
|
}
|
|
})
|
|
# Local filter: it never reruns the scan of drives or shares.
|
|
$lblFilter = New-Object System.Windows.Forms.Label
|
|
$lblFilter.Text = Get-DeployCenterText -Text 'Filter list:'
|
|
$lblFilter.Location = New-Object System.Drawing.Point(16, 80)
|
|
$lblFilter.Size = New-Object System.Drawing.Size(100, 24)
|
|
$lblFilter.ForeColor = $script:Theme.Text
|
|
$dlg.Controls.Add($lblFilter)
|
|
|
|
$txtFilter = New-Object System.Windows.Forms.TextBox
|
|
$txtFilter.Location = New-Object System.Drawing.Point(120, 78)
|
|
$txtFilter.Size = New-Object System.Drawing.Size(500, 26)
|
|
$txtFilter.Anchor = 'Top,Left,Right'
|
|
$txtFilter.BackColor = $script:Theme.PanelAlt
|
|
$txtFilter.ForeColor = $script:Theme.Text
|
|
$dlg.Controls.Add($txtFilter)
|
|
|
|
$list.Location = New-Object System.Drawing.Point(16, 112)
|
|
$dlg.Controls.Add($list)
|
|
|
|
# Explicit shared state between WinForms event handlers.
|
|
# In PowerShell, callbacks may run in a different scope:
|
|
# a local $allFound variable may then remain empty in the filter.
|
|
$sourceState = [hashtable]::Synchronized(@{ AllFound = @() })
|
|
$renderSourceList = {
|
|
param([string]$Query)
|
|
$previousSelection = $null
|
|
if ($list.SelectedItems.Count -gt 0) {
|
|
$previousSelection = [string]$list.SelectedItems[0].SubItems[3].Text
|
|
}
|
|
|
|
$needle = ([string]$Query).Trim()
|
|
$visibleEntries = New-Object System.Collections.Generic.List[object]
|
|
$entries = @($sourceState.AllFound)
|
|
|
|
foreach ($entry in $entries) {
|
|
if ([string]::IsNullOrWhiteSpace($needle)) {
|
|
[void]$visibleEntries.Add($entry)
|
|
continue
|
|
}
|
|
|
|
$searchable = '{0} {1} {2} {3} {4}' -f `
|
|
[string]$entry.Type, [string]$entry.Name, [string]$entry.Path, `
|
|
[string]$entry.Size, [string]$entry.Detail
|
|
|
|
if ($searchable.IndexOf($needle, [System.StringComparison]::OrdinalIgnoreCase) -ge 0) {
|
|
[void]$visibleEntries.Add($entry)
|
|
}
|
|
}
|
|
|
|
$list.BeginUpdate()
|
|
try {
|
|
$list.Items.Clear()
|
|
foreach ($entry in $visibleEntries) {
|
|
$item = New-Object System.Windows.Forms.ListViewItem([string]$entry.Type)
|
|
[void]$item.SubItems.Add([string]$entry.Name)
|
|
[void]$item.SubItems.Add([string]$entry.Size)
|
|
[void]$item.SubItems.Add([string]$entry.Path)
|
|
if ($previousSelection -and ([string]$entry.Path -eq $previousSelection)) {
|
|
$item.Selected = $true
|
|
}
|
|
[void]$list.Items.Add($item)
|
|
}
|
|
} finally {
|
|
$list.EndUpdate()
|
|
}
|
|
|
|
if ($entries.Count -gt 0) {
|
|
$lblStatus.Text = (Get-DeployCenterText -Text '{0} result(s) shown out of {1}. Instant filter; no files changed.' -FormatArguments @($list.Items.Count, $entries.Count))
|
|
} elseif ([string]::IsNullOrWhiteSpace($needle)) {
|
|
$lblStatus.Text = Get-DeployCenterText -Text 'No ISO/WIM/ESD source or extracted installation was found in the accessible paths.'
|
|
} else {
|
|
$lblStatus.Text = (Get-DeployCenterText -Text 'No results for “{0}”. Clear the filter to see the list again.' -FormatArguments @($needle))
|
|
}
|
|
}
|
|
$txtFilter.Add_TextChanged({ & $renderSourceList $txtFilter.Text })
|
|
|
|
$lblStatus = New-Object System.Windows.Forms.Label
|
|
$lblStatus.Text = Get-DeployCenterText -Text 'Read-only scan of the DeployCenter drive and/or SMB share. Separate multiple paths with semicolons.'
|
|
$lblStatus.Location = New-Object System.Drawing.Point(16, 515)
|
|
$lblStatus.Size = New-Object System.Drawing.Size(770, 36)
|
|
$lblStatus.Anchor = 'Bottom,Left,Right'
|
|
$lblStatus.AutoSize = $false
|
|
$lblStatus.ForeColor = $script:Theme.Secondary
|
|
$dlg.Controls.Add($lblStatus)
|
|
|
|
$progressAnalysis = New-Object System.Windows.Forms.ProgressBar
|
|
$progressAnalysis.Name = 'progressWindowsSourceAnalysis'
|
|
$progressAnalysis.Style = [System.Windows.Forms.ProgressBarStyle]::Marquee
|
|
$progressAnalysis.MarqueeAnimationSpeed = 30
|
|
$progressAnalysis.Visible = $false
|
|
$progressAnalysis.Anchor = 'Bottom,Right'
|
|
$dlg.Controls.Add($progressAnalysis)
|
|
|
|
# Recalculate positions from the actual window size:
|
|
# the table uses the remaining space and the status stays visible at the bottom.
|
|
$resizeLayout = {
|
|
$clientWidth = $dlg.ClientSize.Width
|
|
$clientHeight = $dlg.ClientSize.Height
|
|
$rightMargin = 16
|
|
$gap = 12
|
|
$btnWidth = 88
|
|
$editionsWidth = 148
|
|
$left = 12
|
|
$right = [Math]::Max($left + $btnWidth, $clientWidth - $rightMargin)
|
|
$btnEditionsLeft = [Math]::Max($left, $right - $editionsWidth)
|
|
$btnScanLeft = [Math]::Max($left, $btnEditionsLeft - $gap - $btnWidth)
|
|
$txtPath.Width = [Math]::Max(100, $btnScanLeft - $left - $gap)
|
|
$btnEditions.Location = New-Object System.Drawing.Point($btnEditionsLeft, 40)
|
|
$btnEditions.Width = [Math]::Max(120, $right - $btnEditionsLeft)
|
|
$btnScan.Location = New-Object System.Drawing.Point($btnScanLeft, 40)
|
|
$btnScan.Width = [Math]::Max(80, $btnEditionsLeft - $gap - $btnScanLeft)
|
|
$btnScan.Anchor = 'Top,Right'
|
|
$btnEditions.Anchor = 'Top,Right'
|
|
$txtPath.Anchor = 'Top,Left,Right'
|
|
$lblFilter.Location = New-Object System.Drawing.Point(16, 80)
|
|
$txtFilter.Location = New-Object System.Drawing.Point(120, 78)
|
|
$txtFilter.Size = New-Object System.Drawing.Size([Math]::Max(180, $clientWidth - 152), 26)
|
|
$list.Location = New-Object System.Drawing.Point(16, 112)
|
|
$list.Size = New-Object System.Drawing.Size([Math]::Max(300, $clientWidth - 32), [Math]::Max(120, $clientHeight - 205))
|
|
$list.Anchor = 'Top,Bottom,Left,Right'
|
|
$lblStatus.Location = New-Object System.Drawing.Point(16, ($clientHeight - 52))
|
|
$lblStatus.Size = New-Object System.Drawing.Size([Math]::Max(150, $clientWidth - 164), 24)
|
|
$lblStatus.Anchor = 'Bottom,Left,Right'
|
|
$progressAnalysis.Location = New-Object System.Drawing.Point(($clientWidth - 136), ($clientHeight - 52))
|
|
$progressAnalysis.Size = New-Object System.Drawing.Size(120, 20)
|
|
}
|
|
$dlg.Add_Shown({
|
|
& $resizeLayout
|
|
# Automatically start the first scan when the window opens.
|
|
# The Search button remains available if the user changes the paths.
|
|
$btnScan.PerformClick()
|
|
})
|
|
$dlg.Add_SizeChanged({ & $resizeLayout })
|
|
|
|
$btnEditions.Add_Click({
|
|
if ($list.SelectedItems.Count -eq 0) {
|
|
Show-DeployCenterMessage -Message 'Select a source from the list first.' -Title 'Windows editions' -Icon Information -Owner $dlg -Theme $script:Theme -Font $script:FontRegular
|
|
return
|
|
}
|
|
|
|
$selectedPath = [string]$list.SelectedItems[0].SubItems[3].Text
|
|
$selectedName = [string]$list.SelectedItems[0].SubItems[1].Text
|
|
$resolvedImage = $null
|
|
$mountTransferred = $false
|
|
$btnEditions.Enabled = $false
|
|
$btnScan.Enabled = $false
|
|
$dlg.Cursor = [System.Windows.Forms.Cursors]::WaitCursor
|
|
$lblStatus.Text = (Get-DeployCenterText -Text 'Analyzing editions in {0}...' -FormatArguments @($selectedName))
|
|
$progressAnalysis.Visible = $true
|
|
$progressAnalysis.BringToFront()
|
|
$dlg.Refresh()
|
|
try {
|
|
$resolvedImage = Resolve-InstallImagePath -SelectedPath $selectedPath -ExistingImage $script:SelectedWindowsEdition
|
|
$imagePath = [string]$resolvedImage.ImagePath
|
|
$editionResult = Get-WindowsImageEditions -ImagePath $imagePath -LogAction $script:SourceLogAction
|
|
$editions = @($editionResult.Editions)
|
|
$globalImageVersion = [string]$editionResult.GlobalVersion
|
|
$lines = foreach ($edition in $editions) {
|
|
$index = if ($edition.ImageIndex) { $edition.ImageIndex } elseif ($edition.Index) { $edition.Index } else { '?' }
|
|
$name = if ($edition.ImageName) { $edition.ImageName } elseif ($edition.Name) { $edition.Name } else { Get-DeployCenterText -Text 'Name unavailable' }
|
|
$arch = if ($edition.Architecture) { [string]$edition.Architecture } else { Get-DeployCenterText -Text 'Architecture unavailable' }
|
|
$version = if ($edition.Version) { [string]$edition.Version } else { Get-DeployCenterText -Text 'Version unavailable' }
|
|
"Index: $index`r`nName: $name`r`nArchitecture: $arch`r`nVersion: $version"
|
|
}
|
|
# The child form is modal: refresh the status before showing it,
|
|
# otherwise the parent form remains visually stuck on "scan in progress".
|
|
$lblStatus.Text = (Get-DeployCenterText -Text '{0} edition(s) found in {1}. Select an edition.' -FormatArguments @($editions.Count, $selectedName))
|
|
$dlg.Refresh()
|
|
|
|
# Edition table; retain the selected DISM index for later use.
|
|
$resultForm = New-Object System.Windows.Forms.Form
|
|
$resultForm.Text = Get-DeployCenterText -Text 'Available Windows editions'
|
|
$resultForm.StartPosition = 'CenterParent'
|
|
$resultForm.Size = New-Object System.Drawing.Size(700, 480)
|
|
$resultForm.MinimumSize = New-Object System.Drawing.Size(520, 320)
|
|
Set-DeployCenterFormSizing -Form $resultForm
|
|
$resultForm.ShowInTaskbar = $false
|
|
$resultForm.BackColor = $script:Theme.Background
|
|
$resultForm.ForeColor = $script:Theme.Text
|
|
|
|
$sourceLabel = New-Object System.Windows.Forms.Label
|
|
$sourceLabel.Text = (Get-DeployCenterText -Text 'Source:' ) + " $selectedPath"
|
|
$sourceLabel.Dock = [System.Windows.Forms.DockStyle]::Top
|
|
$sourceLabel.Height = 38
|
|
$sourceLabel.Padding = New-Object System.Windows.Forms.Padding(10, 8, 10, 4)
|
|
$sourceLabel.AutoEllipsis = $true
|
|
$sourceLabel.BackColor = $script:Theme.Background
|
|
$sourceLabel.ForeColor = $script:Theme.Text
|
|
|
|
$editionList = New-Object System.Windows.Forms.ListView
|
|
$editionList.View = [System.Windows.Forms.View]::Details
|
|
$editionList.FullRowSelect = $true
|
|
$editionList.GridLines = $true
|
|
$editionList.MultiSelect = $false
|
|
$editionList.HideSelection = $false
|
|
# Only one edition can be selected at a time.
|
|
$editionList.Sorting = [System.Windows.Forms.SortOrder]::None
|
|
$editionList.Dock = [System.Windows.Forms.DockStyle]::Fill
|
|
$editionList.BackColor = $script:Theme.Panel
|
|
$editionList.ForeColor = $script:Theme.Text
|
|
[void]$editionList.Columns.Add((Get-DeployCenterText -Text 'Index'), 65)
|
|
[void]$editionList.Columns.Add((Get-DeployCenterText -Text 'Edition'), 260)
|
|
[void]$editionList.Columns.Add((Get-DeployCenterText -Text 'Architecture'), 100)
|
|
[void]$editionList.Columns.Add((Get-DeployCenterText -Text 'Version'), 105)
|
|
|
|
# Edition is the priority column; Version keeps a fixed width.
|
|
$resizeEditionColumns = {
|
|
if ($editionList.Columns.Count -ge 4) {
|
|
$availableWidth = $editionList.ClientSize.Width - $editionList.Columns[0].Width - $editionList.Columns[2].Width - $editionList.Columns[3].Width - 4
|
|
if ($editionList.Items.Count -gt 0 -and $editionList.Items.Count -gt [Math]::Floor($editionList.ClientSize.Height / 20)) {
|
|
$availableWidth -= [System.Windows.Forms.SystemInformation]::VerticalScrollBarWidth
|
|
}
|
|
$editionList.Columns[1].Width = [Math]::Max(220, $availableWidth)
|
|
}
|
|
}
|
|
$editionList.Add_SizeChanged($resizeEditionColumns)
|
|
$resultForm.Add_Shown($resizeEditionColumns)
|
|
|
|
# Custom-draw headers and cells so the ListView
|
|
# follows the Light/Dark theme instead of keeping a white header.
|
|
$editionList.OwnerDraw = $true
|
|
$editionList.Add_DrawColumnHeader({
|
|
param($sender, $e)
|
|
$headerColor = if ($script:Theme.Contains('TableHeader')) { $script:Theme.TableHeader } else { $script:Theme.PanelAlt }
|
|
$headerBrush = New-Object System.Drawing.SolidBrush($headerColor)
|
|
$headerPen = New-Object System.Drawing.Pen($script:Theme.Border)
|
|
try {
|
|
$e.Graphics.FillRectangle($headerBrush, $e.Bounds)
|
|
$e.Graphics.DrawRectangle($headerPen, $e.Bounds.X, $e.Bounds.Y, $e.Bounds.Width - 1, $e.Bounds.Height - 1)
|
|
[System.Windows.Forms.TextRenderer]::DrawText(
|
|
$e.Graphics, $e.Header.Text, $sender.Font, $e.Bounds,
|
|
$script:Theme.Text,
|
|
([System.Windows.Forms.TextFormatFlags]::VerticalCenter -bor
|
|
[System.Windows.Forms.TextFormatFlags]::Left -bor
|
|
[System.Windows.Forms.TextFormatFlags]::EndEllipsis)
|
|
)
|
|
} finally {
|
|
$headerBrush.Dispose()
|
|
$headerPen.Dispose()
|
|
}
|
|
})
|
|
$editionList.Add_DrawItem({ param($sender, $e) })
|
|
$editionList.Add_DrawSubItem({
|
|
param($sender, $e)
|
|
$cellBack = if (($e.Item.Index % 2) -eq 0) { $script:Theme.TableRowEven } else { $script:Theme.TableRowOdd }
|
|
$cellFore = $script:Theme.Text
|
|
if ($e.Item.Selected) {
|
|
$cellBack = $script:Theme.Accent
|
|
$cellFore = [System.Drawing.Color]::White
|
|
}
|
|
$cellBrush = New-Object System.Drawing.SolidBrush($cellBack)
|
|
$cellPen = New-Object System.Drawing.Pen($script:Theme.Border)
|
|
try {
|
|
$e.Graphics.FillRectangle($cellBrush, $e.Bounds)
|
|
if ($sender.GridLines) {
|
|
$e.Graphics.DrawRectangle($cellPen, $e.Bounds.X, $e.Bounds.Y, $e.Bounds.Width - 1, $e.Bounds.Height - 1)
|
|
}
|
|
[System.Windows.Forms.TextRenderer]::DrawText(
|
|
$e.Graphics, $e.SubItem.Text, $sender.Font, $e.Bounds,
|
|
$cellFore,
|
|
([System.Windows.Forms.TextFormatFlags]::VerticalCenter -bor
|
|
[System.Windows.Forms.TextFormatFlags]::Left -bor
|
|
[System.Windows.Forms.TextFormatFlags]::EndEllipsis)
|
|
)
|
|
} finally {
|
|
$cellBrush.Dispose()
|
|
$cellPen.Dispose()
|
|
}
|
|
})
|
|
|
|
foreach ($edition in $editions) {
|
|
$index = if ($edition.ImageIndex) { [string]$edition.ImageIndex } elseif ($edition.Index) { [string]$edition.Index } else { '?' }
|
|
$name = if ($edition.ImageName) { [string]$edition.ImageName } elseif ($edition.Name) { [string]$edition.Name } elseif ($edition.ImageDescription) { [string]$edition.ImageDescription } else { 'Name unavailable' }
|
|
$archValue = $null
|
|
foreach ($propertyName in @('Architecture', 'ImageArchitecture')) {
|
|
if ($edition.PSObject.Properties[$propertyName] -and $edition.$propertyName) {
|
|
$archValue = $edition.$propertyName
|
|
break
|
|
}
|
|
}
|
|
$arch = if ($null -ne $archValue) {
|
|
switch ([string]$archValue) {
|
|
'0' { 'x86' }
|
|
'5' { 'ARM' }
|
|
'9' { 'x64' }
|
|
'12' { 'ARM64' }
|
|
default { [string]$archValue }
|
|
}
|
|
} else { 'Unavailable' }
|
|
$version = if ($edition.Version) { [string]$edition.Version } else { 'Unavailable' }
|
|
|
|
$row = New-Object System.Windows.Forms.ListViewItem($index)
|
|
[void]$row.SubItems.Add($name)
|
|
[void]$row.SubItems.Add($arch)
|
|
[void]$row.SubItems.Add($version)
|
|
$row.Tag = $edition
|
|
[void]$editionList.Items.Add($row)
|
|
}
|
|
|
|
$buttonPanel = New-Object System.Windows.Forms.Panel
|
|
$buttonPanel.Dock = [System.Windows.Forms.DockStyle]::Bottom
|
|
$buttonPanel.Height = 52
|
|
$buttonPanel.BackColor = $script:Theme.Background
|
|
|
|
$cancelButton = New-Object System.Windows.Forms.Button
|
|
$cancelButton.Text = Get-DeployCenterText -Text 'Cancel'
|
|
$cancelButton.Size = New-Object System.Drawing.Size(100, 30)
|
|
$cancelButton.Anchor = 'Right,Top'
|
|
$cancelButton.Location = New-Object System.Drawing.Point(0, 10)
|
|
$cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
|
|
|
|
$validateButton = New-Object System.Windows.Forms.Button
|
|
$validateButton.Text = Get-DeployCenterText -Text 'Select edition'
|
|
$validateButton.Size = New-Object System.Drawing.Size(140, 30)
|
|
$validateButton.Anchor = 'Right,Top'
|
|
$validateButton.Location = New-Object System.Drawing.Point(0, 10)
|
|
$validateButton.Enabled = $false
|
|
|
|
$editionList.Add_SelectedIndexChanged({
|
|
$validateButton.Enabled = ($editionList.SelectedItems.Count -gt 0 -and $editionList.SelectedItems[0].SubItems[0].Text -ne '?')
|
|
})
|
|
$validateButton.Add_Click({
|
|
if ($editionList.SelectedItems.Count -gt 0) {
|
|
$resultForm.Tag = [pscustomobject]@{
|
|
SourcePath = $selectedPath
|
|
ImagePath = $imagePath
|
|
MountedImage = $resolvedImage.MountedImage
|
|
Edition = $editionList.SelectedItems[0].Tag
|
|
Index = [int]$editionList.SelectedItems[0].SubItems[0].Text
|
|
Name = $editionList.SelectedItems[0].SubItems[1].Text
|
|
}
|
|
$resultForm.DialogResult = [System.Windows.Forms.DialogResult]::OK
|
|
$resultForm.Close()
|
|
}
|
|
})
|
|
$editionList.Add_DoubleClick({
|
|
if ($editionList.SelectedItems.Count -gt 0 -and $validateButton.Enabled) {
|
|
$validateButton.PerformClick()
|
|
}
|
|
})
|
|
$buttonPanel.Controls.Add($cancelButton)
|
|
$buttonPanel.Controls.Add($validateButton)
|
|
$buttonPanel.Add_Resize({
|
|
$validateButton.Location = New-Object System.Drawing.Point(($buttonPanel.ClientSize.Width - $validateButton.Width - 12), 10)
|
|
$cancelButton.Location = New-Object System.Drawing.Point(($validateButton.Left - $cancelButton.Width - 8), 10)
|
|
})
|
|
|
|
$resultForm.Controls.Add($editionList)
|
|
$resultForm.Controls.Add($buttonPanel)
|
|
$resultForm.Controls.Add($sourceLabel)
|
|
$resultForm.AcceptButton = $validateButton
|
|
$resultForm.CancelButton = $cancelButton
|
|
Add-SessionFooter -TargetForm $resultForm -Theme $script:Theme -Font $script:FontSmall `
|
|
-IPv4 $script:DeployIPv4 -VncPassword $script:VncPassword -ShowFooter $false
|
|
$dialogResult = $resultForm.ShowDialog($dlg)
|
|
$chosenEdition = $resultForm.Tag
|
|
$resultForm.Dispose()
|
|
|
|
if ($dialogResult -eq [System.Windows.Forms.DialogResult]::OK -and $chosenEdition) {
|
|
if ($script:SelectedWindowsEdition -and
|
|
-not [string]::Equals([string]$script:SelectedWindowsEdition.SourcePath, [string]$chosenEdition.SourcePath, [StringComparison]::OrdinalIgnoreCase)) {
|
|
[void](Dismount-DeployCenterInstallImage -Image $script:SelectedWindowsEdition)
|
|
}
|
|
$script:SelectedWindowsEdition = $chosenEdition
|
|
$mountTransferred = $true
|
|
$lblStatus.Text = (Get-DeployCenterText -Text 'Selected edition:' ) + " $($chosenEdition.Name) (index $($chosenEdition.Index))."
|
|
& $script:SourceLogAction "Edition selected: $($chosenEdition.Name), index $($chosenEdition.Index), image $imagePath" 'SUCCESS'
|
|
$dlg.DialogResult = [System.Windows.Forms.DialogResult]::OK
|
|
$dlg.Close()
|
|
} else {
|
|
$lblStatus.Text = (Get-DeployCenterText -Text '{0} edition(s) found in {1}. No new edition selected.' -FormatArguments @($editions.Count, $selectedName))
|
|
}
|
|
& $script:SourceLogAction "Edition analysis complete: $imagePath ($($editions.Count) edition(s))." 'SUCCESS'
|
|
} catch {
|
|
$lblStatus.Text = (Get-DeployCenterText -Text "Failed to analyze editions in {0}." -FormatArguments @($selectedName))
|
|
& $script:SourceLogAction "Edition analysis error $selectedPath : $($_.Exception.Message)" 'ERROR'
|
|
Show-DeployCenterMessage -Message $_.Exception.Message -Title 'Edition analysis' -Icon Warning -Owner $dlg -Theme $script:Theme -Font $script:FontRegular
|
|
} finally {
|
|
if ($resolvedImage -and $resolvedImage.MountedImage -and -not $mountTransferred -and
|
|
(-not $script:SelectedWindowsEdition -or $resolvedImage.MountedImage -ne $script:SelectedWindowsEdition.MountedImage)) {
|
|
try { [void](Dismount-DeployCenterInstallImage -Image $resolvedImage) } catch {
|
|
& $script:SourceLogAction "Unable to dismount the ISO after analysis: $($_.Exception.Message)" 'WARNING'
|
|
}
|
|
}
|
|
$dlg.Cursor = [System.Windows.Forms.Cursors]::Default
|
|
$progressAnalysis.Visible = $false
|
|
$btnEditions.Enabled = $true
|
|
$btnScan.Enabled = $true
|
|
}
|
|
})
|
|
|
|
$list.Add_DoubleClick({
|
|
if ($list.SelectedItems.Count -gt 0 -and $btnEditions.Enabled) {
|
|
$btnEditions.PerformClick()
|
|
}
|
|
})
|
|
|
|
$btnScan.Add_Click({
|
|
$btnScan.Enabled = $false
|
|
$dlg.Cursor = [System.Windows.Forms.Cursors]::WaitCursor
|
|
$lblStatus.Text = Get-DeployCenterText -Text 'Searching the specified paths...'
|
|
$sourceState.AllFound = @()
|
|
$list.Items.Clear()
|
|
try {
|
|
# The scan never modifies DeployCenter.ini.
|
|
$sourceState.AllFound = @(Find-WindowsSources -RootPath $txtPath.Text -LogAction $script:SourceLogAction)
|
|
& $renderSourceList $txtFilter.Text
|
|
if (@($sourceState.AllFound).Count -eq 0) {
|
|
$lblStatus.Text = Get-DeployCenterText -Text 'No ISO/WIM/ESD source or extracted installation was found in the accessible paths.'
|
|
& $script:SourceLogAction 'No Windows source found in the accessible paths.' 'WARNING'
|
|
}
|
|
} catch {
|
|
$lblStatus.Text = Get-DeployCenterText -Text 'Search failed. See the message.'
|
|
& $script:SourceLogAction "Source detection error: $($_.Exception.Message)" 'ERROR'
|
|
Show-DeployCenterMessage -Message $_.Exception.Message -Title 'Source detection' -Icon Warning -Owner $dlg -Theme $script:Theme -Font $script:FontRegular
|
|
} finally {
|
|
$dlg.Cursor = [System.Windows.Forms.Cursors]::Default
|
|
$btnScan.Enabled = $true
|
|
}
|
|
})
|
|
|
|
Add-SessionFooter -TargetForm $dlg -Theme $script:Theme -Font $script:FontSmall `
|
|
-IPv4 $script:DeployIPv4 -VncPassword $script:VncPassword
|
|
[void]$dlg.ShowDialog($script:form)
|
|
$dlg.Dispose()
|
|
}
|
|
|
|
function Update-HardwareView {
|
|
$script:btnRefresh.Enabled = $false
|
|
$script:form.Cursor = [System.Windows.Forms.Cursors]::WaitCursor
|
|
|
|
try {
|
|
if (-not (Get-Command Get-HardwareInfo -ErrorAction SilentlyContinue)) {
|
|
throw 'The HardwareInventory module does not provide Get-HardwareInfo.'
|
|
}
|
|
$info = Get-HardwareInfo
|
|
|
|
$script:lblManufacturer.Text = (Get-DeployCenterText -Text 'Manufacturer:') + " $(Get-DeployCenterText -Text ([string]$info.Manufacturer))"
|
|
$script:lblModel.Text = (Get-DeployCenterText -Text 'Model:') + " $(Get-DeployCenterText -Text ([string]$info.Model))"
|
|
$script:lblSerial.Text = (Get-DeployCenterText -Text 'Serial number:') + " $(Get-DeployCenterText -Text ([string]$info.SerialNumber))"
|
|
$script:lblFirmware.Text = (Get-DeployCenterText -Text 'Firmware:') + " $(Get-DeployCenterText -Text ([string]$info.Firmware))"
|
|
$script:lblArchitecture.Text = (Get-DeployCenterText -Text 'Architecture:') + " $($info.Architecture)"
|
|
|
|
$script:HardwareInventory = $info
|
|
if (Get-Command Update-DeployCenterDriverStatus -ErrorAction SilentlyContinue) { Update-DeployCenterDriverStatus }
|
|
$previousDiskIndex = $null
|
|
if ($null -ne $script:cmbDisk.SelectedItem -and $null -ne $script:cmbDisk.SelectedItem.Tag) {
|
|
$previousDiskIndex = [string]$script:cmbDisk.SelectedItem.Tag
|
|
}
|
|
$script:RefreshingDiskList = $true
|
|
$script:cmbDisk.BeginUpdate()
|
|
try {
|
|
$script:cmbDisk.Items.Clear()
|
|
foreach ($disk in $info.Disks) {
|
|
$diskLabel = "Disk {0} - {1} - {2:N1} GB - {3} - {4}" -f $disk.Index, $disk.Model, $disk.SizeGB, $disk.BusType, $disk.Status
|
|
$diskChoice = [pscustomobject]@{ Text = $diskLabel; Tag = [string]$disk.Index }
|
|
[void]$script:cmbDisk.Items.Add($diskChoice)
|
|
}
|
|
$script:cmbDisk.DisplayMember = 'Text'
|
|
if ($script:cmbDisk.Items.Count -gt 0) {
|
|
$selectedIndex = -1
|
|
if ($null -ne $previousDiskIndex) {
|
|
for ($i = 0; $i -lt $script:cmbDisk.Items.Count; $i++) {
|
|
if ([string]$script:cmbDisk.Items[$i].Tag -eq $previousDiskIndex) { $selectedIndex = $i; break }
|
|
}
|
|
}
|
|
if ($selectedIndex -lt 0 -and $script:cmbDisk.Items.Count -eq 1) { $selectedIndex = 0 }
|
|
$script:cmbDisk.SelectedIndex = $selectedIndex
|
|
}
|
|
}
|
|
finally {
|
|
$script:cmbDisk.EndUpdate()
|
|
$script:RefreshingDiskList = $false
|
|
}
|
|
$script:SelectedDiskIndex = if ($script:cmbDisk.SelectedIndex -ge 0) { [string]$script:cmbDisk.SelectedItem.Tag } else { $null }
|
|
|
|
Write-DeployInventory -InventoryDir $script:InventoryDir -InventoryFile $script:InventoryFile -HardwareInfo $info -DeploymentIPv4 $script:DeployIPv4 -VncPassword $script:VncPassword -LogAction $script:SourceLogAction
|
|
|
|
& $script:SourceLogAction "Hardware inventory refreshed." "SUCCESS"
|
|
|
|
foreach ($errorMessage in $info.Errors) {
|
|
& $script:SourceLogAction $errorMessage "WARNING"
|
|
}
|
|
|
|
if ($info.Disks.Count -eq 0) {
|
|
& $script:SourceLogAction "No physical disk detected." "WARNING"
|
|
}
|
|
}
|
|
catch {
|
|
& $script:SourceLogAction "Inventory error: $($_.Exception.Message)" "ERROR"
|
|
if (-not $script:NonInteractive) {
|
|
Show-DeployCenterMessage -Message $_.Exception.Message -Title 'DeployCenter error' -Icon Error -Owner $script:form -Theme $script:Theme -Font $script:FontRegular
|
|
}
|
|
}
|
|
finally {
|
|
$script:btnRefresh.Enabled = $true
|
|
$script:form.Cursor = [System.Windows.Forms.Cursors]::Default
|
|
}
|
|
}
|
|
|
|
function Get-PartitionProfileSizingSelection {
|
|
param(
|
|
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Profile,
|
|
[Parameter(Mandatory=$true)]$DiskInfo,
|
|
[System.Collections.IDictionary]$InitialValues = @{}
|
|
)
|
|
|
|
$capacity = Get-DeployCenterPartitionSizingCapacity -Profile $Profile -DiskSizeBytes ([long]$DiskInfo.SizeBytes)
|
|
return Show-DeployCenterPartitionSizingDialog `
|
|
-Profile $Profile `
|
|
-AvailableGB $capacity.UsableGB `
|
|
-Theme $script:Theme `
|
|
-Font $script:FontRegular `
|
|
-Owner $script:form `
|
|
-InitialValues $InitialValues
|
|
}
|
|
|
|
function Set-PartitionProfileSizingSelection {
|
|
param(
|
|
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Profile,
|
|
[Parameter(Mandatory=$true)]$DiskInfo,
|
|
[System.Collections.IDictionary]$InitialValues = @{}
|
|
)
|
|
|
|
$selection = Get-PartitionProfileSizingSelection -Profile $Profile -DiskInfo $DiskInfo -InitialValues $InitialValues
|
|
if ($null -eq $selection) { return $false }
|
|
$script:SelectedPartitionSizing = New-DeployCenterPartitionSizingState -Profile $Profile -DiskInfo $DiskInfo -Selection $selection
|
|
return $true
|
|
}
|
|
|
|
function Update-DeployCenterLayout {
|
|
if ($null -eq $script:form -or $script:form.IsDisposed) { return }
|
|
$w = $script:form.ClientSize.Width
|
|
$h = $script:form.ClientSize.Height
|
|
$m = 24
|
|
$contentWidth = [Math]::Max(300, $w - (2 * $m))
|
|
|
|
# Header: keep the theme selector aligned to the right edge.
|
|
if ($null -ne $script:cmbTheme) {
|
|
$script:cmbTheme.Location = New-Object System.Drawing.Point([Math]::Max($m, $w - $m - $script:cmbTheme.Width), 25)
|
|
}
|
|
|
|
# The two upper panels keep their height but stretch horizontally.
|
|
$script:groupPC.Location = New-Object System.Drawing.Point($m, 100)
|
|
$script:groupPC.Size = New-Object System.Drawing.Size($contentWidth, 90)
|
|
$script:groupDisks.Location = New-Object System.Drawing.Point($m, 205)
|
|
# Keep the panel bottom clear of the action buttons at the minimum 800x600 layout.
|
|
$script:groupDisks.Size = New-Object System.Drawing.Size($contentWidth, 270)
|
|
Set-DiskSelectorWidth
|
|
|
|
$footerHeight = 26
|
|
$buttonHeightLocal = 38
|
|
$buttonYLocal = $h - $footerHeight - 14 - $buttonHeightLocal
|
|
|
|
$buttonWidthLocal = 140
|
|
$buttonGapLocal = 12
|
|
$script:btnClose.Size = New-Object System.Drawing.Size($buttonWidthLocal, $buttonHeightLocal)
|
|
$script:btnRefresh.Size = New-Object System.Drawing.Size($buttonWidthLocal, $buttonHeightLocal)
|
|
$script:btnDeploy.Size = New-Object System.Drawing.Size($buttonWidthLocal, $buttonHeightLocal)
|
|
$script:btnLog.Size = New-Object System.Drawing.Size($buttonWidthLocal, $buttonHeightLocal)
|
|
$script:btnClose.Location = New-Object System.Drawing.Point([Math]::Max($m, $w - $m - $buttonWidthLocal), $buttonYLocal)
|
|
$script:btnDeploy.Location = New-Object System.Drawing.Point([Math]::Max($m, $script:btnClose.Left - $buttonGapLocal - $buttonWidthLocal), $buttonYLocal)
|
|
$script:btnRefresh.Location = New-Object System.Drawing.Point([Math]::Max($m, $script:btnDeploy.Left - $buttonGapLocal - $buttonWidthLocal), $buttonYLocal)
|
|
$script:btnLog.Location = New-Object System.Drawing.Point($m, $buttonYLocal)
|
|
Set-DiskSelectorWidth
|
|
}
|