【问题标题】:Remove Event by Name按名称删除事件
【发布时间】:2014-03-24 12:06:57
【问题描述】:

我想使用 VisualBasic .Net 从 WinForms 控件中删除特定的处理程序。

例如:

我有一个 Windows.Forms.ToolStripButton 并注册了一个 Click-Event 以保存数据。

现在我需要从按钮中删除此事件,但我无法访问已注册的事件方法!我只知道使用了哪个句柄。在我的情况下“点击”

为了更好地理解:我必须为 GridView 编写扩展。选择多条线时,我必须为所选行集成大规模编辑。为此,我需要为保存按钮注册一个新的单击事件并删除旧的。但我无法访问事件处理程序的地址。我只有注册事件的按钮

这是我迄今为止尝试过的:

Dim btnSaveEventInfo = btnSave.GetType.GetEvent("Click")
Dim method = Activator.CreateInstance(btnSaveEventInfo.GetType, True)

RemoveHandler btnSave.Click, method

这会引发 InvalidCastException

有人知道吗?

Gr33tz 江鱼

【问题讨论】:

标签: vb.net events handler removeall


【解决方案1】:

哇。这比我预期的要难得多。这样做是可能的,但我会警告你,这真是一团糟。最终,您需要做的是获取事件的Delegate 对象。一旦你有了它,删除事件处理程序就很容易了,因为你可以这样做:

Dim d As [Delegate] = ' ... (we'll figure this out later)
For Each handler As [Delegate] In d.GetInvocationList()
    RemoveHandler btnSave.Click, DirectCast(handler, EventHandler)
Next

但是,获得活动的代表特别困难。反射可以很容易地为任何所需的事件获取EventInfo 对象,但它没有提供一种简单的方法来获取该事件的委托。从技术上讲,当然,没有办法总是得到委托,因为事件私下封装了他们的委托,并且可以以任何自定义方式实现。但是,由于大多数事件都是以“正常”方式实现的,因此可以通过反射将私有委托隐藏在对象中。当然,如果微软决定改变它编译事件的“正常”方式,所有这一切都会中断,但既然这是你所拥有的最佳选择,你就会坚持下去。

更复杂的是,WinForm 控件的事件处理程序以自定义方式实现(尽管它们彼此一致),因此获取控件事件的委托更加复杂.在this page of Bob Powell's blog 上有一篇关于这一切的精彩文章。在这里重复所有信息对我来说太长了,但我已经用它来整理一个 VB.NET 示例,说明如何解决您的特定情况:

Dim currType As Type = btnSave.GetType()
Dim eventFieldInfo As FieldInfo = Nothing
Do
    eventFieldInfo = currType.GetField("EventClick", BindingFlags.Static Or BindingFlags.Instance Or BindingFlags.NonPublic)
    If eventFieldInfo Is Nothing Then
        currType = currType.BaseType
    End If
Loop While eventFieldInfo Is Nothing

Dim ehl As EventHandlerList = DirectCast(btnSave.GetType().GetProperty("Events", BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.FlattenHierarchy).GetValue(btnSave), EventHandlerList)
Dim d As [Delegate] = ehl(eventFieldInfo.GetValue(btnSave))

For Each handler As EventHandler In d.GetInvocationList()
    RemoveHandler btnSave.Click, handler
Next

请参阅上面的链接以了解其工作原理和方式。但是,正如我所说,这非常难看,并且可能会在未来的 .NET 版本中中断。因此,如果您有任何其他方法可以做到这一点,最好使用这些替代方法。例如,如果您可以控制添加事件处理程序的代码,则可以只保留对该委托的单独引用,以便稍后将其删除,例如:

Dim myClickHandler As EventHandler = AddressOf btnSave_Click
AddHandler btnSave.Click, myClickHandler

' ...

RemoveHandler btnSave.Click, myClickHandler

【讨论】:

  • 到了EventHandlerList,当我看到你已经制定了一个帖子:) +1
  • @MrPaulch 干得好!我很高兴知道我不是唯一一个被吸进这个兔子洞的人。
  • 在测试了上面的代码之后,它确实从代码中删除了所有点击处理程序,但只有在添加了处理程序时才会永久删除: AddHandler btnSaveClick, clickHandler 在我的情况下,处理程序被添加到方法中并且控制是WithEvents。为此,处理程序被永久添加,因此上面的代码将不起作用。另请参阅:stackoverflow.com/questions/2208775/…
  • @gangfish 嗯。是的,这是一个艰难的过程。我想不出任何方法,即使有反射,你可以删除 WithEvents 引用。在这种情况下,我真的会更加强调,需要重新考虑你的设计:(
  • @gangfish 或者,如果您无法更改处理事件的代码,也许您可​​以想出一些方法将按钮包装在另一个类中,或者使用派生按钮类来允许您首先阻止事件触发。
猜你喜欢
  • 2012-05-22
  • 2018-04-01
  • 2017-07-03
  • 1970-01-01
  • 2014-08-31
  • 1970-01-01
  • 2016-12-25
相关资源
最近更新 更多