.netpowershellwinformsscope

Dynamically create .NET buttons, with button pressed return its name, or other unique identifier


I am trying to dynamically add buttons to a list, and have $Button (or another variable) be set to whatever is clicked (No OK/Cancel button needed).

The below code creates the buttons, and resizes the window fine, but I cannot for the life of me figure out how to return the button name, or anything else unique to it. I have tried too many things to remember, but throughout all those tries, write-host $Button.name (when added, not below) works fine upon clicking, but with similar to below either $Button is empty, or contains "cancelled" (which I guess is because of the form.close()), or only the first assigned button .name. What am I missing?

#Add assemblies
add-type -assemblyname System.Windows.Forms
add-type -assemblyname System.Drawing

#Define the form
$Form = new-object System.Windows.Forms.Form
$Form.text = 'Which ticket to work on?'

$Tickets = "A","2","i"

#Define each button
for($i = 0; $i -lt $Tickets.count; $i++)
{
  write-host "`$i = $i"
  $Button = new-object System.Windows.Forms.Button
  $Button.name = $i
  $Position = 25 + (55 * $i)
  $Button.location = new-object System.Drawing.Point(25,$Position)
  $Button.size = new-object System.Drawing.Size(525,50)
  $Button.text = $Tickets[$i]

  $Button.add_click({
    $Button = $this.text
    $form.close()
  }.GetNewClosure())

  $Form.Controls.Add($Button)
}

#Set form size, for contents
$Height = 125 + (50 * ($Tickets.count))
$Form.Size = New-Object System.Drawing.Size(600,$Height)
$Form.StartPosition = 'CenterScreen'
$Form.Topmost = $true

#display the form
$Form.ShowDialog()

#display the result
"Button $Button was pressed"

Solution

  • You’re running into two issues:

    Use the event sender to know which button was clicked, store the selected value somewhere accessible (e.g., the form’s Tag), set DialogResult to OK, then close the form.

    # Add assemblies
    Add-Type -AssemblyName System.Windows.Forms
    Add-Type -AssemblyName System.Drawing
    
    # Define the form
    $Form = New-Object System.Windows.Forms.Form
    $Form.Text = 'Which ticket to work on?'
    
    $Tickets = 'A','2','i'
    
    # Create buttons dynamically
    for ($i = 0; $i -lt $Tickets.Count; $i++) {
        $button = New-Object System.Windows.Forms.Button
        $button.Name = "btn_$i"                 # unique name if you want it
        $button.Text = $Tickets[$i]             # what the user sees
        $button.Tag  = $Tickets[$i]             # store the value you want to return
        $button.Size = New-Object System.Drawing.Size(525, 50)
        $button.Location = New-Object System.Drawing.Point(25, 25 + (55 * $i))
    
        # Use the sender to know which button was clicked
        $null = $button.Add_Click({
            param($sender, $eventArgs)
            $form.Tag = $sender.Tag                                # or $sender.Text / $sender.Name
            $form.DialogResult = [System.Windows.Forms.DialogResult]::OK
            $form.Close()
        }.GetNewClosure())
    
        $Form.Controls.Add($button)
    }
    
    # Size and show
    $Form.Size = New-Object System.Drawing.Size(600, 125 + (55 * $Tickets.Count))
    $Form.StartPosition = 'CenterScreen'
    $Form.TopMost = $true
    
    $result = $Form.ShowDialog()
    
    # Read back the selection
    if ($result -eq [System.Windows.Forms.DialogResult]::OK -and $Form.Tag) {
        "Button $($Form.Tag) was pressed"
    } else {
        "No selection."
    }