【问题标题】:PowerShell classes and .NET eventsPowerShell 类和 .NET 事件
【发布时间】:2021-11-22 17:14:15
【问题描述】:

我正在编写一个带有小型 WPF GUI 的 PowerShell 脚本。我的代码被组织在一个从中创建单例的类中。我已经读过事件处理程序脚本块中的 $this 指向事件发送者而不是我的包含类实例。如何从事件处理程序访问我的类实例?

例如

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()

【问题讨论】:

  • 您可以尝试在SetupEventHandlers 中创建一个实例,如$instance=$this 并在处理程序中使用$instanceadd_Click)吗?
  • $sender 是您在add_click 中的课程。 StartAction 不是事件处理程序,它只是您的类的一个方法。修改StartAction签名并通过强制转换$sender调用事件处理程序中的方法([MyClass]$sender).StartAction()

标签: powershell


【解决方案1】:
  • 确实,脚本块中的automatic $this variable作为.NET事件处理程序指的是事件发送者

  • 如果PowerShell custom class的方法内部设置事件处理程序脚本块,则$this事件发送者定义遮蔽类方法中的通常定义(指手头的类实例)。

有两种解决方法,都依赖于 PowerShell 的动态作用域,它允许后代作用域查看来自祖先作用域的变量。

  • 使用 Get-Variable -Scope 1引用 范围的 $this(事件处理程序脚本块在 调用者的子范围)。
    [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) 
        })

    }
  • 为了更直接地利用动态作用域,您可以按照Abdul Niyas P M 的建议进行,即在调用者的作用域中定义一个帮助变量,该变量引用自定义类实例使用不同的名称,您可以在使用相同方法设置的可能多个事件处理程序脚本块中引用它:
    [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) 
        })

    }

另请注意,从 PowerShell 7.2 开始,您不能直接使用自定义类方法作为事件处理程序 - this answer 显示了解决方法,这也需要解决 @ 987654334@阴影问题。

【讨论】:

    【解决方案2】:

    试试这个示例

    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()
    

    编辑 1: 您可以尝试像这样创建一个引用类的专用方法的委托吗?

    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()
    

    【讨论】:

    • 请注意,事件处理程序脚本块中的 $sender事件发送者,即 WPF 按钮控件,而不是手头的自定义类实例。不幸的是,$this _also 指的是事件发送者,正是这种$this 的影子使得引用手头的自定义类实例变得具有挑战性。
    • 我确认这对我不起作用。调试显示:$sender.Name 是 BtnStartAction,所以它是按钮而不是自定义类实例,尝试将其转换为 MyClass 失败。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-17
    • 1970-01-01
    相关资源
    最近更新 更多