如何在 PowerShell 中添加事件操作处理程序

2023-11-27

终端.Gui (gui.cs)提供了一个Button类与Clicked事件定义为:

        public event Action Clicked;

我正在尝试在 PowerShell 中为 Terminal.Gui 编写一个示例应用程序,并且正在努力连接事件处理程序。

Add-Type -AssemblyName Terminal.Gui
[Terminal.Gui.Application]::Init() 
$win = New-Object Terminal.Gui.Window
$win.Title = "Hello World"
$btn = New-Object Terminal.Gui.Button
$btn.X = [Terminal.Gui.Pos]::Center()
$btn.Y = [Terminal.Gui.Pos]::Center()
$btn.Text= "Press me"

# Here lies dragons
[Action]$btn.Clicked = {
    [Terminal.Gui.Application]::RequestStop() 
}

$win.Add($btn)

[Terminal.Gui.Application]::Top.Add($win)
[Terminal.Gui.Application]::Run()  

The Clicked =上面示例中的赋值会返回错误:

InvalidOperation: The property 'Clicked' cannot be found on this object. Verify that the property exists and can be set.

但智能感知会自动完成Clicked对我来说......所以我猜这是一个类型问题?

我找不到任何 PowerShell 文档[Action]我发现的其他样本都没有给我带来任何快乐。

如何为一个事件定义一个事件处理程序ActionPowerShell 中基于 dotnet 事件?


史蒂夫·李的有用回答提供关键指针;让我补充一下背景信息:

PowerShell 提供两种基本的事件订阅机制:

  • (a) .NET 原生,如史蒂夫的回答所示,您在其中附加一个脚本块 ({ ... }) as a delegate到一个物体的<Name>事件通过.add_<Name>()实例方法(委托是一段用户提供的回调代码,在事件触发时调用) - 请参阅下一节。

  • (b) PowerShell介导的, 使用Register-ObjectEvent和相关的 cmdlet:

    • 类似于 (a) 的基于回调的方法可以通过将脚本块传递给-Action范围。
    • 或者,可以通过以下方式按需检索排队事件Get-Event cmdlet.

方法(b)的回调方法只能及时起作用,而电源外壳控制前台线程,即not这里的情况,因为[Terminal.Gui.Application]::Run() call blocks它。 因此,必须使用方法(a)。


Re (a):

C# offers 句法糖以运算符的形式+= and -=用于附加和分离事件处理程序委托, which look like 作业,但实际上被翻译成add_<Event>() and remove_<Event>() 方法调用.

您可以使用以下命令查看这些方法名称:[powerShell]键入作为示例:

PS> [powershell].GetEvents() | select Name, *Method, EventHandlerType


