none
Send parameters to button.add_click function created dynamically

    Question

  • Hi,

    I am creating a form with a TabControl that includes dynimically created TabPage, containing each on and off buttons.

    The way it is currently done, I am struggling to get the object I am currently dealing with to make sure I apply what I want on the proper element.

    On each TabPage I have

    $btn_VMOff.add_Click({ (ClickOff $Idx) })

    but $Idx is always empty Since I suspect it is keeping the variable as opposed to the value I would like to pass as a parameter.

    I have tried with $($Idx), "$Idx", b ut it makes no difference.

    Currently, my function ClickOff is as follow

    Function ClickOff([string]$Parametre)
    { msgbox "Titre" $Parametre "Information"
    }

    Any idea on how to do this ?

    Many Thanks.


    Monday, August 19, 2013 3:04 PM

Answers

  • The problem is in how you're assigning the delegates:

    $btn_VMOff.add_Click({ (ClickOff $Idx) })

    The $this and $_ variables are active inside the script block within the curly braces here.  Within that script block, you're calling the ClickOff function yourself, and not passing it the $this or $_ variables as arguments.

    There are a few different ways you can write this, but I prefer saving my ScriptBlocks to a variable, rather than giving them actual function names.  It's nice and easy to read, without any confusion over variable scope.

    $clickOff = {
        $this | Get-Member
        $_
    }
    
    $btn_VMOff.add_Click($clickOff)

    Edit:

    Another option, if you prefer to keep your 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})


    Tuesday, August 20, 2013 1:14 PM
  • David is right. Here is a little sample that shows all the options:

    $null=[Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
    $form = New-Object Windows.Forms.Form
    $btn1 = New-Object Windows.Forms.Button
    $btn1.Text = "Button1"
    $btn2 = New-Object Windows.Forms.Button
    $btn2.Text = "Button2"
    $btn2.Left=100
    $btn3 = New-Object Windows.Forms.Button
    $btn3.Text = "Button3"
    $btn3.Top=100
    $btn4 = New-Object Windows.Forms.Button
    $btn4.Text = "Button4"
    $btn4.Top=100
    $btn4.Left=100
    $form.Controls.Add($btn1) 
    $form.Controls.Add($btn2)
    $form.Controls.Add($btn3)
    $form.Controls.Add($btn4)
    
    function OnClick($Sender, $EventArgs){ 
    	write-host $Sender,$EventArgs 
    }
    
    function OnClick2{ 
    	Write-Host $this,$_ 
    }
    
    $sbOnClick3={
    	Write-Host $this,$_ 
    }
    
    $sbOnClick4={ param($Sender,$EventArgs)
    	Write-Host $Sender,$EventArgs
    }
    
    $btn1.add_Click({OnClick $this $_})
    $btn2.add_Click({OnClick2})
    $btn3.add_Click($sbOnClick3)
    $btn4.add_Click($sbOnClick4)
    $form.ShowDialog()

    Tuesday, August 20, 2013 2:07 PM
  • Hi David,

    Thank you for you explainations.

    I actually had to find a work around since I couldn't find a way to access the objects created dynamically within my functions.

    $This was OK for the actual control (button) but I needed to change a few properties on the other TabPage elements and I couldn't find a way to do it.

    So I am storing them into hash tables and the Index is in the .tag property.

    Well educated powershell developpers are probably going to find it uggly, but for the time being, it works and this is eventually what matters :-)

    Thank you very much to Dirk and yourself for your help.

    Tuesday, August 20, 2013 2:17 PM

