Reputation: 2952
I try to dynamically assign functions to dynamically created buttons. Unfortunately, variables are not being passed to the .add_Click({})
block as I expect it.
I tried already to create a global variable $Global:Var
, or to enter the variable in a hash table or created a function which I called in my click statement. However, at most I only got the last item from the list.
How can I dynamically allocate a variable to the $var.add_Click({})
block?
My code:
Foreach ($Tenant in $Customer){
if (-not ($Tenant -eq "")){
$Tenant_Button = New-Object -TypeName System.Windows.Forms.Button
$Tenant_Button.Location = New-Object -TypeName System.Drawing.Size(5, (15 + ($Tenant_Counter++ * 40)))
$Tenant_Button.Size = New-Object -TypeName System.Drawing.Size(125, 35)
$Tenant_Button.Text = $Tenant
$Tenant_Button.Font = $Font_AccountButton
#Below starts the .add_Click({}) part!#
#Above variables are not accessible in the below part.
$Tenant_Button.add_Click({
$Customer_Domain = ($CsvImport | Where-Object {$_.command_name -match "Admin $($Tenant_Button.Text)"}).command_name
$DomainCounter = 0
Foreach ($Domain in $Customer_Domain){
$Button_Cust = New-Object -TypeName System.Windows.Forms.Button
$Button_Cust.Location = New-Object -TypeName System.Drawing.Size(545, (25 + ($DomainCounter++ * 40)))
$Button_Cust.Size = New-Object -TypeName System.Drawing.Size(125, 35)
$Button_Cust.Text = $Domain
$MainWindow.Controls.Add($Button_Cust)
$Button_Cust.add_Click({
$FunctionIndex = [array]::IndexOf(($CsvImport).command_name, $Domain)
$Customer_Function = ($CsvImport[$FunctionIndex]).object_command
Invoke-Expression -Command $Customer_Function
})
}
})
$GroupBox_Acc.Controls.Add($Tenant_Button)
As mentioned before, with the global variable or the function or a hash table solution I managed to enter one variable content but always only the last. Hence, all buttons had the same function assigned. How can I assign twenty different functions to twenty different buttons?
To complete the example I provide the surrounding code. Enter the 'problematic' code in the $Button_Accounts.add_Click({})
(It can be found in the end of the below code) command.
$CsvImport = Import-Csv -Delimiter ',' -LiteralPath 'C:\Test\Coding\commands.csv'
$MainWindow = New-Object -TypeName System.Windows.Forms.Form
$MainWindow.Text = 'Administrator Window'
$MainWindow.Width = 600
$MainWindow.Height = 555
$MainWindow.AutoSize = $true
$Button_Accounts = New-Object -TypeName System.Windows.Forms.Button
$Button_Accounts.Location = New-Object -TypeName System.Drawing.Size(25, 225)
$Button_Accounts.Size = New-Object -TypeName System.Drawing.Size(200, 75)
$Button_Accounts.Text = 'Accounts'
$MainWindow.Controls.Add($Button_Accounts)
$ComboBox = New-Object -TypeName System.Windows.Forms.ComboBox
$GroupBox_Acc = New-Object -TypeName System.Windows.Forms.GroupBox
$Customer = $CsvImport.customer | Select-Object -Unique
$GroupBox_Acc.Text = 'Tenant List:'
$GroupBox_Acc.Location = New-Object -TypeName System.Drawing.Point(250, 25)
$GroupBox_Acc.Size = New-Object -TypeName System.Drawing.Size(270, 500)
$MainWindow.Controls.Add($GroupBox_Acc)
$Button_Accounts.Add_Click({
## Add above code here ##
})
$MainWindow.ShowDialog()
An example of the csv content:
command_group, Customer, command_name, object_commmand
Accounts, AAAA, Add Admin AP account, add_AP_admin_account
Accounts, AAAA, Add Admin AP Local account, add_AP_Local_admin_account
Accounts, BBBB, Add Admin ARL G account, add_ARLG_admin_account
Accounts, BBBB, Add Admin ARL CO account, add_ARLCO_admin_account
Upvotes: 3
Views: 1900
Reputation: 6860
So lets talk about why it doesnt work first.
The script block {}
is using the last value of the variable called before being run.
@("RED","BLUE","GREEN") | %{
$Team = $_
$Button.add_Click({
[System.Windows.MessageBox]::Show($Team)
})
}
$MainWindow.ShowDialog()
The Dialog will always pop back Green no matter if I hit the Red or Blue button it will still only popup Green. Thats because when the event for Click
is sent it takes what the very last value of the variable $Team
is.
So the fix is GetNewClosure()
@("RED","BLUE","GREEN") | %{
$Team = $_
$Button.add_Click({
[System.Windows.MessageBox]::Show($Team)
}.GetNewClosure())
}
$MainWindow.ShowDialog()
What GetNewClosure()
on the scriptblock {}
does is take the current value of the variables and stores them with the scriptblock. So if the variable changes it doesnt effect the variable being stored in the scriptblock.
The above code returns Red when Red is clicked, Blue when Blue is clicked and Green when Green is clicked.
Upvotes: 5
Reputation: 93
There are a few different ways you can write this. Using scriptblocks stored in variables is nice and easy to read, without any confusion over variable scope.
$clickOff = {
$this | Get-Member
$_
}
$btn_VMOff.add_Click($clickOff)
Another option, if you prefer to keep p.e. ClickOn and ClickOff code in actual functions, is to assign the handler like this:
Function ClickOff
{
$this | get-member
$_
}
$btn_VMOff.add_Click(${function:ClickOff})
Upvotes: 0