PowerShell Windows窗体包装器 [英] PowerShell Windows Forms Wrapper

查看:193
本文介绍了PowerShell Windows窗体包装器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在PowerShell中,通常使用



PowerShell Form-所需的控制代码:

  $ Form = Form-Control Form @ {Text =Dock test; StartPosition =CenterScreen ; Padding = 4; Activated = {$ Dock [0] .Select()}} 
$ Table = $ Form | FormLayoutPanel @ {RowCount = 2; ColumnCount = 2; ColumnStyles =(Percent,50 ),AutoSize; Dock =Fill}
$ Panel = $ Table | Form Panel @ {Dock =Fil L; BorderStyle =FixedSingle; BackColor =Teal} - 设置@ {RowSpan = 2}
$ Button = $ Panel |表单按钮@ {位置=50,50;尺寸=50,50; BackColor =Silver; Enabled = $ False}
$ Group = $ Table | Form GroupBox @ {Text =Dock; AutoSize = $ True}
$ Flow = $ Group | Form FlowLayoutPanel @ {AutoSize = $ True; FlowDirection =TopDown; Dock =填充; 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()


In PowerShell it is quiet common to use Windows Forms to build a User Interface for small cmdlets but the syntaxis required for this are often partly redundant and quiet verbose. This leads to the question: Is there a way to minimize the code required or does there exist a Windows Forms wrapper for PowerShell to reduce the verbose and redundant syntaxis?
I am not looking for the ShowUI as this solution is too heavy considering it based on Windows Presentation Foundation (see also: WPF vs WinForms) and the fact that it concerns a PowerShell module which makes it more difficult to deploy it than a wrapper function.

解决方案

In a lot of cases a wrapper in not required to make your code less verbose, take e.g. the lengthy WinForms PowerShell script here. Code pieces like this:

$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

Can easily be simplified in WinForms to a single line:

$Tab1.Padding = 3

And if the padding would be different for each side, PowerShell will automatically convert:

$Tab1.Padding = "4, 6, 4, 6"

Note: PowerShell does not convert $Tab1.Padding = "3" or $Tab1.Padding = "4, 6"

Nevertheless, the native way to create a windows form control is far from DRY (don't repeat yourself) programming. Although (multiple) properties can be added at creation (using:New-Object System.Windows.Forms.Button -Property @{Location = "75, 120"; Size = "75, 23"}) , multiple properties can't be set right away at a later state. Above that, it isn't quick and easy to add events1, child controls and container properties (as e.g. RowSpan), or any combination, intermediately at creation of a windows form control. Bottom line, you have to reference the windows form control over and over again to set its properties and more (with e.g. $OKButton.<property> = ... as in this 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"

That's why I have created a reusable PowerShell Form Control wrapper that let's you minimize Windows Forms (WinForms) code to it's essence.

1) unless you use On<event> methods, see also: addEventListener vs onclick

PowerShell Form-Control Wrapper

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

Syntax

Creating a control
<System.Windows.Forms.Control> = Form-Control [-Control <String>] [-Member <HashTable>]

Modifying a control
<Void> = Form-Control [-Control <System.Windows.Forms.Control>] [-Member <HashTable>]

Adding a (new) control to a container
<System.Windows.Forms.Control> = Form-Control [-Control <String>|<System.Windows.Forms.Control>] [-Member <HashTable>] [-Add <System.Windows.Forms.Control[]>]

Piping a container to a (new) control
<System.Windows.Forms.Control> = <System.Windows.Forms.Control> | Form-Control [-Control <String>|<System.Windows.Forms.Control>] [-Member <HashTable>] [-Set <HashTable>] [-PassParent]

Parameters

-Control <String>|<System.Windows.Forms.Control> (position 0, default: Form)
The -Control parameter accepts either a Windows form control type name ([String]) or an existing form control ([System.Windows.Forms.Control] ). Windows form control type names are like Form, Label, TextBox, Button, Panel, ..., etc. If a Windows form control type name ([String]) is supplied, the wrapper will create and return a new Windows form control with properties and settings as defined by the rest of the parameters.
If an existing Windows form control ([System.Windows.Forms.Control] ) is supplied, the wrapper will update the existing Windows form control using the properties and settings as defined by the rest of the parameters.

-Member <HashTable> (position 1)
Sets property values, invokes methods and add events on a new or existing object.

  • If the hash name represents property on the control, e.g. Size = "50, 50", the value will be assigned to the control property value.

  • If the hash name represents method on the control, e.g. Scale = {1.5, 1.5}, the control method will be invoked using the value for arguments .

  • If the hash name represents event on the control, take e.g. Click = {$Form.Close()}, the value ( [ScriptBlock]) will be added to the control events.

Two collection properties, ColumnStyles and RowStyles, are simplified especially for the TableLayoutPanel control which is considered a general substitute for the WPF Grid control: - The ColumnStyles property, clears all column widths and reset them with the ColumnStyle array supplied by the hash value. - The RowStyles property, clears all row Heigths and reset them with the RowStyle array supplied by the hash value.
Note: If want to add or insert a single specific ColumnStyle or RowStyle item, you need to fallback on the native statement, as e.g.: [Void]$Control.Control.ColumnStyles.Add((New-Object Windows.Forms.ColumnStyle("Percent", 100)).

-Add <Array>
The -Addparameter adds one or more child controls to the current control.
Note: the -add parameter cannot be used if container is piped to the control.

-Container <System.Windows.Forms.Control> (from pipeline)
The parent container is usually provided from the pipeline: $ParentContainer | Form $ChildControl and attached a (new) child control to the concerned container.

-Set <HashTable>
The -Setparameter sets (SetCellPosition, SetColumn, SetColumnSpan, SetRow, SetRowSpan and SetStyle) the specific child control properties related its parent panel container, e.g. .Set RowSpan = 2
Note: the -set column - and row parameters can only be used if a container is piped to the control.

-GetParent
By default the (child) control will be returned by the form-control function unless the -GetParent switch is supplied which will return the parent container instead. Note: the -set column - and row parameters can only be used if a container is piped to the control.

Examples

There are two way to setup the Windows Forms hierarchy:

  1. Adding a (new) control to a container
  2. Piping a container to a (new) control

Adding a (new) control to a container
For this example I have reworked the Creating a Custom Input Box at docs.microsoft.com using the PowerShell Form-Control wrapper:

$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
}

Note 1: Although the adding controls appears more structured especially for small forms, the drawback is that can't invoke methods that relate to both the parent container and child control (like -Set RowSpan).
Note 2: You might easily get lost in open and close parenthesis if try build child (or even grandchild) controls directly in a parent container (like the above Label control). Besides it more difficult to reference such a child (e.g. $OKButton vs. $Form.Controls["OKButton"], presuming you have set the button property Name = "OKButton)

Piping a container to a (new) control
For this example, I have created a user interface to test the dockproperty behavior. The form looks like this:

The PowerShell Form-Control code required for this:

$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 Windows窗体包装器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