在PowerShell中,使用Windows Forms为小型cmdlet构建用户界面是很平常的,但是这需要的语法通常是部分冗余和安静的冗长。这导致了一个问题:是否有一种方法可以最小化所需的代码,或者是否存在PowerShell的Windows窗体包装器以减少冗长和冗余的语法? 我不是在寻找ShowUI,因为这个解决方案太重了,因为它基于Windows Presentation Foundation(另请参阅: WPF vs WinForms)以及它涉及PowerShell模块的事实,这使得部署它比包装函数更难。
在很多情况下,包装器不需要使您的代码更简洁,例如冗长的WinForms PowerShell脚本here。像这样的代码片段:
$System_Windows_Forms_Padding = New-Object System.Windows.Forms.Padding
$System_Windows_Forms_Padding.All = 3
$System_Windows_Forms_Padding.Bottom = 3
$System_Windows_Forms_Padding.Left = 3
$System_Windows_Forms_Padding.Right = 3
$System_Windows_Forms_Padding.Top = 3
$Tab1.Padding = $System_Windows_Forms_Padding
可以很容易地将WinForms简化为单行:
$Tab1.Padding = 3
如果每边的填充不同,PowerShell将自动转换:
$Tab1.Padding = "4, 6, 4, 6"
注意:PowerShell不会转换$Tab1.Padding = "3"
或$Tab1.Padding = "4, 6"
然而,创建窗体控件的本地方式远非DRY (don't repeat yourself) programming。虽然可以在创建时添加(多个)属性(使用:New-Object System.Windows.Forms.Button -Property @{Location = "75, 120"; Size = "75, 23"}
),但是在以后的状态下不能立即设置多个属性。除此之外,在创建窗体控件时,在中间添加events1,子控件和容器属性(例如RowSpan
)或任何组合并不快捷。最重要的是,您必须一遍又一遍地引用窗体控件以设置其属性等(例如$OKButton.<property> = ...
中的example):
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Point(75,120)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"
这就是为什么我创建了一个可重用的PowerShell Form Control包装器,让你最小化Windows Forms(WinForms)代码。
1)除非你使用On<event>
方法,另见:addEventListener vs onclick
Function Form-Control {
[CmdletBinding(DefaultParametersetName='Self')]param(
[Parameter(Position = 0)]$Control = "Form",
[Parameter(Position = 1)][HashTable]$Member = @{},
[Parameter(ParameterSetName = 'AttachChild', Mandatory = $false)][Windows.Forms.Control[]]$Add = @(),
[Parameter(ParameterSetName = 'AttachParent', Mandatory = $false)][HashTable]$Set = @{},
[Parameter(ParameterSetName = 'AttachParent', Mandatory = $false)][Alias("Parent")][Switch]$GetParent,
[Parameter(ParameterSetName = 'AttachParent', Mandatory = $true, ValueFromPipeline = $true)][Windows.Forms.Control]$Container
)
If ($Control -isnot [Windows.Forms.Control]) {Try {$Control = New-Object Windows.Forms.$Control} Catch {$PSCmdlet.WriteError($_)}}
$Styles = @{RowStyles = "RowStyle"; ColumnStyles = "ColumnStyle"}
ForEach ($Key in $Member.Keys) {
If ($Style = $Styles.$Key) {[Void]$Control.$Key.Clear()
For ($i = 0; $i -lt $Member.$Key.Length; $i++) {[Void]$Control.$Key.Add((New-Object Windows.Forms.$Style($Member.$Key[$i])))}
} Else {
Switch (($Control | Get-Member $Key).MemberType) {
"Property" {$Control.$Key = $Member.$Key}
"Method" {Invoke-Expression "[Void](`$Control.$Key($($Member.$Key)))"}
"Event" {Invoke-Expression "`$Control.Add_$Key(`$Member.`$Key)"}
Default {Write-Error("The $($Control.GetType().Name) control doesn't have a '$Key' member.")}
}
}
}
$Add | ForEach {$Control.Controls.Add($_)}
If ($Container) {$Container.Controls.Add($Control)}
If ($Set) {$Set.Keys | ForEach {Invoke-Expression "`$Container.Set$_(`$Control, `$Set.`$_)"}}
If ($GetParent) {$Container} Else {$Control}
}; Set-Alias Form Form-Control
创建一个控件
<System.Windows.Forms.Control> = Form-Control [-Control <String>] [-Member <HashTable>]
修改控件
<Void> = Form-Control [-Control <System.Windows.Forms.Control>] [-Member <HashTable>]
将(新)控件添加到容器中
<System.Windows.Forms.Control> = Form-Control [-Control <String>|<System.Windows.Forms.Control>] [-Member <HashTable>] [-Add <System.Windows.Forms.Control[]>]
将容器管道连接到(新)控件
<System.Windows.Forms.Control> = <System.Windows.Forms.Control> | Form-Control [-Control <String>|<System.Windows.Forms.Control>] [-Member <HashTable>] [-Set <HashTable>] [-PassParent]
-Control <String>|<System.Windows.Forms.Control>
(位置0,默认:Form
)
-Control
参数接受Windows窗体控件类型名称([String]
)或现有窗体控件([System.Windows.Forms.Control]
)。 Windows窗体控件类型名称类似于Form
, Label
, TextBox
, Button
, Panel
, ...
等。如果提供了Windows窗体控件类型名称([String]
),则包装器将创建并返回一个新的Windows窗体控件,其中包含由其余参数定义的属性和设置。
如果提供了现有的Windows窗体控件([System.Windows.Forms.Control]
),则包装器将使用其余参数定义的属性和设置更新现有的Windows窗体控件。
-Member <HashTable>
(位置1)
设置属性值,调用方法并在新对象或现有对象上添加事件。
property
,例如Size = "50, 50"
,该值将分配给控件属性值。method
,例如Scale = {1.5, 1.5}
,将使用参数值调用控制方法。event
,请参考例如Click = {$Form.Close()}
,值([ScriptBlock]
)将被添加到控制事件中。两个集合属性ColumnStyles
和RowStyles
特别针对TableLayoutPanel控件进行了简化,Grid控件被认为是WPF ColumnStyles
控件的一般替代: - ColumnStyle
属性,清除所有列宽并使用哈希值提供的RowStyles
数组重置它们。 - RowStyle
属性,清除所有行Heigth并使用散列值提供的[Void]$Control.Control.ColumnStyles.Add((New-Object Windows.Forms.ColumnStyle("Percent", 100))
数组重置它们。
注意:如果要添加或插入单个特定的ColumnStyle或RowStyle项,则需要回退本机语句,例如:-Add <Array>
。
-Add
-add
parameter将一个或多个子控件添加到当前控件。
注意:如果容器通过管道输出到控件,则无法使用-Container <System.Windows.Forms.Control>
参数。
$ParentContainer | Form $ChildControl
(来自管道)
父容器通常由管道提供:-Set <HashTable>
并将(新)子控件附加到相关容器。
-Set
SetCellPosition
parameter设置(SetColumn
,SetColumnSpan
,SetRow
,SetRowSpan
,SetStyle
和Set RowSpan = 2
)与其父面板容器相关的特定子控件属性,例如, .-set
注意:-GetParent
列 - 和行参数只能在容器通过管道输出到控件时使用。
form-control
默认情况下,-GetParent
函数将返回(子)控件,除非提供了-set
开关,它将返回父容器。注意:Creating a Custom Input Box列 - 和行参数只能在容器通过管道输出到控件时使用。
有两种方法可以设置Windows窗体层次结构:
将(新)控件添加到容器中
对于此示例,我使用PowerShell Form-Control包装器在docs.microsoft.com上重新编写了$TextBox = Form TextBox @{Location = "10, 40"; Size = "260, 20"}
$OKButton = Form Button @{Location = "75, 120"; Size = "75, 23"; Text = "OK"; DialogResult = "OK"}
$CancelButton = Form Button @{Location = "150, 120"; Size = "75, 23"; Text = "Cancel"; DialogResult = "Cancel"}
$Result = (Form-Control Form @{
Size = "300, 200"
Text = "Data Entry Form"
StartPosition = "CenterScreen"
KeyPreview = $True
Topmost = $True
AcceptButton = $OKButton
CancelButton = $CancelButton
} -Add (
(Form Label @{Text = "Please enter the information below:"; Location = "10, 20"; Size = "280, 20"}),
$TextBox, $OKButton, $CancelButton
)
).ShowDialog()
if ($result -eq [System.Windows.Forms.DialogResult]::OK)
{
$x = $TextBox.Text
$x
}
:
-Set RowSpan
注意1:虽然添加控件看起来更加结构化,特别是对于小表单,但缺点是无法调用与父容器和子控件(如Label
)相关的方法。
注意2:如果尝试直接在父容器(如上面的$OKButton
控件)中构建子控件(甚至孙子控件),您可能很容易迷失在打开和关闭的括号中。除了它更难以引用这样的孩子(例如$Form.Controls["OKButton"]
与Name = "OKButton
,假设你已设置按钮属性dock
)
将容器管道连接到(新)控件 在本例中,我创建了一个用户界面来测试property行为。表单如下所示:
$Form = Form-Control Form @{Text = "Dock test"; StartPosition = "CenterScreen"; Padding = 4; Activated = {$Dock[0].Select()}}
$Table = $Form | Form TableLayoutPanel @{RowCount = 2; ColumnCount = 2; ColumnStyles = ("Percent", 50), "AutoSize"; Dock = "Fill"}
$Panel = $Table | Form Panel @{Dock = "Fill"; BorderStyle = "FixedSingle"; BackColor = "Teal"} -Set @{RowSpan = 2}
$Button = $Panel | Form Button @{Location = "50, 50"; Size = "50, 50"; BackColor = "Silver"; Enabled = $False}
$Group = $Table | Form GroupBox @{Text = "Dock"; AutoSize = $True}
$Flow = $Group | Form FlowLayoutPanel @{AutoSize = $True; FlowDirection = "TopDown"; Dock = "Fill"; Padding = 4}
$Dock = "None", "Top", "Left", "Bottom", "Right", "Fill" | ForEach {
$Flow | Form RadioButton @{Text = $_; AutoSize = $True; Click = {$Button.Dock = $This.Text}}
}
$Close = $Table | Form Button @{Text = "Close"; Dock = "Bottom"; Click = {$Form.Close()}}
$Form.ShowDialog()
此代码所需的PowerShell表单控制代码:
qazxswpoi