From 96de607e71dc0b89b5df2f411affc3c6a78e81b5 Mon Sep 17 00:00:00 2001 From: papanda925 Date: Wed, 2 Sep 2026 00:00:15 +0900 Subject: [PATCH] Improve high-DPI layouts and documentation --- .github/workflows/validate.yml | 17 +++ HiDPICustomGraphicalInputBoxSample.ps1 | 117 +++++++++++-------- HiDPIGraphicalDatePickerSample.ps1 | 107 +++++++++++------ HiDPIMultiple-selectionListBoxesSample.ps1 | 118 +++++++++++-------- HiDPISelectingItemsFromListBoxSample.ps1 | 127 +++++++++++--------- README.md | 130 ++++++++++++++++++--- Tests/Test-Repository.ps1 | 50 ++++++++ 7 files changed, 460 insertions(+), 206 deletions(-) create mode 100644 .github/workflows/validate.yml create mode 100644 Tests/Test-Repository.ps1 diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 0000000..4727d6a --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,17 @@ +name: Validate PowerShell samples + +on: + push: + pull_request: + +permissions: + contents: read + +jobs: + validate: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - name: Parse and validate samples + shell: pwsh + run: ./Tests/Test-Repository.ps1 diff --git a/HiDPICustomGraphicalInputBoxSample.ps1 b/HiDPICustomGraphicalInputBoxSample.ps1 index 5ffade0..62296b9 100644 --- a/HiDPICustomGraphicalInputBoxSample.ps1 +++ b/HiDPICustomGraphicalInputBoxSample.ps1 @@ -1,61 +1,84 @@ -#this sample is Based on is microsoft sample thit is Creating a Custom Input Box -#URL:https://docs.microsoft.com/en-us/powershell/scripting/samples/creating-a-custom-input-box?view=powershell-7.2 +# Based on Microsoft's "Creating a custom input box" PowerShell sample. +# This version adds DPI scaling and layout containers so controls remain visible. + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing -$form = New-Object System.Windows.Forms.Form +[System.Windows.Forms.Application]::EnableVisualStyles() + +$form = [System.Windows.Forms.Form]::new() $form.Text = 'Data Entry Form' -$form.Size = New-Object System.Drawing.Size(300,200) -$form.StartPosition = 'CenterScreen' - -#For High DPI, We Set AutoScaleDimensions and AutoScaleMode -#The reason SizeF is set to 96 is that the standard Windows resolution for the display is 96. -#Maybe you shuld try that All object resets Drawing.Point And Drawing.Size -#In some cases, review the Drawing.Point and Drawing.Size of all objects, depending on the resolution of your display. -$form.AutoScaleDimensions = New-Object System.Drawing.SizeF(96, 96) -$form.AutoScaleMode = [System.Windows.Forms.AutoScaleMode]::Dpi - -$okButton = New-Object System.Windows.Forms.Button -$okButton.Location = New-Object System.Drawing.Point(75,220) -$okButton.Size = New-Object System.Drawing.Size(75,50) -$okButton.Text = 'OK' -$okButton.DialogResult = [System.Windows.Forms.DialogResult]::OK -$form.AcceptButton = $okButton -$form.Controls.Add($okButton) +$form.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen +$form.ClientSize = [System.Drawing.Size]::new(480, 180) +$form.MinimumSize = [System.Drawing.Size]::new(400, 190) +$form.AutoScaleDimensions = [System.Drawing.SizeF]::new(96.0, 96.0) +$form.AutoScaleMode = [System.Windows.Forms.AutoScaleMode]::Dpi +$form.Topmost = $true -$cancelButton = New-Object System.Windows.Forms.Button -$cancelButton.Location = New-Object System.Drawing.Point(150,220) -$cancelButton.Size = New-Object System.Drawing.Size(200,50) -$cancelButton.Text = 'Cancel' -$cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel -$form.CancelButton = $cancelButton -$form.Controls.Add($cancelButton) +# Use the current Windows dialog font. Point-based fonts scale more naturally +# than a fixed, large pixel font when the display DPI changes. +$uiFont = [System.Drawing.SystemFonts]::MessageBoxFont.Clone() +$form.Font = $uiFont -$label = New-Object System.Windows.Forms.Label -$label.Location = New-Object System.Drawing.Point(10,20) -$label.Size = New-Object System.Drawing.Size(800,40) +$layout = [System.Windows.Forms.TableLayoutPanel]::new() +$layout.Dock = [System.Windows.Forms.DockStyle]::Fill +$layout.Padding = [System.Windows.Forms.Padding]::new(12) +$layout.ColumnCount = 1 +$layout.RowCount = 3 +[void]$layout.RowStyles.Add([System.Windows.Forms.RowStyle]::new([System.Windows.Forms.SizeType]::AutoSize)) +[void]$layout.RowStyles.Add([System.Windows.Forms.RowStyle]::new([System.Windows.Forms.SizeType]::Percent, 100)) +[void]$layout.RowStyles.Add([System.Windows.Forms.RowStyle]::new([System.Windows.Forms.SizeType]::AutoSize)) +$form.Controls.Add($layout) + +$label = [System.Windows.Forms.Label]::new() +$label.AutoSize = $true +$label.Margin = [System.Windows.Forms.Padding]::new(3, 3, 3, 10) $label.Text = 'Please enter the information in the space below:' -$form.Controls.Add($label) +$layout.Controls.Add($label, 0, 0) -$textBox = New-Object System.Windows.Forms.TextBox -$textBox.Location = New-Object System.Drawing.Point(10,100) -$textBox.Size = New-Object System.Drawing.Size(800,20) -#For High DPI, We Set Font Size -#In Japanese, "Yu Gothic UI" is the best font type. However, this font is for Japan only. Therefore, the default font definition is undefined. -#$Font = New-Object System.Drawing.Font("Yu Gothic UI",45,([System.Drawing.FontStyle]::Regular),[System.Drawing.GraphicsUnit]::Pixel) -$Font = New-Object System.Drawing.Font("",45,([System.Drawing.FontStyle]::Regular),[System.Drawing.GraphicsUnit]::Pixel) -$textBox.Font = $Font +$textBox = [System.Windows.Forms.TextBox]::new() +$textBox.Dock = [System.Windows.Forms.DockStyle]::Top +$layout.Controls.Add($textBox, 0, 1) -$form.Controls.Add($textBox) +$buttonPanel = [System.Windows.Forms.FlowLayoutPanel]::new() +$buttonPanel.AutoSize = $true +$buttonPanel.Dock = [System.Windows.Forms.DockStyle]::Fill +$buttonPanel.FlowDirection = [System.Windows.Forms.FlowDirection]::RightToLeft +$buttonPanel.WrapContents = $false +$layout.Controls.Add($buttonPanel, 0, 2) -$form.Topmost = $true +$cancelButton = [System.Windows.Forms.Button]::new() +$cancelButton.AutoSize = $true +$cancelButton.MinimumSize = [System.Drawing.Size]::new(80, 30) +$cancelButton.Text = 'Cancel' +$cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel +$buttonPanel.Controls.Add($cancelButton) + +$okButton = [System.Windows.Forms.Button]::new() +$okButton.AutoSize = $true +$okButton.MinimumSize = [System.Drawing.Size]::new(80, 30) +$okButton.Text = 'OK' +$okButton.DialogResult = [System.Windows.Forms.DialogResult]::OK +$buttonPanel.Controls.Add($okButton) -$form.Add_Shown({$textBox.Select()}) -$result = $form.ShowDialog() +$form.AcceptButton = $okButton +$form.CancelButton = $cancelButton +$form.Add_Shown({ $textBox.Select() }) + +$selectedText = $null +try { + if ($form.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { + $selectedText = $textBox.Text + } +} +finally { + $form.Dispose() + $uiFont.Dispose() +} -if ($result -eq [System.Windows.Forms.DialogResult]::OK) -{ - $x = $textBox.Text - $x +if ($null -ne $selectedText) { + $selectedText } diff --git a/HiDPIGraphicalDatePickerSample.ps1 b/HiDPIGraphicalDatePickerSample.ps1 index c2aa5e9..c343af7 100644 --- a/HiDPIGraphicalDatePickerSample.ps1 +++ b/HiDPIGraphicalDatePickerSample.ps1 @@ -1,50 +1,81 @@ -#This sample is Based on is microsoft sample thit is Graphical Date Picker -#URL:https://docs.microsoft.com/en-us/powershell/scripting/samples/creating-a-graphical-date-picker?view=powershell-7.2 +# Based on Microsoft's "Creating a graphical date picker" PowerShell sample. +# This version adds DPI scaling and layout containers so controls remain visible. + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing -$form = New-Object Windows.Forms.Form -Property @{ - StartPosition = [Windows.Forms.FormStartPosition]::CenterScreen - Size = New-Object Drawing.Size 243, 300 - Text = 'Select a Date' - Topmost = $true -} +[System.Windows.Forms.Application]::EnableVisualStyles() -#For High DPI, We Set AutoScaleDimensions and AutoScaleMode -#The reason SizeF is set to 96 is that the standard Windows resolution for the display is 96. -#Maybe you shuld try that All object resets Drawing.Point And Drawing.Size -#In some cases, review the Drawing.Point and Drawing.Size of all objects, depending on the resolution of your display. -$form.AutoScaleDimensions = New-Object System.Drawing.SizeF(96, 96) -$form.AutoScaleMode = [System.Windows.Forms.AutoScaleMode]::Dpi +$form = [System.Windows.Forms.Form]::new() +$form.Text = 'Select a Date' +$form.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen +$form.ClientSize = [System.Drawing.Size]::new(320, 280) +$form.MinimumSize = [System.Drawing.Size]::new(300, 270) +$form.AutoScaleDimensions = [System.Drawing.SizeF]::new(96.0, 96.0) +$form.AutoScaleMode = [System.Windows.Forms.AutoScaleMode]::Dpi +$form.Topmost = $true +$uiFont = [System.Drawing.SystemFonts]::MessageBoxFont.Clone() +$form.Font = $uiFont -$calendar = New-Object Windows.Forms.MonthCalendar -Property @{ - ShowTodayCircle = $false - MaxSelectionCount = 1 -} -$form.Controls.Add($calendar) +$layout = [System.Windows.Forms.TableLayoutPanel]::new() +$layout.Dock = [System.Windows.Forms.DockStyle]::Fill +$layout.Padding = [System.Windows.Forms.Padding]::new(12) +$layout.ColumnCount = 1 +$layout.RowCount = 2 +[void]$layout.RowStyles.Add([System.Windows.Forms.RowStyle]::new([System.Windows.Forms.SizeType]::Percent, 100)) +[void]$layout.RowStyles.Add([System.Windows.Forms.RowStyle]::new([System.Windows.Forms.SizeType]::AutoSize)) +$form.Controls.Add($layout) -$okButton = New-Object Windows.Forms.Button -Property @{ - Location = New-Object Drawing.Point 38, 500 - Size = New-Object Drawing.Size 75, 50 - Text = 'OK' - DialogResult = [Windows.Forms.DialogResult]::OK -} -$form.AcceptButton = $okButton -$form.Controls.Add($okButton) +$calendarPanel = [System.Windows.Forms.FlowLayoutPanel]::new() +$calendarPanel.Dock = [System.Windows.Forms.DockStyle]::Fill +$calendarPanel.FlowDirection = [System.Windows.Forms.FlowDirection]::LeftToRight +$calendarPanel.WrapContents = $false +$layout.Controls.Add($calendarPanel, 0, 0) -$cancelButton = New-Object Windows.Forms.Button -Property @{ - Location = New-Object Drawing.Point 113, 500 - Size = New-Object Drawing.Size 150, 50 - Text = 'Cancel' - DialogResult = [Windows.Forms.DialogResult]::Cancel -} +$calendar = [System.Windows.Forms.MonthCalendar]::new() +$calendar.MaxSelectionCount = 1 +$calendar.ShowTodayCircle = $false +$calendarPanel.Controls.Add($calendar) + +$buttonPanel = [System.Windows.Forms.FlowLayoutPanel]::new() +$buttonPanel.AutoSize = $true +$buttonPanel.Dock = [System.Windows.Forms.DockStyle]::Fill +$buttonPanel.FlowDirection = [System.Windows.Forms.FlowDirection]::RightToLeft +$buttonPanel.WrapContents = $false +$layout.Controls.Add($buttonPanel, 0, 1) + +$cancelButton = [System.Windows.Forms.Button]::new() +$cancelButton.AutoSize = $true +$cancelButton.MinimumSize = [System.Drawing.Size]::new(80, 30) +$cancelButton.Text = 'Cancel' +$cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel +$buttonPanel.Controls.Add($cancelButton) + +$okButton = [System.Windows.Forms.Button]::new() +$okButton.AutoSize = $true +$okButton.MinimumSize = [System.Drawing.Size]::new(80, 30) +$okButton.Text = 'OK' +$okButton.DialogResult = [System.Windows.Forms.DialogResult]::OK +$buttonPanel.Controls.Add($okButton) + +$form.AcceptButton = $okButton $form.CancelButton = $cancelButton -$form.Controls.Add($cancelButton) -$result = $form.ShowDialog() +$selectedDate = $null +try { + if ($form.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { + $selectedDate = $calendar.SelectionStart + } +} +finally { + $form.Dispose() + $uiFont.Dispose() +} -if ($result -eq [Windows.Forms.DialogResult]::OK) { - $date = $calendar.SelectionStart - Write-Host "Date selected: $($date.ToShortDateString())" +if ($null -ne $selectedDate) { + $selectedDate } diff --git a/HiDPIMultiple-selectionListBoxesSample.ps1 b/HiDPIMultiple-selectionListBoxesSample.ps1 index 523f3a9..91ba7ea 100644 --- a/HiDPIMultiple-selectionListBoxesSample.ps1 +++ b/HiDPIMultiple-selectionListBoxesSample.ps1 @@ -1,66 +1,82 @@ +# High-DPI-aware Windows Forms sample for selecting multiple items. + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing -$form = New-Object System.Windows.Forms.Form -$form.Text = 'Data Entry Form' -$form.Size = New-Object System.Drawing.Size(300,200) -$form.StartPosition = 'CenterScreen' +[System.Windows.Forms.Application]::EnableVisualStyles() -#For High DPI, We Set AutoScaleDimensions and AutoScaleMode -#The reason SizeF is set to 96 is that the standard Windows resolution for the display is 96. -#Maybe you shuld try that All object resets Drawing.Point And Drawing.Size -#In some cases, review the Drawing.Point and Drawing.Size of all objects, depending on the resolution of your display. -$form.AutoScaleDimensions = New-Object System.Drawing.SizeF(96, 96) -$form.AutoScaleMode = [System.Windows.Forms.AutoScaleMode]::Dpi +$form = [System.Windows.Forms.Form]::new() +$form.Text = 'Select Items' +$form.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen +$form.ClientSize = [System.Drawing.Size]::new(460, 320) +$form.MinimumSize = [System.Drawing.Size]::new(380, 280) +$form.AutoScaleDimensions = [System.Drawing.SizeF]::new(96.0, 96.0) +$form.AutoScaleMode = [System.Windows.Forms.AutoScaleMode]::Dpi +$form.Topmost = $true +$uiFont = [System.Drawing.SystemFonts]::MessageBoxFont.Clone() +$form.Font = $uiFont -$OKButton = New-Object System.Windows.Forms.Button -$OKButton.Location = New-Object System.Drawing.Point(75,160) -$OKButton.Size = New-Object System.Drawing.Size(75,50) -$OKButton.Text = 'OK' -$OKButton.DialogResult = [System.Windows.Forms.DialogResult]::OK -$form.AcceptButton = $OKButton -$form.Controls.Add($OKButton) +$layout = [System.Windows.Forms.TableLayoutPanel]::new() +$layout.Dock = [System.Windows.Forms.DockStyle]::Fill +$layout.Padding = [System.Windows.Forms.Padding]::new(12) +$layout.ColumnCount = 1 +$layout.RowCount = 3 +[void]$layout.RowStyles.Add([System.Windows.Forms.RowStyle]::new([System.Windows.Forms.SizeType]::AutoSize)) +[void]$layout.RowStyles.Add([System.Windows.Forms.RowStyle]::new([System.Windows.Forms.SizeType]::Percent, 100)) +[void]$layout.RowStyles.Add([System.Windows.Forms.RowStyle]::new([System.Windows.Forms.SizeType]::AutoSize)) +$form.Controls.Add($layout) -$CancelButton = New-Object System.Windows.Forms.Button -$CancelButton.Location = New-Object System.Drawing.Point(150,160) -$CancelButton.Size = New-Object System.Drawing.Size(200,50) -$CancelButton.Text = 'Cancel' -$CancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel -$form.CancelButton = $CancelButton -$form.Controls.Add($CancelButton) +$label = [System.Windows.Forms.Label]::new() +$label.AutoSize = $true +$label.Margin = [System.Windows.Forms.Padding]::new(3, 3, 3, 10) +$label.Text = 'Please make one or more selections from the list below:' +$layout.Controls.Add($label, 0, 0) -$label = New-Object System.Windows.Forms.Label -$label.Location = New-Object System.Drawing.Point(10,20) -$label.Size = New-Object System.Drawing.Size(800,60) -$label.Text = 'Please make a selection from the list below:' -$form.Controls.Add($label) +$listBox = [System.Windows.Forms.ListBox]::new() +$listBox.Dock = [System.Windows.Forms.DockStyle]::Fill +$listBox.SelectionMode = [System.Windows.Forms.SelectionMode]::MultiExtended +[void]$listBox.Items.AddRange([object[]]('Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5')) +$layout.Controls.Add($listBox, 0, 1) -$listBox = New-Object System.Windows.Forms.Listbox -$listBox.Location = New-Object System.Drawing.Point(75,80) -$listBox.Size = New-Object System.Drawing.Size(260,20) +$buttonPanel = [System.Windows.Forms.FlowLayoutPanel]::new() +$buttonPanel.AutoSize = $true +$buttonPanel.Dock = [System.Windows.Forms.DockStyle]::Fill +$buttonPanel.FlowDirection = [System.Windows.Forms.FlowDirection]::RightToLeft +$buttonPanel.WrapContents = $false +$layout.Controls.Add($buttonPanel, 0, 2) -$listBox.SelectionMode = 'MultiExtended' -#For High DPI, We Set Font Size -#In Japanese, "Yu Gothic UI" is the best font type. However, this font is for Japan only. Therefore, the default font definition is undefined. -#$Font = New-Object System.Drawing.Font("Yu Gothic UI",45,([System.Drawing.FontStyle]::Regular),[System.Drawing.GraphicsUnit]::Pixel) -$Font = New-Object System.Drawing.Font("",45,([System.Drawing.FontStyle]::Regular),[System.Drawing.GraphicsUnit]::Pixel) -$listBox.Font = $Font +$cancelButton = [System.Windows.Forms.Button]::new() +$cancelButton.AutoSize = $true +$cancelButton.MinimumSize = [System.Drawing.Size]::new(80, 30) +$cancelButton.Text = 'Cancel' +$cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel +$buttonPanel.Controls.Add($cancelButton) -[void] $listBox.Items.Add('Item 1') -[void] $listBox.Items.Add('Item 2') -[void] $listBox.Items.Add('Item 3') -[void] $listBox.Items.Add('Item 4') -[void] $listBox.Items.Add('Item 5') +$okButton = [System.Windows.Forms.Button]::new() +$okButton.AutoSize = $true +$okButton.MinimumSize = [System.Drawing.Size]::new(80, 30) +$okButton.Text = 'OK' +$okButton.DialogResult = [System.Windows.Forms.DialogResult]::OK +$buttonPanel.Controls.Add($okButton) -$listBox.Height = 70 -$form.Controls.Add($listBox) -$form.Topmost = $true +$form.AcceptButton = $okButton +$form.CancelButton = $cancelButton -$result = $form.ShowDialog() +$selectedItems = $null +try { + if ($form.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { + $selectedItems = @($listBox.SelectedItems) + } +} +finally { + $form.Dispose() + $uiFont.Dispose() +} -if ($result -eq [System.Windows.Forms.DialogResult]::OK) -{ - $x = $listBox.SelectedItems - $x +if ($null -ne $selectedItems) { + $selectedItems } diff --git a/HiDPISelectingItemsFromListBoxSample.ps1 b/HiDPISelectingItemsFromListBoxSample.ps1 index 8578e0b..e3c26e3 100644 --- a/HiDPISelectingItemsFromListBoxSample.ps1 +++ b/HiDPISelectingItemsFromListBoxSample.ps1 @@ -1,67 +1,90 @@ +# High-DPI-aware Windows Forms sample for selecting one item. + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing -$form = New-Object System.Windows.Forms.Form -$form.Text = 'Select a Computer' -$form.Size = New-Object System.Drawing.Size(300,200) -$form.StartPosition = 'CenterScreen' +[System.Windows.Forms.Application]::EnableVisualStyles() -#For High DPI, We Set AutoScaleDimensions and AutoScaleMode -#The reason SizeF is set to 96 is that the standard Windows resolution for the display is 96. -#Maybe you shuld try that All object resets Drawing.Point And Drawing.Size -#In some cases, review the Drawing.Point and Drawing.Size of all objects, depending on the resolution of your display. -$form.AutoScaleDimensions = New-Object System.Drawing.SizeF(96, 96) -$form.AutoScaleMode = [System.Windows.Forms.AutoScaleMode]::Dpi +$form = [System.Windows.Forms.Form]::new() +$form.Text = 'Select a Computer' +$form.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen +$form.ClientSize = [System.Drawing.Size]::new(460, 340) +$form.MinimumSize = [System.Drawing.Size]::new(380, 280) +$form.AutoScaleDimensions = [System.Drawing.SizeF]::new(96.0, 96.0) +$form.AutoScaleMode = [System.Windows.Forms.AutoScaleMode]::Dpi +$form.Topmost = $true +$uiFont = [System.Drawing.SystemFonts]::MessageBoxFont.Clone() +$form.Font = $uiFont -$okButton = New-Object System.Windows.Forms.Button -$okButton.Location = New-Object System.Drawing.Point(75,160) -$okButton.Size = New-Object System.Drawing.Size(75,50) -$okButton.Text = 'OK' -$okButton.DialogResult = [System.Windows.Forms.DialogResult]::OK -$form.AcceptButton = $okButton -$form.Controls.Add($okButton) - -$cancelButton = New-Object System.Windows.Forms.Button -$cancelButton.Location = New-Object System.Drawing.Point(150,160) -$cancelButton.Size = New-Object System.Drawing.Size(150,50) -$cancelButton.Text = 'Cancel' -$cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel -$form.CancelButton = $cancelButton -$form.Controls.Add($cancelButton) +$layout = [System.Windows.Forms.TableLayoutPanel]::new() +$layout.Dock = [System.Windows.Forms.DockStyle]::Fill +$layout.Padding = [System.Windows.Forms.Padding]::new(12) +$layout.ColumnCount = 1 +$layout.RowCount = 3 +[void]$layout.RowStyles.Add([System.Windows.Forms.RowStyle]::new([System.Windows.Forms.SizeType]::AutoSize)) +[void]$layout.RowStyles.Add([System.Windows.Forms.RowStyle]::new([System.Windows.Forms.SizeType]::Percent, 100)) +[void]$layout.RowStyles.Add([System.Windows.Forms.RowStyle]::new([System.Windows.Forms.SizeType]::AutoSize)) +$form.Controls.Add($layout) -$label = New-Object System.Windows.Forms.Label -$label.Location = New-Object System.Drawing.Point(10,20) -$label.Size = New-Object System.Drawing.Size(500,40) +$label = [System.Windows.Forms.Label]::new() +$label.AutoSize = $true +$label.Margin = [System.Windows.Forms.Padding]::new(3, 3, 3, 10) $label.Text = 'Please select a computer:' -$form.Controls.Add($label) +$layout.Controls.Add($label, 0, 0) -$listBox = New-Object System.Windows.Forms.ListBox -$listBox.Location = New-Object System.Drawing.Point(10,80) -$listBox.Size = New-Object System.Drawing.Size(260,20) -$listBox.Height = 80 -#For High DPI, We Set Font Size -#In Japanese, "Yu Gothic UI" is the best font type. However, this font is for Japan only. Therefore, the default font definition is undefined. -#$Font = New-Object System.Drawing.Font("Yu Gothic UI",45,([System.Drawing.FontStyle]::Regular),[System.Drawing.GraphicsUnit]::Pixel) -$Font = New-Object System.Drawing.Font("",45,([System.Drawing.FontStyle]::Regular),[System.Drawing.GraphicsUnit]::Pixel) -$listBox.Font = $Font +$listBox = [System.Windows.Forms.ListBox]::new() +$listBox.Dock = [System.Windows.Forms.DockStyle]::Fill +$listBox.SelectionMode = [System.Windows.Forms.SelectionMode]::One +[void]$listBox.Items.AddRange([object[]]( + 'atl-dc-001', + 'atl-dc-002', + 'atl-dc-003', + 'atl-dc-004', + 'atl-dc-005', + 'atl-dc-006', + 'atl-dc-007' +)) +$layout.Controls.Add($listBox, 0, 1) -[void] $listBox.Items.Add('atl-dc-001') -[void] $listBox.Items.Add('atl-dc-002') -[void] $listBox.Items.Add('atl-dc-003') -[void] $listBox.Items.Add('atl-dc-004') -[void] $listBox.Items.Add('atl-dc-005') -[void] $listBox.Items.Add('atl-dc-006') -[void] $listBox.Items.Add('atl-dc-007') +$buttonPanel = [System.Windows.Forms.FlowLayoutPanel]::new() +$buttonPanel.AutoSize = $true +$buttonPanel.Dock = [System.Windows.Forms.DockStyle]::Fill +$buttonPanel.FlowDirection = [System.Windows.Forms.FlowDirection]::RightToLeft +$buttonPanel.WrapContents = $false +$layout.Controls.Add($buttonPanel, 0, 2) -$form.Controls.Add($listBox) +$cancelButton = [System.Windows.Forms.Button]::new() +$cancelButton.AutoSize = $true +$cancelButton.MinimumSize = [System.Drawing.Size]::new(80, 30) +$cancelButton.Text = 'Cancel' +$cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel +$buttonPanel.Controls.Add($cancelButton) -$form.Topmost = $true +$okButton = [System.Windows.Forms.Button]::new() +$okButton.AutoSize = $true +$okButton.MinimumSize = [System.Drawing.Size]::new(80, 30) +$okButton.Text = 'OK' +$okButton.DialogResult = [System.Windows.Forms.DialogResult]::OK +$buttonPanel.Controls.Add($okButton) -$result = $form.ShowDialog() +$form.AcceptButton = $okButton +$form.CancelButton = $cancelButton + +$selectedItem = $null +try { + if ($form.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { + $selectedItem = $listBox.SelectedItem + } +} +finally { + $form.Dispose() + $uiFont.Dispose() +} -if ($result -eq [System.Windows.Forms.DialogResult]::OK) -{ - $x = $listBox.SelectedItem - $x +if ($null -ne $selectedItem) { + $selectedItem } diff --git a/README.md b/README.md index 3358edf..41d455f 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,124 @@ -# PowerShell_HiDPI_GUI_Sample +# PowerShell High-DPI Windows Forms Samples -This is PowerShell GUI Sample for High DPI Type Display. +PowerShellからWindows Formsのダイアログを作成し、高DPIディスプレイでも文字やボタンが見切れにくい画面配置を学ぶためのサンプル集です。 -Many Powershell GUI samples do not consider high DPI displays. -That is no exception at Microsoft. +各サンプルは1ファイルで実行できます。外部PowerShellモジュールやVisual Studioは不要です。 -ex:https://docs.microsoft.com/en-us/dotnet/api/system.drawing.sizef?view=net-6.0&viewFallbackFrom=windowsdesktop-6.0 +## 改善したポイント -So, I tryed to create High dpi GUI sample. +- 96 DPIを設計時の基準として`AutoScaleDimensions`を設定 +- `AutoScaleMode = Dpi`による自動スケーリング +- 固定の巨大なピクセルフォントをやめ、Windowsのダイアログ用フォントを使用 +- `TableLayoutPanel`と`FlowLayoutPanel`を使い、DPIや文字サイズが変わっても部品を再配置 +- `ClientSize`と`MinimumSize`を使用し、フォーム外にボタンが出ないように修正 +- OK/キャンセル、Enter/Escの標準的なダイアログ操作に対応 +- フォームとフォントを`finally`で確実に破棄 -# the type of GUI is there +## 動作環境 -| No| sample | -| ------------- | --------------- | -| 1 | Custom Graphical InputBox Sample | -| 2 | Graphical Date Picker Sample | -| 3 | Multiple-selection List Boxes Sample | -| 4 | Selecting Items from a List Box Sample | +- Windows +- Windows PowerShell 5.1、またはWindows上のPowerShell 7 +- .NETの`System.Windows.Forms`と`System.Drawing` - - -# Reference site +Windows Formsを使用するため、macOSとLinuxでは動作しません。 -https://stackoverflow.com/questions/185804/how-to-control-the-font-dpi-in-net-winforms-app +## サンプル一覧 -https://www.web-dev-qa-db-ja.com/ja/c%23/dpi%E5%AF%BE%E5%BF%9C%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E4%BD%9C%E6%88%90/970761106/ +| ファイル | 内容 | OKを押したときの出力 | +| --- | --- | --- | +| `HiDPICustomGraphicalInputBoxSample.ps1` | 文字入力ダイアログ | 入力した文字列 | +| `HiDPIGraphicalDatePickerSample.ps1` | カレンダー形式の日付選択 | 選択した`DateTime`値 | +| `HiDPIMultiple-selectionListBoxesSample.ps1` | リストから複数選択 | 選択した項目の配列 | +| `HiDPISelectingItemsFromListBoxSample.ps1` | リストから1つ選択 | 選択した項目 | +## 実行方法 + +PowerShellでリポジトリのフォルダーへ移動し、実行するファイルを指定します。 + +```powershell +Set-Location .\PowerShell_HiDPI_GUI_Sample +.\HiDPICustomGraphicalInputBoxSample.ps1 +``` + +他の例: + +```powershell +$selectedDate = .\HiDPIGraphicalDatePickerSample.ps1 +$selectedItems = .\HiDPIMultiple-selectionListBoxesSample.ps1 +$selectedComputer = .\HiDPISelectingItemsFromListBoxSample.ps1 +``` + +キャンセルした場合は値を出力しません。 + +### 実行ポリシーでブロックされた場合 + +まず、現在の設定を確認します。 + +```powershell +Get-ExecutionPolicy -List +``` + +組織管理のPCでは管理者の方針に従ってください。このリポジトリのサンプルは実行ポリシーを自動変更しません。 + +## 高DPI対応の要点 + +### 1. 96 DPIを基準値として記録する + +```powershell +$form.AutoScaleDimensions = [System.Drawing.SizeF]::new(96.0, 96.0) +$form.AutoScaleMode = [System.Windows.Forms.AutoScaleMode]::Dpi +``` + +96 DPIはWindows Forms上の設計基準です。実行時のDPIが異なる場合、Windows Formsがフォームと子コントロールの拡大率を計算します。 + +### 2. 固定座標だけに依存しない + +高DPI環境では、文字だけが大きくなってボタンや入力欄と重なることがあります。このサンプルでは、行と列で配置する`TableLayoutPanel`と、ボタンを順番に並べる`FlowLayoutPanel`を使っています。 + +### 3. フォントサイズをピクセルで固定しない + +```powershell +$uiFont = [System.Drawing.SystemFonts]::MessageBoxFont.Clone() +$form.Font = $uiFont +``` + +現在のWindows設定に合うダイアログ用フォントを使用します。個別に大きなフォントが必要な場合も、レイアウトパネルや`AutoSize`と組み合わせてください。 + +## 制限事項 + +- サンプルはWindows Forms向けであり、WPF/XAMLのDPI処理とは異なります。 +- PowerShellスクリプト単体では、通常の`.exe`アプリと同じ方法でDPI Awarenessマニフェストを埋め込めません。 +- 複数の異なるDPIのモニター間を移動する挙動は、PowerShellと.NETのバージョン、Windowsの設定によって異なる場合があります。 +- GUIの実表示はWindows環境で確認する必要があります。 + +## トラブルシューティング + +### 画面が表示されない + +Windows上で実行していることを確認してください。PowerShell 7を使う場合も、Windows版が必要です。 + +### 文字や部品の大きさが想定と違う + +Windowsの「設定」→「システム」→「ディスプレイ」で拡大率を確認してください。100%、125%、150%、200%など複数の拡大率で確認すると違いを把握しやすくなります。 + +### スクリプトの結果を再利用したい + +標準出力を変数へ代入します。 + +```powershell +$value = .\HiDPICustomGraphicalInputBoxSample.ps1 +``` + +## 参考資料 + +- [Windows Forms - Automatic scaling](https://learn.microsoft.com/dotnet/desktop/winforms/forms/autoscale) +- [ContainerControl.AutoScaleDimensions](https://learn.microsoft.com/dotnet/api/system.windows.forms.containercontrol.autoscaledimensions) +- [ContainerControl.AutoScaleMode](https://learn.microsoft.com/dotnet/api/system.windows.forms.containercontrol.autoscalemode) +- [Creating a custom input box](https://learn.microsoft.com/powershell/scripting/samples/creating-a-custom-input-box) +- [Creating a graphical date picker](https://learn.microsoft.com/powershell/scripting/samples/creating-a-graphical-date-picker) + +## English summary + +Standalone PowerShell/Windows Forms examples that demonstrate DPI-based automatic scaling, system dialog fonts, responsive layout panels, standard dialog behavior, and deterministic cleanup. diff --git a/Tests/Test-Repository.ps1 b/Tests/Test-Repository.ps1 new file mode 100644 index 0000000..0f9d12a --- /dev/null +++ b/Tests/Test-Repository.ps1 @@ -0,0 +1,50 @@ +$ErrorActionPreference = 'Stop' + +$repositoryRoot = Split-Path -Parent $PSScriptRoot +$sampleFiles = Get-ChildItem -LiteralPath $repositoryRoot -Filter '*.ps1' -File +$failures = [System.Collections.Generic.List[string]]::new() + +foreach ($sampleFile in $sampleFiles) { + $tokens = $null + $parseErrors = $null + [void][System.Management.Automation.Language.Parser]::ParseFile( + $sampleFile.FullName, + [ref]$tokens, + [ref]$parseErrors + ) + + foreach ($parseError in $parseErrors) { + $failures.Add("$($sampleFile.Name): $($parseError.Message)") + } + + $source = [System.IO.File]::ReadAllText($sampleFile.FullName) + if ($source -notmatch 'AutoScaleMode\]\:\:Dpi') { + $failures.Add("$($sampleFile.Name): AutoScaleMode.Dpi is required.") + } + if ($source -notmatch 'AutoScaleDimensions\s*=.+96\.0.+96\.0') { + $failures.Add("$($sampleFile.Name): 96-DPI design dimensions are required.") + } + if ($source -notmatch 'TableLayoutPanel') { + $failures.Add("$($sampleFile.Name): use a layout container instead of only fixed coordinates.") + } + if ($source -notmatch '\$form\.Dispose\(\)') { + $failures.Add("$($sampleFile.Name): dispose the form deterministically.") + } + if ($source -match 'Font\("",\s*45') { + $failures.Add("$($sampleFile.Name): do not use an unnamed 45-pixel font.") + } +} + +$readme = [System.IO.File]::ReadAllText((Join-Path $repositoryRoot 'README.md')) +foreach ($sampleFile in $sampleFiles) { + if ($readme -notmatch [regex]::Escape($sampleFile.Name)) { + $failures.Add("README does not list $($sampleFile.Name).") + } +} + +if ($failures.Count -gt 0) { + $failures | ForEach-Object { Write-Error $_ } + exit 1 +} + +Write-Host "Validated $($sampleFiles.Count) PowerShell samples."