2

What I am trying to accomplish is to create buttons that launch exe files in a certain directory when clicked, but when I try using a foreach loop to create a few buttons, all of the buttons just launch the file the last button is supposed to launch.

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()

Whatever I'm doing is probably pretty stupid, so I was just looking for any alternatives or solutions to this other than to just hard code everything.

1 Answer 1

2

It is likely that you need to use .GetNewClosure() ScriptBlock method so that each script block (button click event) holds the current value of the $file variable at the moment of enumeration.

Example of what this means:

$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

In that sense, and assuming this is the issue, the following should work:

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

A nice alternative to having a need to use .GetNewClosure() can be seen on this answer. The .Tag property of the Button can be used to store the information of the file's path which then can be used on the button's .Click event:

foreach ($file in $files) {
    $button = [System.Windows.Forms.Button]@{
        Width  = 100
        Height = 50
        Text   = $file
        Tag    = "$home\Downloads\$file"
    }
    $button.Add_Click({ Start-Process -FilePath $this.Tag })
    $flp.Controls.Add($button)
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.