I want to Dynamically assign information to an "Add_Click" event on a GUI button in powershell.
So an overview - I am making a GUI tool, that loads some buttons for custom "fixes" for peoples PC's. Each button is loaded from a script, and each script contains variables for information such as, Button Name, Button Icon, as well as a function that does whatever the script is meant to do.
So as per example below, I am finding the problem is where I assign the Add_Click event. I'm sorry not sure how to explain better, but it does not seem to expand the $script variable, it will save the add_click as the variable, so that when I run the program, all buttons are using whatever was last stored in the variable $script (so all the buttons look different, but run the same script).
What I want to be able to do, is have each button use an Add_Click to run whatever script is stored in $script at the time the Add_Click is assigned in the "Foreach" loop.
Example script:
#Button Container (Array to store all the Buttons)
$ComputerActionButtons = @()
#Get all the script files that we will load as buttons
$buttonsFileArray = Get-ChildItem "$dir\Addin\ " | Select -ExpandProperty Name
#Load Buttons
$ButtonTempCount = 1
Foreach ($script in $buttonsFileArray){
. $dir\Addin\$script
Write-Host "Loading Button: $ButtonName"
# Generate button
$TempButton = New-Object System.Windows.Forms.Button
$TempButton.Location = New-Object System.Drawing.Size(140,20)
$TempButton.Size = New-Object System.Drawing.Size(100,100)
$TempButton.Text = "$ButtonName"
$TempButton.Image = $ButtonIcon
$TempButton.TextImageRelation = "ImageAboveText"
$TempButton.Add_Click({& $dir\Addin\$script})
$ComputerActionButtons += $TempButton
}
#Add Buttons to panel
$CAButtonLoc = 20
Foreach ($button in $ComputerActionButtons){
$button.Location = New-Object System.Drawing.Size(50,$CAButtonLoc)
$ComputerActionsPanel.Controls.Add($button)
$CAButtonLoc = $CAButtonLoc + 120
}
I have been researching variables, found you can do a New-Variable and Get-Variable command to dynamically set and get variable names, but using this does not seem to help because of the original problem.
I hope I have explained this well enough, please let me know if I can give more information to help.
Thank you,
Adam
Instead of using GetNewClosure, which create new dynamic module, capture local scope variables to it and bind ScriptBlock to that module, so in fact you lose access to any function defined not in global scope, you can just store any button specific data in the button itself. Control base class have Tag property where you can store any data you want.
$TempButton.Tag=@{Script=$script}
$TempButton.Add_Click({param($Sender) & "$dir\Addin\$($Sender.Tag.Script)"})
You need to create a closure over the $script value at the time of assignment, instead of relying on the value of $script at runtime.
Fortunately, all ScriptBlocks support this with the GetNewClosure() method.
foreach($i in 1..3){
$blocks += {echo $i}.GetNewClosure()
}
$blocks |Foreach-Object {$_.Invoke()}
which produces:
1
2
3
As opposed to
foreach($i in 1..3){
$blocks += {echo $i}
}
$blocks |Foreach-Object {$_.Invoke()}
which produces:
3
3
3
What GetNewClosure() does is that it "captures" the variables referenced inside the ScriptBlock at that time and binds it to the local scope of the scriptblock.
This way the original $i variable (or $script for that matter) is "hidden" beneath an $i variable that has the same value as $i used to have - namely at the time GetNewClosure() was called
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With