Janelas PowerShell Forms Wrapper
em PowerShell é muito comum usar As formas do Windows para construir uma Interface de utilizador para pequenos cmdlets, mas a sintaxe necessária para isto é muitas vezes parcialmente redundante e verbosa silenciosa. Isto leva à questão:
Existe uma maneira de minimizar o código necessário ou existe uma capa do Windows para PowerShell para reduzir a sintaxe verbal e redundante?
Eu não estou procurando o ShowUI como esta solução é muito pesada considerando-a com base em Janelas Presentation Foundation (see also: WPF vs WinForms) {[[3]} and the fact that it concerns a PowerShell module which makes it more difficult to deploy it than a wrapper function.
1 answers
Em muitos casos, uma embalagem não é necessária para tornar o seu código menos descritivo, por exemplo, a longa escrita PowerShell WinForms aqui. Peças de código como esta:
$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
Pode ser facilmente simplificado em WinForms para uma única linha:
$Tab1.Padding = 3
E se o enchimento fosse diferente para cada lado, o PowerShell converter-se-ia automaticamente:
$Tab1.Padding = "4, 6, 4, 6"
Nota: o PowerShell não converte $Tab1.Padding = "3"
ou $Tab1.Padding = "4, 6"
New-Object System.Windows.Forms.Button -Property @{Location = "75, 120"; Size = "75, 23"}
) , múltiplas propriedades não podem ser definidas imediatamente num estado posterior. Acima disso, não é fácil e rápido adicionar eventos1, comandos para crianças e propriedades do contentor (como por exemplo RowSpan
), ou qualquer combinação, intermediadamente na criação de um comando de forma de janelas. Resumindo, tem de consultar o formulário do windows. controle uma e outra vez para definir suas propriedades e mais (com por exemplo $OKButton.<property> = ...
Como neste exemplo ) :
$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"
É por isso que criei um invólucro de controle de forma de PowerShell reutilizável que vamos minimizar o código de formas de Windows (WinForms) para a sua essência.
1) a menos que utilize On<event>
métodos, ver também: addEventListener vs onclick
Invólucro De Controlo De Forma De PowerShell
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
Sintaxe
Criar um controlo<System.Windows.Forms.Control> = Form-Control [-Control <String>] [-Member <HashTable>]
Modificar um controlo<Void> = Form-Control [-Control <System.Windows.Forms.Control>] [-Member <HashTable>]
Adicionar um (novo) controlo a um contentor<System.Windows.Forms.Control> = Form-Control [-Control <String>|<System.Windows.Forms.Control>] [-Member <HashTable>] [-Add <System.Windows.Forms.Control[]>]
Canalizar um recipiente para um (novo) controlo<System.Windows.Forms.Control> = <System.Windows.Forms.Control> | Form-Control [-Control <String>|<System.Windows.Forms.Control>] [-Member <HashTable>] [-Set <HashTable>] [-PassParent]
Parâmetros
-Control <String>|<System.Windows.Forms.Control>
(posição 0, predefinição: Form
)
O parâmetro -Control
aceita um nome do tipo de controlo do formulário do Windows ([String]
) ou um controlo do formulário existente ([System.Windows.Forms.Control]
). Tipo de controlo do formulário do Windows nomes são como Form
, Label
, TextBox
, Button
, Panel
, ...
, etc.
Se for fornecido um nome do tipo de controlo do formulário do Windows ([String]
), o invólucro irá criar e devolver um novo controlo do formulário do Windows com propriedades e configurações definidas pelo resto dos parâmetros.
Se for fornecido um controlo de formulários do Windows ([System.Windows.Forms.Control]
), o invólucro irá actualizar o controlo de formulários do Windows existente, usando as propriedades e configurações definidas pelo resto do parametro.
-Member <HashTable>
(posição 1)
Define os valores da propriedade, invoca métodos e adiciona eventos num objecto novo ou existente.
Se o nome hash representar
property
no controle, por exemploSize = "50, 50"
, o valor será atribuído ao valor da propriedade de controle.Se o nome hash representar
method
no controle, por exemploScale = {1.5, 1.5}
, o método de controle será invocado usando o valor para argumentos .Se o nome hash representar
event
no controle, por exemploClick = {$Form.Close()}
, o valor ([ScriptBlock]
) será adicionado aos eventos de controle.
Duas propriedades de colecção, ColumnStyles
e RowStyles
, são simplificadas especialmente para o controlo de TableLayoutPanel que é considerado um substituto geral para a grelha De Controlo WPF :
- O ColumnStyles
propriedade, limpa todas as larguras de colunas e repõe - as com a ColumnStyle
lista fornecida pelo valor de hash.
- O RowStyles
propriedade, limpa todos os Heigths da linha e reiniciá-los com o RowStyle
lista fornecida pelo valor de hash.
Nota: se quiser adicionar ou inserir um único item específico do Colunstyle ou do RowStyle, terá de recuar na instrução nativa, como por exemplo:: [Void]$Control.Control.ColumnStyles.Add((New-Object Windows.Forms.ColumnStyle("Percent", 100))
.
-Add <Array>
O parâmetro -Add
adiciona um ou mais comandos para crianças ao controlo actual.
Nota: -add
O parâmetro Não pode ser utilizado se o contentor for encaminhado para o controlo.
-Container <System.Windows.Forms.Control>
(do gasoduto)
O contentor de origem é normalmente fornecido a partir do gasoduto: $ParentContainer | Form $ChildControl
e anexado um (novo) controlo de crianças ao Contentor em causa.
-Set <HashTable>
Os conjuntos de parâmetros -Set
(SetCellPosition
, SetColumn
, SetColumnSpan
, SetRow
, SetRowSpan
e SetStyle
as propriedades específicas de controlo de crianças relacionavam o recipiente do painel Progenitor, por exemplo: .Set RowSpan = 2
Nota: os parâmetros da coluna e linha -set
só podem ser utilizados se o contentor for encaminhado para o controlo.
-GetParent
Por omissão, o controlo (filho) será devolvido pela função form-control
, a menos que seja fornecido o botão -GetParent
que irá devolver o contentor-mãe em alternativa.
Nota: os parâmetros da coluna e linha -set
só podem ser utilizados se um contentor for encaminhado para o controlo.
Exemplos
Há duas formas de configurar a hierarquia das formas do Windows:
- adicionar um (novo) controlo a um contentor
- canalizar um recipiente para um (novo) controlo
Adicionar um (novo) controlo a um contentor
Para este exemplo, eu retrabalhei o criando uma caixa de entrada personalizada em docs.microsoft.com usando o invólucro de controlo de forma de PowerShell:
$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
}
Nota 1: Embora os controles de adição aparecem mais estruturados especialmente para formas pequenas, a desvantagem é que não pode invocar métodos que se relacionam tanto com o contêiner pai e controle de crianças (como -Set RowSpan
).
Nota 2: {[82] } pode facilmente perder-se entre parêntesis abertos e fechados se tentar criar filhos (ou mesmo netos) controlar directamente num contentor parental (como o controlo acima Label
). Além disso, é mais difícil referenciar tal criança (por exemplo, $OKButton
vs. $Form.Controls["OKButton"]
, presumindo que você definiu o propriedade do botãoName = "OKButton
)
Canalizar um recipiente para um (novo) controlo
Para este exemplo, eu criei uma interface de usuário para testar o comportamento da propriedade dock
. O formulário parece-se com isto:
O código de controlo de forma PowerShell necessário para isto:
$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()