PowerShell Add_Click 在 foreach 循环中

PowerShell Add_Click in foreach loop

我想要完成的是创建在单击时在特定目录中启动 exe 文件的按钮,但是当我尝试使用 foreach 循环创建几个按钮时,所有按钮都只是在最后启动文件按钮应该会启动。

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

$form = New-Object System.Windows.Forms.Form
$form.Text = 'Main Window'
$form.Size = New-Object System.Drawing.Size(600,400)

$flp = New-Object System.Windows.Forms.FlowLayoutPanel
$flp.Location = New-Object System.Drawing.Point(0,0)
$flp.Height = $form.Height
$flp.Width = $form.Width
$form.Controls.Add($flp)

$files = Get-ChildItem "$home\Downloads" -Include *.exe -Name

foreach ($file in $files){
    $button = New-Object System.Windows.Forms.Button
    $flp.Controls.Add($button)
    $button.Width = 100
    $button.Height = 50
    $button.Text = $file
    $button.Add_Click{
        Start-Process -FilePath "$home\Downloads$file"
    }
}

$form.Topmost = $true
$form.ShowDialog()

无论我在做什么可能都非常愚蠢,所以我只是在寻找任何替代方案或解决方案,而不是对所有内容进行硬编码。

很可能你需要使用.GetNewClosure() ScriptBlock method,这样每个脚本块(按钮点击事件)在枚举的时候都保存着$file变量的当前值。

这意味着什么的例子:

$blocks = foreach($i in 0..5) {
    { "hello $i" }
}
& $blocks[0] # => hello 5
& $blocks[1] # => hello 5

$blocks = foreach($i in 0..5) {
    { "hello $i" }.GetNewClosure()
}
& $blocks[0] # => hello 0
& $blocks[1] # => hello 1

从这个意义上说,假设这是问题所在,以下应该有效:

foreach ($file in $files) {
    $button        = New-Object System.Windows.Forms.Button
    $button.Width  = 100
    $button.Height = 50
    $button.Text   = $file
    $thisEvent     = {
        Start-Process -FilePath "$home\Downloads$file"
    }.GetNewClosure()
    $button.Add_Click($thisEvent)
    $flp.Controls.Add($button)
}

可以在 this answer. The .Tag property of the Button 上看到需要使用 .GetNewClosure() 的一个很好的替代方法可以用来存储文件路径的信息,然后可以在按钮的 .Click 事件:

foreach ($file in $files) {
    $button        = New-Object System.Windows.Forms.Button
    $button.Width  = 100
    $button.Height = 50
    $button.Text   = $file
    # Store the file's path in the Tag's property of this Button
    $button.Tag    = "$home\Downloads$file"
    $button.Add_Click({
        Start-Process -FilePath $this.Tag
    })
    $flp.Controls.Add($button)
}