Get parent name
You need to set the .Name
property for the button controls if you want to get their names inside the MouseEnter script:
Add-Type -AssemblyName System.Windows.Forms$A = @{ Main = [System.Windows.Forms.Form] @{ StartPosition = 'CenterParent' } Button01 = [System.Windows.Forms.Button] @{ Top = 0 ; Name = 'Button01'} Button02 = [System.Windows.Forms.Button] @{ Top = 30 ; Name = 'Button02'} Button03 = [System.Windows.Forms.Button] @{ Top = 60 ; Name = 'Button03'}}$Script = { Write-host $this.Name }1..3 | ForEach-Object { $A["Button0$_"].Add_MouseEnter($Script) $A.Main.Controls.Add($A["Button0$_"])}[void]$A.Main.ShowDialog()$A.Main.Dispose()
Edit
Creating and naming the buttons inside the ForEach-Object
loop could save you typing the name for each button:
Add-Type -AssemblyName System.Windows.Forms$A = @{ Main = [System.Windows.Forms.Form] @{ StartPosition = 'CenterParent' }}$Script = { Write-host $this.Name }1..3 | ForEach-Object { $A["Button0$_"] = [System.Windows.Forms.Button] @{ Top = ($_ -1) * 30 ; Name = "Button0$_"} $A["Button0$_"].Add_MouseEnter($Script) $A.Main.Controls.Add($A["Button0$_"])}[void]$A.Main.ShowDialog()$A.Main.Dispose()
As Theo points out in his answer, you need to name your button objects by assigning to their .Name
property.
To avoid naming your buttons twice - once as a property name in your hashtable, and again via the .Name
property - you can create the buttons as an array, which simplifies the solution overall:
$A = @{ Main = [System.Windows.Forms.Form] @{ StartPosition = 'CenterParent' } # Create an *array* of buttons Buttons = [System.Windows.Forms.Button] @{ Top = 0; Name = 'Button1' }, [System.Windows.Forms.Button] @{ Top = 30; Name = 'Button2' }, [System.Windows.Forms.Button] @{ Top = 60; Name = 'Button3' }}# Print the name of the button being moused over.$Script = { $this.Name | Out-Host }# Attach the event-handler script block to each button.$A.Buttons | % { $_.Add_MouseEnter($Script)}# Add the buttons to the form.$A.Main.Controls.AddRange($A.Buttons)$null = $A.Main.ShowDialog()
If you want to avoid assigning to the .Name
property, you can use the following approach:
Use PowerShell's ETS (Extended Type System) to add the key of the hashtable entry in which each button is stored as a custom property (a NoteProperty
instance member), using Add-Member
, which the event-handler script can query:
Add-Type -AssemblyName System.Windows.Forms$A = @{ Main = [System.Windows.Forms.Form] @{ StartPosition = 'CenterParent' } Button01 = [System.Windows.Forms.Button] @{ Top = 0 } Button02 = [System.Windows.Forms.Button] @{ Top = 30 } Button03 = [System.Windows.Forms.Button] @{ Top = 60 }}$Script = { # Access the custom .CustomParent property added to each button instance below. Write-Host $this.CustomParent}1..3 | % { $key = "Button0$_" $btn = $A[$key] # Add a .CustomParent NoteProperty member to the button object # storing the key of the hashtable entry in which that button is stored. $btn | Add-Member -NotePropertyMembers @{ CustomParent = $key } $btn.Add_MouseEnter($Script) $A.Main.Controls.Add($btn)}[void]$A.Main.ShowDialog()
As for why PowerShell doesn't - and shouldn't - provide an automatic (built in) variable such as $GetParentName
to support this scenario:
There are two unrelated worlds involved in this scenario:
PowerShell variables
The .NET types from the
System.Windows.Forms
namespace (WinForms)
WinForms is language-agnostic - any .NET language can use it; all it knows about is how instance of its types are nested at runtime and how to raise appropriate .NET events, typically in response to user events.
The source object of an event, as reported by WinForms, is surfaced as $this
in a PowerShell script block acting as a .NET event delegate invoked directly by WinForms.
WinForms (rightfully) has no knowledge of what data structure or variable on the PowerShell side the event-originating object is stored in.
Even on the PowerShell side this is completely at your discretion - you could have chosen individual variables, for instance, or an array, as shown above, so there is no general pattern here that an automatic variable could support.
In the case at hand, PowerShell itself has no way of knowing that your $Script
block happens to be indirectly associated with a button instance incidentally stored in a hashtable entry.