Name             : InvocationStateChanged
AddMethod        : Void add_InvocationStateChanged(System.EventHandler`1[System.Management.Automation.PSInvocationStateChangedEventArgs])
RemoveMethod     : Void remove_InvocationStateChanged(System.EventHandler`1[System.Management.Automation.PSInvocationStateChangedEventArgs])
RaiseMethod      : 
EventHandlerType : System.EventHandler`1[System.Management.Automation.PSInvocationStateChangedEventArgs]

PowerShell 提供no这种用于附加/删除事件处理程序的语法糖,因此必须直接调用这些方法。

不幸的是,两者都没有Get-Member制表符补全都不知道这些方法,而相反,原始事件names令人困惑地do即使您无法直接对它们进行操作,也可以通过制表符完成。

Github 建议 #12926旨在解决这两个问题。

用于事件定义的约定:

The EventHandlerType上面的属性显示了事件处理程序委托的类型名称,在本例中正确地遵循习俗使用基于泛型类型的委托System.EventHandler<TEventArgs>,其签名为:

public delegate void EventHandler<TEventArgs>(object? sender, TEventArgs e);

TEventArgs表示包含的实例的类型特定事件的信息。 其他习俗是这样的事件参数类型派生自System.EventArgs类,手头的类型,PSInvocationStateChangedEventArgs, is.

提供的活动no特定事件的信息按照惯例使用非通用的System.EventHandler代表:

public delegate void EventHandler(object? sender, EventArgs e);

据推测,因为该代表历史上用于all代表们,甚至对于那些with事件参数 - 之前generics.NET 2 中出现了 -EventArgs参数仍然存在,约定是传递EventArgs.Empty而不是null表示没有参数。
同样,长期建立的框架类型定义了非通用的custom具有特定事件参数类型的代表,例如System.Windows.Forms.KeyPressEventHandler.

这些约定都不是enforced由CLR然而,正如所讨论的事件被定义为所证明的那样public event Action Clicked;,它使用无参数委托作为事件处理程序。

通常建议遵守约定以免违背用户的期望,尽管这样做有时不太方便。


PowerShell 在使用脚本块时非常灵活({ ... })作为代表,尤其是not强制执行特定的参数签名 via param(...):

无论脚本块是否声明任何、太多或太少的参数,脚本块都会被接受,尽管那些绑定到脚本块参数的事件发起对象实际传递的参数必须是类型兼容的(假设脚本块的参数是显式键入的)。

因此,史蒂夫的代码:

$btn.Add_Clicked({
    param($sender, $e)
    [Terminal.Gui.Application]::RequestStop()
})

尽管有无用的参数声明,但仍然有效,因为没有任何参数被传递到脚本块,因为System.Action委托类型是无参数.

以下内容就足够了:

$btn.Add_Clicked({
  [Terminal.Gui.Application]::RequestStop()
})

注意:即使没有声明参数,您也可以通过以下方式引用事件发送者(触发事件的对象):自动的$this多变的(在这种情况下,与$btn).


简化的示例代码:

  • 打电话很重要[Terminal.Gui.Application]::Shutdown()为了退出应用程序后将终端返回到可用状态

  • 至少其中之一Terminal.Gui类型不适合 PowerShell:

    • What are conceptually text properties aren't implemented as type [string], but as [NStack.ustring]; while you can use [string] instances transparently to assign to such properties, displaying them again performs enumeration and renders the code points of the underlying characters individually.
      • 解决方法:打电话.ToString(); e.g. $btn.Text.ToString()
  • 从 PowerShell 7.3.2 开始,没有与 NuGet 包直接集成,因此将已安装包的程序集加载到 PowerShell 会话中非常麻烦 - 请参阅这个答案, which 展示了如何使用.NET核心SDK下载包并使其依赖项可用.

    • In PowerShell(核心)7.2+,问题可以解决在这种情况下: The Microsoft.PowerShell.ConsoleGuiTools module 附带 Terminal.Gui.dll,这样您就可以安装该模块,并在那里引用 DLL。

      • 此解决方法由 Jonathan DeMarks 提供这个 GitHub 评论,并将其集成到下面的示例代码中。

      • In Windows PowerShell您必须按照以下步骤操作上述答案首先制作Terminal.Gui.dll以便示例代码运行。

    • 注意Add-Type -AssemblyName仅适用于位于current目录(而不是script's目录)或随 PowerShell 本身一起提供(PowerShell [Core] v6+)/位于 GAC (Windows PowerShell) 中。

    • 鉴于目前从 PowerShell 使用 NuGet 包是多么麻烦,GitHub 功能建议 #6724要求Add-Type进行增强以直接支持 NuGet 包。

using namespace Terminal.Gui

# Load the Terminal.Gui.dll assembly.
if ($PSVersionTable.PSVersion -ge '7.2') {
  # Load the Terminal.Gui assembly via the 'Microsoft.PowerShell.ConsoleGuiTools'
  # module, by installing that module on demand.
  if (-not (Get-Module -ListAvailable Microsoft.PowerShell.ConsoleGuiTools)) {
    Write-Verbose -Verbose "Installing module Microsoft.PowerShell.ConsoleGuiTools on demand, in the current user's scope."
    Install-Module -Scope CurrentUser -ErrorAction Stop Microsoft.PowerShell.ConsoleGuiTools
  }
  # Terminal.Gui.dll is inside the module's folder.
  try { Add-Type -LiteralPath (Join-Path (Get-Module -ListAvailable Microsoft.PowerShell.ConsoleGuiTools).ModuleBase Terminal.Gui.dll) } catch { throw }
}
else {
  # Windows PowerShell (or earlier PS Core versions)
  # Unfortunately, there's no easy way to gain access to Terminal.Gui.dll, and the
  # best option is to use an aux. NET SDK project as shown in https://stackoverflow.com/a/50004706/45375
  # The next command assumes that the steps there have been followed.
  try { Add-Type -Path C:\Users\jdoe\.nuget-pwsh\packages-winps\terminal.gui\*\Terminal.Gui.dll } catch { throw }
}

# Initialize the "GUI".
# Note: This must come before creating windows and controls.
[Application]::Init()

$win = [Window] @{
  Title = 'Hello World'
}

$btn = [Button] @{
  X    = [Pos]::Center()
  Y    = [Pos]::Center()
  Text = 'Quit'
}
$win.Add($btn)
[Application]::Top.Add($win)

# Attach an event handler to the button.
# Note: Register-ObjectEvent -Action is NOT an option, because
# the [Application]::Run() method that isused to display the window is blocking.
$btn.add_Clicked({
    # Close the modal window.
    # This call is also necessary to stop printing garbage in response to mouse
    # movements later.
    [Application]::RequestStop()
  })

# Show the window (takes over the whole screen). 
# Note: This is a blocking call.
[Application]::Run()

# Required to restore the previous terminal screen
# and for being able to rerun the application in the same session.
[Application]::Shutdown()
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 PowerShell 中添加事件操作处理程序 的相关文章

随机推荐

  • Qt 多媒体:找不到-lpulse

    我修改了 pro QT core gui multimedia multimediawidgets 我包括
  • 为什么在重新分区 Spark Dataframe 时会出现这么多空分区?

    我想将数据框 df1 分区为 3 列 该数据框恰好有这 3 列的 990 个独特组合 In 17 df1 createOrReplaceTempView df1 view In 18 spark sql select count from
  • 使用 AVFoundation 进行视频录制

    我正在尝试使用 AVFoundation 录制视频 当我仅向会话添加视频输入时 一切正常 但是当我向其中添加音频输入时 它会停止录制视频 录制开始后立即调用委托方法 这是我的代码 void recordVideo self session
  • 我的计算机中缺少 SDL.dll - VS 2010

    我正在尝试编译我编写的 SDL 程序 但是当我这样做时 会出现以下错误 该程序无法启动 因为您的计算机缺少 SDL dll 尝试重新安装程序来解决此问题 我不知道为什么 我有SDL dll 我已将其放入正确的文件夹中 C Windows S
  • 如何更新 bootstrap-typeahead.js 中的源选项

    我使用 bootstrap typeahead 来允许多重选择 这里是demo 原始代码已由 Sherbrow 更新Twitter bootstrap 预先输入多个值 我的问题与以下用例相关 插入后Alaska值 我想更新不再显示的源Ala
  • 什么时候可以使用前向声明?

    我正在寻找何时允许在另一个类的头文件中对一个类进行前向声明的定义 我是否可以对基类 作为成员持有的类 通过引用传递给成员函数的类等执行此操作 将自己置于编译器的位置 当你向前声明一个类型时 编译器只知道该类型存在 它对其规模 成员或方法一无
  • Android 相机预览回调在 4.1 中未调用

    我有一个应用程序可以获取带有表面的相机预览帧 它可以在 Android 4 0 4 上运行 但更新后在同一设备上无法与 Jelly Bean 4 1 2 一起运行 简单来说 回调永远不会被回调 这是代码 剪了一点 public class
  • 如何在 Postgres 中列出视图的列?

    对于物理表 我一直使用以下 SQL select column name data type character maximum length from INFORMATION SCHEMA COLUMNS where table name
  • java中增强for循环的最后一次迭代

    有没有办法确定循环是否是最后一次迭代 我的代码看起来像这样 int array 1 2 3 StringBuilder builder new StringBuilder for int i array builder append i i
  • iOS 版 Objective C 中的属性反射

    我希望能够在 Objective C 中的类上使用反射来确定运行时可用的属性 我现在使用类来做类似的事情 NSString str NSStringFromClass object class 我想做的是使用这个结果返回类并查看哪些属性可用
  • 如何在 Ubuntu 中安装 Visual Studio 2015

    我听说过 Visual Studio Net 2015 适用于 Ubuntu 我在网上还找不到任何有用的东西你能帮我在 ubuntu linux 上安装这个版本吗 谢谢 据我所知 Visual Studio 不能在 LINUX 或 UBUN
  • 将 UIImage 转换为 NSData 再转换回 UIImage

    我必须从数据库填充表格单元格 并且图像位于设备上 我必须将该图像存储在imageDataObject 如何转换它UIImage to NSData 请建议 我尝试了很多解决方案 但它没有转换回来NSData to UIImage cell
  • SSL证书问题:无法在git中获取本地颁发者证书[重复]

    这个问题在这里已经有答案了 我在推送 git 时遇到问题 显示此错误消息 SSL certificate problem unable to get local issuer certificate 之前有多种方法解决了此问题 A 确保根证
  • RESTful 比 SOAP 更快吗?何时使用其中之一?

    复制 这是 REST 服务的最佳用途是什么 以及许多其他内容 请关闭它 在网络开发中 我应该很好地学习 RESTful 服务并让我未来的所有项目都使用它吗 它比 SOAP 服务更快吗 什么时候用哪个 在某些情况下我应该选择其中一种吗 根据我
  • 为变量分配一个范围

    每当我尝试为变量分配一个范围时 如下所示 Var1 range 10 50 然后尝试打印变量 Var1 range 10 50 print Var1 它只是打印 range 10 50 而不是该范围内的所有数字 为什么是这样 那是因为ran
  • 智能指针可以与使用 new T[] 分配的数组一起使用吗?

    智能指针如何处理数组 例如 void function std unique ptr
  • 如何使用 Windows 批处理脚本将空 ASCII 字符 (nul) 写入文件?

    我尝试从 Windows 批处理脚本将 ASCII 空字符 nul 写入文件 但没有成功 我最初尝试使用echo像这样 echo
  • iOS 8 如何检测蓝牙耳机是否已插入?

    在我的项目中 我使用AVAudioSession检测任何耳机已插入或拔出 但在这种情况下 我无法检测到蓝牙设备何时插入 这是我的耳机状态代码 void audioRouteChangeListenerCallback NSNotificat
  • 更改 jenkins 管道以使用 github 而不是 gitlab

    我有一组在 jenkins 管道中运行的自动化测试 测试代码位于 gitlab 中 我从 gitlab 提取代码的部分如下所示 我使用那里已经存在的 gitlab 凭据 因为其他项目使用相同的 gitlab 凭据 我使用位于测试代码库中的一
  • 如何在 PowerShell 中添加事件操作处理程序

    终端 Gui gui cs 提供了一个Button类与Clicked事件定义为 public event Action Clicked 我正在尝试在 PowerShell 中为 Terminal Gui 编写一个示例应用程序 并且正在努力连