How to create dynamically a click event

Ask questions about creating Graphical User Interfaces (GUI) in PowerShell and using WinForms controls.
Forum rules
Do not post any licensing information in this forum.

Any code longer than three lines should be added as code using the 'Select Code' dropdown menu or attached as a file.
Locked
User avatar
tab-svm
Posts: 9
Meble kuchenne na zamówienie - na wymiar - Wrocław
Joined: Wed Jun 03, 2015 4:37 am

How to create dynamically a click event

Post by tab-svm »

Hi,

Need some help with creating click events while loading the form. I tried multiple things but without good results

I created a Powershell Gui application which runs a function on the form_load.

The Function reads an xml file which includes VM name,ip,role,type,status etc..
For each VM in the XML, the function creates new variables for a panel with a picture and couple of lables inside and adds it in a flowlayoutpanel.

Code: Select all

function Get-DataXml
{
	$Path = "Z:\workingdata.xml"
	$dataXml = [xml](get-content $Path)
	$dataXml.Load($Path)
	
	$computers = $dataXml.ictos.computers.computer
	
	$flowlayoutpanel1.SuspendLayout()
	
	foreach ($vm in $computers)
	{
		
		New-Variable -Name ('vmbox' + $vm.id) -Force
		New-Variable -Name ('vmbox' + $vm.id + 'pic') -Force
		New-Variable -Name ('vmbox' + $vm.id + 'l1') -Force
		New-Variable -Name ('vmbox' + $vm.id + 'l2') -Force
		New-Variable -Name ('vmbox' + $vm.id + 'l3') -Force
		New-Variable -Name ('vmbox' + $vm.id + 'l4') -Force
		
		$paneel = Get-Variable ('vmbox' + $vm.id)
		$picture = Get-Variable -ValueOnly ('vmbox' + $vm.id + 'pic')
		$l1 = Get-Variable -ValueOnly ('vmbox' + $vm.id + 'l1')
		$l2 = Get-Variable -ValueOnly ('vmbox' + $vm.id + 'l2')
		$l3 = Get-Variable -ValueOnly ('vmbox' + $vm.id + 'l2')
		$l4 = Get-Variable -ValueOnly ('vmbox' + $vm.id + 'l2')
			
		$paneel = New-Object 'System.Windows.Forms.Panel'
		$picture = New-Object 'System.Windows.Forms.PictureBox'
		$l1 = New-Object 'System.Windows.Forms.Label'
		$l2 = New-Object 'System.Windows.Forms.Label'
		$l3 = New-Object 'System.Windows.Forms.Label'
		$l4 = New-Object 'System.Windows.Forms.Label'
		
		$paneel.SuspendLayout()
		$picture.SuspendLayout()
		
		$paneel.Controls.Add($picture)
		$paneel.Controls.Add($l1)
		$paneel.Controls.Add($l2)
		$paneel.Controls.Add($l3)
		$paneel.Controls.Add($l4)
		
		$paneel.BackColor = 'Silver'
		$paneel.BorderStyle = 'FixedSingle'
		$paneel.Cursor = 'Hand'
		$paneel.ImeMode = 'Off'
		$paneel.Location = '15, 15'
		$paneel.Margin = '15, 15, 15, 15'
		$paneel.Size = '200, 250'
		$paneel.Tag = $vm.role
		$paneel.Visible = $True
		
		$picture.BackColor = 'Transparent'
		$picture.Dock = 'Top'
		$picture.Image = [System.Drawing.Bitmap]::FromFile(".\Source\" + $vm.status + ".png")
		$picture.Location = '0, 0'
		$picture.Anchor = 'Top, Left, Right'
		$picture.Margin = '0, 0, 0, 0'
		$picture.Padding = '5, 5, 5, 0'
		$picture.Size = '190, 130'
		$picture.TabStop = $False
		
		$l1.Anchor = 'Top, Left'
		$l1.BackColor = 'Transparent'
		$l1.Cursor = 'Hand'
		$l1.Font = 'Impact, 19pt'
		$l1.Location = '0, 135'
		$l1.Margin = '0, 0, 0, 0'
		$l1.Size = '198, 36'
		if ($vm.role -eq "SKA"){ $l1.Text = $vm.type + " " + $vm.name[13] }
		else { $l1.Text = $vm.type }
		$l1.TextAlign = 'MiddleCenter'
		$l1.UseCompatibleTextRendering = $True
		
		$l2.AutoSize = $true
		$l2.Dock = 'Bottom'
		$l2.Text = $vm.name
		$l2.Visible = $false
		
		$l3.BackColor = 'Transparent'
		$l3.Font = 'Lucida Sans Unicode, 12pt'
		$l3.Location = '0, 171'
		$l3.Margin = '0, 0, 0, 0'
		$l3.Size = '198, 25'
		$l3.Text = $vm.user
		$l3.TextAlign = 'MiddleCenter'
		$l3.UseCompatibleTextRendering = $True

		$l4.AutoSize = $True
		$l4.Dock = 'Bottom'
		$l4.Text = $vm.ipadres
		$l4.visible = $false
				
		$flowlayoutpanel1.Controls.Add($paneel)
		
		$paneel.resumeLayout()
		$flowlayoutpanel1.ResumeLayout()
	}
}
When the form is loaded is this my result:
panel.JPG
panel.JPG (28.61 KiB) Viewed 277 times
There is a hidden label which has the ip address of the VM.

So my script for each vm in the xml ends up like:
$vmbox1 (panel)
$vmbox1pic (picture)
$vmbox1l1 (label1)
$vmbox1l2 (label2) etc..

So Far So Good!

In my previous version I had 12 static panels and just enabled or disabled them by the amount of vm's get from the xml. So the clickevent was also static.

Now I want to create also the click event dynamically. The clickevent looks like this:

Code: Select all

$Clickevent = {
			$connectIP = $l4.Text
			switch ($paneel.tag)
			{
				"pds" {
					if ($l1.Text -eq "PDS Beheer")
					{
						$msgBoxInput = [System.Windows.Forms.MessageBox]::Show('Systeem overnemen?', 'Bevestigen', 'YesNo', 'Question')
						if ($msgBoxInput -eq "Yes") { &"C:\Program Files\RealVNC\VNC Viewer\vncviewer.exe" "$connectIP"::5900 }
					}
					if ($l1.Text -eq "PDS Acquisitie")
					{
						$msgBoxInput = [System.Windows.Forms.MessageBox]::Show('Systeem overnemen?', 'Bevestigen', 'YesNo', 'Question')
						if ($msgBoxInput -eq "Yes") { &"C:\Program Files\RealVNC\VNC Viewer\vncviewer.exe" "$connectIP"::5901 }
					}
					if ($l1.Text -eq "PDS Presentatie")
					{
						$msgBoxInput = [System.Windows.Forms.MessageBox]::Show('Systeem overnemen?', 'Bevestigen', 'YesNo', 'Question')
						if ($msgBoxInput -eq "Yes") { &"C:\Program Files\RealVNC\VNC Viewer\vncviewer.exe" "$connectIP"::5902 }
					}
				}
				"mgd" {
					$msgBoxInput = [System.Windows.Forms.MessageBox]::Show('Systeem overnemen?', 'Bevestigen', 'YesNo', 'Question')
					if ($msgBoxInput -eq "Yes") { &"C:\Program Files\RealVNC\VNC Viewer\vncviewer.exe" "$connectIP"::5900 }
				}
				"rnavb" {
					$msgBoxInput = [System.Windows.Forms.MessageBox]::Show('Systeem overnemen?', 'Bevestigen', 'YesNo', 'Question')
					if ($msgBoxInput -eq "Yes")
					{
						$Server = "$connectIP"
						$User = *****
						$Password = *****
						&cmdkey /generic:TERMSRV/$Server /user:$User /pass:$Password
						&mstsc /v:$Server
					}
				}
				default
				{
					&mstsc /v:$connectIP
				}
			}
}
How do I create this while loading the form. The click event needs info from label1 label4 and panel.tag of each box. Because of GUI limitation of Powershell the clickevent for a vmbox needs to be added in the visible controls of that box (panel itself, picture and the 2 visible labels)

I'm almost there, please HELP :)

jvierra
Posts: 14444
Joined: Tue May 22, 2007 9:57 am
Contact:

Re: How to create dynamically a click event

Post by jvierra »

To add a click event just use the method of the control.

$control.add_Click($clickEvent)

User avatar
tab-svm
Posts: 9
Joined: Wed Jun 03, 2015 4:37 am

Re: How to create dynamically a click event

Post by tab-svm »

jvierra wrote:
Fri May 29, 2020 7:47 am
To add a click event just use the method of the control.

$control.add_Click($clickEvent)
You serious? I posted the clickevent. and offcourse used the add_Click method. How else?
But the thing is that the code in the clickevent, which is also dynamic doesnt do the thing.

jvierra
Posts: 14444
Joined: Tue May 22, 2007 9:57 am
Contact:

Re: How to create dynamically a click event

Post by jvierra »

What thing is it supposed to do?

Have you tried to run in the debugger and step through the code?

I am sorry but it is really hard to understand what you are asking.

K.Kalicharan
Posts: 1
Joined: Wed Dec 04, 2019 10:17 am

Re: How to create dynamically a click event

Post by K.Kalicharan »

Have you tried using GetNewClosure()

Meaning as you create dynamic objects you have to create the click event together with the object.. At which time you add GetNewClosure() at the end of the click event.

example:
$button1.add_Click(
{
your client event code ....
}.GetNewClosure()
)

jvierra
Posts: 14444
Joined: Tue May 22, 2007 9:57 am
Contact:

Re: How to create dynamically a click event

Post by jvierra »

After rereading the question a few times I realized that the issue is how to get the controls that are associated with each button.

I saw the GetNewClosure suggestion but that really has nothing to do with the actual issue. Yes this will bind variables to the "current" value of a variable but cannot bind the code to future values of a variable. We can write one piece of code that wil always work with any control event it is bound to. Just use "$this" to reference the current control sending the event.

The issue is that the controls will work best if each synthetic control (controls associated with each button) needs to be in a container. We can use a grou0pp box for this. A GroupBox has a "Controls" property.

To get the current button's labels we can just use "$this" to reference the current button and "$this.Controls['label1']" to get the associated label by name.
When building dynamic controls we do not need to create variables. Control names are easier to work with and, inside a container, a control name can be the same as a name in another container. This allows us to generate controls as collections and assign the same code to the events on all controls.

Locked