'Button doesn't register the right callback [duplicate]

I'm trying to create a powershell script with a GUI where I a button per file in a directory.

I have this loop to create the button:

$i = 100
foreach($file in Get-ChildItem $savePath -recurse -include *.json) {
    $filename = Split-Path $file.FullName -leaf
    $saveName = $filename -split "\."
    $btn                   = New-Object system.Windows.Forms.Button
    $saveName1 = $saveName[1]
    $btn.text              = "Load save $saveName1"
    $btn.location          = New-Object System.Drawing.Point(10,$i)
    $btn.width             = 200
    $btn.height            = 30
    $btn.Add_Click({
        [System.Windows.Forms.MessageBox]::Show($filename.clone() , $saveName1.clone())
        copy_and_print (New-Object String $filename.clone()) (New-Object String $saveName1.clone())
    })
    $LocalPrinterForm.controls.Add($btn)
    $i = $i + 40
}

But when I click on a button the values of $filename and $saveName1 are always the one corresponding to the last file (I've added .clone() to try to avoid references issues but it doesn't fix my issue).

For example if I have those files: a.json and b.json then the MessageBox will always print b.json independently of which button I click.

How do I make sure that the right callback get registered for each button?



Solution 1:[1]

Continuing from my comment, you can utilize the button control's .Tag property to store information for that particular button to use:

$i = 100
# using -Filter is MUCH faster than -Include
foreach($file in (Get-ChildItem $savePath -Recurse -Filter '*.json' -File)) {
    $btn          = New-Object system.Windows.Forms.Button
    $btn.text     = "Load save $($file.Extension)"
    $btn.location = New-Object System.Drawing.Point(10,$i)
    $btn.width    = 200
    $btn.height   = 30
    # save the needed properties as object in the Tags property of the button control
    $btn.Tag      = $file | Select-Object FullName, Name, Extension
    $btn.Add_Click({
        # inside the click handler, you can refer to the button object 
        # itself with automatic variable $this
        [System.Windows.Forms.MessageBox]::Show($this.Tag.Name, $this.Tag.Extension)
        copy_and_print $this.Tag.Name $this.Tag.Extension
    })
    $LocalPrinterForm.controls.Add($btn)
    $i += 40  # next y position
}

We do not know what your copy_and_print function does, but perhaps the first parameter should be the file's FullName. In that case change to

copy_and_print $this.Tag.FullName $this.Tag.Extension

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Theo