I am working on a PowerShell script with a small WPF GUI. My code is organzized in a class from which a singleton is created. I have read that $this inside an event handler script block points to the event sender and not to my containing class instance. How can I access my class instance from the event handler?
Ex.
class MyClass {
$form #Reference to the WPF form
[void] StartAction([object] $sender, [System.Windows.RoutedEventArgs] $e) {
...
}
[void] SetupEventHandlers() {
$this.form.FindName("BtnStartAction").add_Click({
param($sender, $e)
# !!!! Does not work, $this is not my class instance but the event sender !!!!
$this.StartAction($sender, $e)
})
}
[void] Run() {
$this.InitWpf() #Initializes the form from a XAML file.
$this.SetupEventHandlers()
...
}
}
$instance = [MyClass]::new()
$instance.Run()
CodePudding user response:
Indeed, the automatic
$this
variable in a script block acting as a .NET event handler refers to the event sender.If an event-handler script block is set up from inside a method of a PowerShell custom
class
, the event-sender definition of$this
shadows the usual definition in a class method (referring to the class instance at hand).
There are two workarounds, both relying on PowerShell's dynamic scoping, which allows descendant scopes to see variables from ancestral scopes.
- Use
Get-Variable
-Scope 1
to reference the parent scope's$this
value (event-handler script blocks run in a child scope of the caller).
[void] SetupEventHandlers() {
$this.form.FindName("BtnStartAction").add_Click({
param($sender, $e)
# Get the value of $this from the parent scope.
(Get-Variable -ValueOnly -Scope 1 this).StartAction($sender, $e)
})
}
- Taking more direct advantage of dynamic scoping, you can go with Abdul Niyas P M's suggestion, namely to define a helper variable in the caller's scope that references the custom-class instance under a different name, which you can reference in - potentially multiple - event-handler script blocks set up from the same method:
[void] SetupEventHandlers() {
# Set up a helper variable that points to $this
# under a different name.
$thisClassInstance = $this
$this.form.FindName("BtnStartAction").add_Click({
param($sender, $e)
# Reference the helper variable.
$thisClassInstance.StartAction($sender, $e)
})
}
Also note that, as of PowerShell 7.2, you cannot directly use custom-class methods as event handlers - this answer shows workarounds, which also require solving the $this
shadowing problem.
CodePudding user response:
Try this sample
class MyClass {
$form #Reference to the WPF form
[void] StartAction([System.Windows.RoutedEventArgs] $e) {
#$this ...
}
[void] SetupEventHandlers() {
$this.form.FindName("BtnStartAction").add_Click({
param($sender, $e)
([MyClass]$sender).StartAction($e)
})
}
[void] Run() {
$this.InitWpf()
$this.SetupEventHandlers()
}
}
$instance = [MyClass]::new()
$instance.Run()
Edit 1: Could you try creating a delegate referring to a dedicated method of the class like this?
class MyClass {
$form #Reference to the WPF form
[void] StartAction() {
#$this ...
}
[void] SetupEventHandlers() {
$handler = [System.EventHandler]::CreateDelegate([System.EventHandler], $this, "Handler")
$this.form.FindName("BtnStartAction").add_Click($handler)
}
[void] Handler ([System.Object]$sender, [System.EventArgs]$e) {
$this.StartAction()
}
[void] Run() {
$this.InitWpf()
$this.SetupEventHandlers()
}
}
$instance = [MyClass]::new()
$instance.Run()