winforms 在PowerShell中向函数传递对象时遇到问题

fruv7luv  于 11个月前  发布在  Shell
关注(0)|答案(1)|浏览(108)

这里是新用户。我不做任何编程/编码的贸易和有限的教育经验,所以我知道的大部分是从修修补补。我正在编写一个PowerShell脚本,构建一个GUI,并有一些麻烦更新一个GUI项目的基础上编辑另一个GUI项目。我已经简化了它,以显示我的问题。
下面是调用的函数:

function GUITitleTextBox_TextChanged
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory=$True, Position=0)][System.Object]    $Sender,
        [parameter(Mandatory=$True, Position=1)][System.EventArgs] $EventArguments,
        [parameter(Mandatory=$True, Position=2)][String]           $Text,
        [parameter(Mandatory=$False, Position=3)][System.Object]   $ControlToUpdate
    )
    
    Write-Host $Sender.Text
    Write-Host $Text
    Write-Host $ControlToUpdate.Text
}

字符串
下面是创建对象和添加事件的相关代码:

#Add the GUI Title textbox
$GUITitleTextBox       = New-Object System.Windows.Forms.TextBox
$GUITitleTextBox.Text  = "Default Form"
$GUITitleTextBox.Width = 240
$GUITitleTextBox.Add_TextChanged({GUITitleTextBox_TextChanged -Sender $this -EventArguments $_ -Text $this.Text -ControlToUpdate $GUITitleTextBox}) #Add an event when text changes.
$Form.Controls.Add($GUITitleTextBox) #Add the textbox to the Form


下面是控制台的输出:

Default For
Default For

Default Fo
Default Fo

Default F
Default F


使用$this传递给函数的任何东西都可以正常工作,就像两个Write-Host命令发送数据一样,但是当传递带有变量名($GUITitleTextBox)的对象时,它就不起作用了。我可以在创建对象的代码下面直接添加一个Write-Host $GUITitleTextBox.Text。如果在声明和示例化$GUITitleTextBox时,我执行$script:GUITitleTextBox将作用域设置为整个脚本,那么我当然可以更改Text属性。
更复杂的是,在另一个脚本上,我可以按照相同的格式将Windows窗体对象传递给一个函数。
其他脚本的函数:

function ContainerDimensionControlChanged()
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory=$True, Position=0)] [System.Object] $CallingControl,
        [parameter(Mandatory=$True, Position=1)] [System.Object] $Container,
        [parameter(Mandatory=$True, Position=2)] [string]        $Axis,
        [parameter(Mandatory=$False, Position=3)][System.Object] $ControlToUpdate
    )

    <Removed because it's not relevant>

    $ControlToUpdate.Value = $Container.$Axis
}


下面是调用它的代码($Panel和$WidthTextBox都是在运行此代码的同一函数中较早/较晚创建的其他表单元素):

$WidthSlider = New-Object System.Windows.Forms.TrackBar
$WidthSlider.Top = 45
$WidthSlider.Left = 330
$WidthSlider.Name = "WidthSlider"
$WidthSlider.AutoSize = $true
$WidthSlider.Minimum = 5
$WidthSlider.Maximum = 750
$WidthSlider.Value = $PanelDefaultWidth
$WidthSlider.Add_ValueChanged({ContainerDimensionControlChanged $This $Panel "Width" $WidthTextBox})


那么为什么第二个工作,而第一个不工作?我错过了什么?

hm2xizp9

hm2xizp91#

PowerShell的$this变量在事件处理程序的脚本块中使用时并不总是您所期望的。
在你的第一个脚本中,$this并没有引用$GUITitleTextBox,而是引用了脚本块运行的上下文,而不是你的文本框,这就是为什么它没有按预期工作。相反,使用$sender,它被传递给函数并引用了引发事件的控件。
下面是更正后的事件处理程序添加行:

$GUITitleTextBox.Add_TextChanged({
    GUITitleTextBox_TextChanged -Sender $GUITitleTextBox -EventArguments $_ -Text $GUITitleTextBox.Text -ControlToUpdate $GUITitleTextBox
})

字符串

根据评论更新

在PowerShell中,当您在事件中使用脚本块(匿名函数)时,在事件注册时不会捕获其中使用的变量,除非您强制它们与use语句一起使用或通过适当地确定它们的范围。以下是如何正确地将$ControlToUpdate传递给事件处理程序中的函数:

  • 使用GetNewClosure()方法创建一个闭包,将变量的当前状态绑定到事件处理程序的脚本块。

下面是完整的更正代码,以确保$ControlToUpdate被函数接收:

function GUITitleTextBox_TextChanged {
    param (
        [Parameter(Mandatory=$True)][System.Object] $Sender,
        [Parameter(Mandatory=$True)][System.EventArgs] $EventArguments,
        [Parameter(Mandatory=$True)][String] $Text,
        [Parameter(Mandatory=$True)][System.Windows.Forms.Control] $ControlToUpdate
    )
    
    $ControlToUpdate.Text = $Text
}

$GUITitleTextBox = New-Object System.Windows.Forms.TextBox
$GUITitleTextBox.Text = "Default Form"
$GUITitleTextBox.Width = 240

# Ensure that the $GUITitleTextBox is captured in the script block's closure
$handler = {
    GUITitleTextBox_TextChanged -Sender $GUITitleTextBox -EventArguments $_ -Text $GUITitleTextBox.Text -ControlToUpdate $GUITitleTextBox
}.GetNewClosure()

$GUITitleTextBox.Add_TextChanged($handler)

$Form = New-Object System.Windows.Forms.Form
$Form.Controls.Add($GUITitleTextBox)

# Other form setup here...

# Show the form
$Form.ShowDialog()


通过使用GetNewClosure(),我们确保用作事件处理程序的脚本块捕获了$GUITitleTextBox的当前状态,因此当文本更改时,它会将当前控制传递给函数。

相关问题