All replies

  • Hi Serge,

    you can read up on Windows.Forms eventhandlers through PowerShell here http://blogs.msdn.com/b/powershell/archive/2008/05/24/wpf-powershell-part-3-handling-events.aspx

    Basically within the event handler function you can access $this and $_ which are the Sender and EventArgs:

    function ClickOff
    { 
       #access the sender
       $this
       #access EventArgs
       $_
    }

    Monday, August 19, 2013 3:35 PM
  • Hi Dirk,

    Sorry, I must be missing something obvious.

    Neither $this or $_ are giving me something. They seem both null, I ma not getting anyhting from them. Even $this | get-member doesn't return anything.

    Monday, August 19, 2013 4:00 PM
  • Hi Serge,

    if you post the code you are using now (at least the essential part), I might be able to see what's going wrong.

    Tuesday, August 20, 2013 7:20 AM
  • Hi Dirk,

    Thank you very much for your help.

    Here is the code I am using to dynamically create my tabs with the buttons.

    Function AddNewTab ($TabName)
    { $Idx = $Script:TabVMs.Count
     $TmpTab = New-Object System.Windows.Forms.TabPage

     $TmpTab.DataBindings.DefaultDataSourceUpdateMode = 0
     $System_Drawing_Point = New-Object System.Drawing.Point
     $System_Drawing_Point.X = 4
     $System_Drawing_Point.Y = 22
     $TmpTab.Location = $System_Drawing_Point
     $TmpTab.Name = $TabName
     $System_Windows_Forms_Padding = New-Object System.Windows.Forms.Padding
     $System_Windows_Forms_Padding.All = 3
     $System_Windows_Forms_Padding.Bottom = 3
     $System_Windows_Forms_Padding.Left = 3
     $System_Windows_Forms_Padding.Right = 3
     $System_Windows_Forms_Padding.Top = 3
     $TmpTab.Padding = $System_Windows_Forms_Padding
     $System_Drawing_Size = New-Object System.Drawing.Size
     $System_Drawing_Size.Height = 393
     $System_Drawing_Size.Width = 555
     $TmpTab.Size = $System_Drawing_Size
     $TmpTab.TabIndex = 0
     $TmpTab.Text = $TabName
     $TmpTab.UseVisualStyleBackColor = $True
     $TmpTab.add_Click($handler_TabPage:_Click)
     $TmpTab.Tag = $Idx

     $System_Drawing_Point = New-Object System.Drawing.Point
     $System_Drawing_Point.X = 40
     $System_Drawing_Point.Y = 90
     $System_Drawing_Size = New-Object System.Drawing.Size
     $System_Drawing_Size.Height = 36
     $System_Drawing_Size.Width = 39

     $btn_VMOff = New-Object System.Windows.Forms.Button

     $btn_VMOff.DataBindings.DefaultDataSourceUpdateMode = 0
     $btn_VMOff.Font = New-Object System.Drawing.Font("Microsoft Sans Serif",8.25,1,3,1)
     $btn_VMOff.ForeColor = [System.Drawing.Color]::FromArgb(255,255,0,0)
     $btn_VMOff.Image = [System.Drawing.Image]::FromFile("C:\Scripts\Powershell\Manage\Images\rouge.ico")
     $btn_VMOff.Location = $System_Drawing_Point
     $btn_VMOff.Name = "btn_VMOff" + $Idx
     $btn_VMOff.Size = $System_Drawing_Size
     $btn_VMOff.TabIndex = 1
     $btn_VMOff.UseVisualStyleBackColor = $True
     $btn_VMOff.Visible = $True
     $btn_VMOff.add_Click({ ClickOff $Idx })

     $TmpTab.Controls.Add($btn_VMOff)

     $btn_VMOn = New-Object System.Windows.Forms.Button
     $btn_VMOn.DataBindings.DefaultDataSourceUpdateMode = 0
     $btn_VMOn.Font = New-Object System.Drawing.Font("Microsoft Sans Serif",8.25,1,3,1)
     $btn_VMOn.ForeColor = [System.Drawing.Color]::FromArgb(255,0,255,0)
     $btn_VMOn.Image = [System.Drawing.Image]::FromFile("C:\Scripts\Powershell\Manage\Images\vert.ico")
     $btn_VMOn.Location = $System_Drawing_Point
     $btn_VMOn.Name = "btn_VMOn" + $Idx
     $btn_VMOn.Size = $System_Drawing_Size
     $btn_VMOn.TabIndex = 2
     $btn_VMOn.UseVisualStyleBackColor = $True
     $btn_VMOn.Visible = $False
     $btn_VMOn.add_Click({ ClickOn $Idx })

     $TmpTab.Controls.Add($btn_VMOn)
     
     $Script:TabVMs += $TmpTab
    }

     The I have these functions for each buttons.

    Function ClickOff($Parametre)
    { $This | get-member
     $_
     msgbox "Titre" "$Parametre" "Information"
    }

    Function ClickOn($Parametre)
    { $This | get-member
     $_
     msgbox "Titre" "$Parametre" "Information"
    }

    and the way I add the tabs to my forms. The Tab generation doesn't seem to create any problem.

    AddNewTab "NetApp"
    $Panneaux.Controls.Add($TabVMs[$TabVMs.Count-1])

    Is the fact that I am always using the same button variable within the procedure could be the issue ?

    I am not sure about this since the variable is the added to the tab and seems to belong to it.

    When I click on the button, I have my message box coming up, but empty.

    Tuesday, August 20, 2013 8:30 AM
  • Hi Serge,

    I think I can see the problem now. In your Event handler functions you use a named parameter. You can either use two named parameters (those get $_ and $this assigned automatically) and refer to them by name or no parameters at all and refer to $_ and $this:

    #change your event handler functions to either one of the variants below Function ClickOff($Sender,$eventArgs) { $Sender | get-member $eventArgs }

    Function ClickOff
    {
       $this | get-member
       $_
    }


    Tuesday, August 20, 2013 9:14 AM
  • Hi Dirk,

    Sorry, I must be thick but none of the 2 options work fully with my code.

    I event tried to call the function with the parameter's name ClickOff -Parametre $Idx With no luck

    If I use $This and $_, I get System.Windows.Forms.MouseEventArgs in my message box reading $_

    With the other option, nothing specified in the function, I get nothing with $Sender and $eventArgs.

    However, I get something with $This and its properties. So for now, I am going to store my $Idx in the Tag field so I can identify which button has been clicked !

    Not pretty but that works !

    Many thanks for your time and help.

    Tuesday, August 20, 2013 9:46 AM
  • The problem is in how you're assigning the delegates:

    $btn_VMOff.add_Click({ (ClickOff $Idx) })

    The $this and $_ variables are active inside the script block within the curly braces here.  Within that script block, you're calling the ClickOff function yourself, and not passing it the $this or $_ variables as arguments.

    There are a few different ways you can write this, but I prefer saving my ScriptBlocks to a variable, rather than giving them actual function names.  It's nice and easy to read, without any confusion over variable scope.

    $clickOff = {
        $this | Get-Member
        $_
    }
    
    $btn_VMOff.add_Click($clickOff)

    Edit:

    Another option, if you prefer to keep your 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})


    Tuesday, August 20, 2013 1:14 PM
  • David is right. Here is a little sample that shows all the options:

    $null=[Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
    $form = New-Object Windows.Forms.Form
    $btn1 = New-Object Windows.Forms.Button
    $btn1.Text = "Button1"
    $btn2 = New-Object Windows.Forms.Button
    $btn2.Text = "Button2"
    $btn2.Left=100
    $btn3 = New-Object Windows.Forms.Button
    $btn3.Text = "Button3"
    $btn3.Top=100
    $btn4 = New-Object Windows.Forms.Button
    $btn4.Text = "Button4"
    $btn4.Top=100
    $btn4.Left=100
    $form.Controls.Add($btn1) 
    $form.Controls.Add($btn2)
    $form.Controls.Add($btn3)
    $form.Controls.Add($btn4)
    
    function OnClick($Sender, $EventArgs){ 
    	write-host $Sender,$EventArgs 
    }
    
    function OnClick2{ 
    	Write-Host $this,$_ 
    }
    
    $sbOnClick3={
    	Write-Host $this,$_ 
    }
    
    $sbOnClick4={ param($Sender,$EventArgs)
    	Write-Host $Sender,$EventArgs
    }
    
    $btn1.add_Click({OnClick $this $_})
    $btn2.add_Click({OnClick2})
    $btn3.add_Click($sbOnClick3)
    $btn4.add_Click($sbOnClick4)
    $form.ShowDialog()

    Tuesday, August 20, 2013 2:07 PM
  • Hi David,

    Thank you for you explainations.

    I actually had to find a work around since I couldn't find a way to access the objects created dynamically within my functions.

    $This was OK for the actual control (button) but I needed to change a few properties on the other TabPage elements and I couldn't find a way to do it.

    So I am storing them into hash tables and the Index is in the .tag property.

    Well educated powershell developpers are probably going to find it uggly, but for the time being, it works and this is eventually what matters :-)

    Thank you very much to Dirk and yourself for your help.

    Tuesday, August 20, 2013 2:17 PM