提问人:Mikepedia 提问时间:11/5/2023 更新时间:11/5/2023 访问量:50
在 PowerShell 中将对象传递给函数时遇到问题
Trouble Passing Objects to Functions in PowerShell
问:
新用户在这里。我不做任何编程/编码,教育经验有限,所以我所知道的大部分都是从修修补补中得出的。我正在开发一个构建 GUI 的 PowerShell 脚本,并且在编辑另一个 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})
那么,为什么第二个有效,而第一个无效呢?我错过了什么?
答:
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
评论