对于自定义事件,我可以像这样检查处理程序:
If Object.EventNameEvent Is Nothing Then
MsgBox("Is not handling it.")
End If
...但是我怎么能这样做,例如,检查设计器中生成的按钮的“.click”事件?这不起作用:
If Button1.ClickEvent Is Nothing Then
MsgBox("Is not handling it.")
End If
更新
我的琐事示例:
MsgBox(HasAttachedHandler(MySub, Button1.Click)) ' Expected result: True
MsgBox(HasAttachedHandler(MyFunc, Button1.Click)) ' Expected result: False
Private Sub MySub() Handles Button1.Click, Button2.Click
' bla bla bla
End Sub
Private Function MyFunc() Handles Button2.Click
' bla bla bla
End Function
更新2:
我正在尝试使用@ varocarbas 解决方案,但并没有完全按照我的需要进行操作,因此我尝试进行必要的修改以使其正常工作。问题是事件“FontChaged”没有返回所需的结果,如下所示:
Public Class Form1
Private WithEvents Button1 As New Button
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
' This is working (Result True):
MsgBox(HasAttachedHandler(Button1, "Click", "Button1_Click")) ' Result: True
' This is not working (Result False):
MsgBox(HasAttachedHandler(Button1, "FontChanged", "Button1_Click")) ' Expected result: True
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles _
Button1.Click, _
Button1.MouseHover, _
Button1.GotFocus, _
Button1.Enter, _
Button1.FontChanged, _
Button1.AutoSizeChanged
End Sub
Private Function HasAttachedHandler(ByVal ctl As Control, ByVal eventname As String, ByVal targetMethod As String) As Boolean
For Each evnt In ctl.GetType().GetEvents()
' Get secret key for the current event:
Dim curEvent As Reflection.FieldInfo = GetType(Control).GetField("Event" & evnt.Name, Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Static)
If (curEvent IsNot Nothing) Then
Dim secret As Object = curEvent.GetValue(Nothing)
' Retrieve the current event:
Dim eventsProp As Reflection.PropertyInfo = GetType(System.ComponentModel.Component).GetProperty("Events", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)
Dim events As System.ComponentModel.EventHandlerList = DirectCast(eventsProp.GetValue(ctl, Nothing), System.ComponentModel.EventHandlerList)
If (Not IsNothing(events(secret))) AndAlso curEvent.Name.ToLower = "event" & eventname.ToLower Then
Dim handler As [Delegate] = events(secret)
Dim method As Reflection.MethodInfo = handler.Method
If (targetMethod = method.Name) Then Return True
End If
End If
Next
Return False
End Function
End Class
答案 0 :(得分:2)
这是我尝试转换和修改@HansPassant发布的代码:
Imports System.Reflection
Imports System.ComponentModel
Public Class Form1
Private WithEvents btnA As New Button
Private WithEvents btnB As New Button
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
btnA.AutoSize = True
btnA.Text = "Handler Attached"
btnA.Location = New Point(10, 10)
Me.Controls.Add(btnA)
btnB.AutoSize = True
btnB.Text = "No Handlers Attached"
Dim pt As Point = btnA.Location
pt.Offset(btnA.Width, 0)
btnB.Location = pt
Me.Controls.Add(btnB)
End Sub
Private Sub btnA_Click(sender As Object, e As System.EventArgs) Handles btnA.Click
Dim btnA_Handled As Boolean = HasAttachedHandler("Click", btnA)
Dim btnB_Handled As Boolean = HasAttachedHandler("Click", btnB)
Debug.Print("btnA_Handled = " & btnA_Handled)
Debug.Print("btnB_Handled = " & btnB_Handled)
End Sub
Private Function HasAttachedHandler(ByVal EventName As String, ByVal ctl As Control) As Boolean
' Get secret click event key
Dim eventClick As FieldInfo = GetType(Control).GetField("Event" & EventName, BindingFlags.NonPublic Or BindingFlags.Static)
Dim secret As Object = eventClick.GetValue(Nothing)
' Retrieve the click event
Dim eventsProp As PropertyInfo = GetType(Component).GetProperty("Events", BindingFlags.NonPublic Or BindingFlags.Instance)
Dim events As EventHandlerList = DirectCast(eventsProp.GetValue(ctl, Nothing), EventHandlerList)
Return Not IsNothing(events(secret))
End Function
End Class
答案 1 :(得分:1)
我将在.NET框架中介绍我处理这类事情的非正统方法。从你的问题......
...“点击”在设计师中生成的按钮事件......
所以我假设这只适用于System.Windows.Forms.Control
的子类。正如许多其他人所证明的那样,使用Control
EventHandlerList
管理其事件系统的方式略有不同。例如,对于KeyDown
事件,它实现如下...
Public Custom Event KeyDown As KeyEventHandler
AddHandler(ByVal value As KeyEventHandler)
MyBase.Events.AddHandler(Control.EventKeyDown, value)
End AddHandler
RemoveHandler(ByVal value As KeyEventHandler)
MyBase.Events.RemoveHandler(Control.EventKeyDown, value)
End RemoveHandler
End Event
其中EventKeyDown
被定义为稍后用于检索处理程序的“关键”对象
Private Shared ReadOnly EventKeyDown As Object
好的,到目前为止,太棒了! Idle_Mind和varocarbas都指出了解决方案,但是你提到FontChanged
事件似乎不适合所提出的方法。为什么?因为两种方法都假定会有一个EventFontChanged
字段对象,而实际上它被定义为......
Private Shared ReadOnly EventFont As Object
现在很明显假设......
GetField("Event" + eventName)
......根本不起作用。
由于所有事件都在Control
类中定义,我们可以直接从那里反映出来。为了找到对象键,我们需要从事件处理程序中找到“字段引用”(请参阅上面的KeyDown
示例实现)。我们得到CIL字节代码,循环遍历它,直到我们找到加载静态字段引用(ldsfld
)的操作码。 ldsfld
的操作数是一个32位整数,可以解析为一个类型。剩下的很简单,因为Idle_Mind和varocarbas都已经证明了。
Imports System.Reflection
Imports System.ComponentModel
Imports System.Reflection.Emit
Public Class Form1
Private Shared Sub OnFooEvent(sender As Object, e As EventArgs) Handles Foo.Click, _
Foo.MouseHover, _
Foo.GotFocus, _
Foo.Enter, _
Foo.FontChanged, _
Foo.AutoSizeChanged
End Sub
Private Sub OnWindowLoad(sender As Object, e As EventArgs) Handles MyBase.Load
MsgBox(HasHandlers(Foo, "AutoSizeChanged"))
End Sub
Private Function HasHandlers(ByVal instance As Object, ByVal arg As String) As Boolean
Dim baseType = GetType(Control)
Dim eventInfo = baseType.GetEvent(arg)
Dim method = eventInfo.AddMethod.GetMethodBody().GetILAsByteArray()
Dim id As Integer
For i As Integer = 0 To method.Length - 1
If method(i) <> OpCodes.Ldsfld.Value Then Continue For
i += 1
Dim operand(3) As Byte
Buffer.BlockCopy(method, i, operand, 0, 4)
id = BitConverter.ToInt32(operand, 0)
Exit For
Next
If id = 0 Then Return False
Dim key = baseType.Module.ResolveField(id).GetValue(instance)
Dim listInfo = GetType(Component).GetProperty("Events", BindingFlags.NonPublic Or BindingFlags.Instance)
Dim list = listInfo.GetValue(instance)
Return list(key) IsNot Nothing
End Function
End Class
这段代码不安全,因为我们还没有考虑每个字节码的操作数大小!因此,循环可能会意外地拾取非操作码字节并将其误认为是ldsfld
指令。
答案 2 :(得分:0)
首先,我想强调一下Hans Passant的代码非常出色,并且有一个很好的起点,大部分工作都已完成。我已经参与了Idle_Mind代码的一部分并做了一些研究,在这里你得到了你想要的东西:
Imports System.Reflection
Imports System.ComponentModel
Public Class Form1
Private WithEvents Button1 As New Button
Private Sub Button1_Click(sender As Object, e As System.EventArgs) Handles Button1.Click
End Sub
Private Function HasAttachedHandler2(ByVal ctl As Control, ByVal targetMethod As String) As Boolean
Dim allEvents0 = ctl.GetType().GetEvents()
For Each evnt In allEvents0
' Get secret key for the current event
Dim curEvent As FieldInfo = GetType(Control).GetField("Event" & evnt.Name, BindingFlags.NonPublic Or BindingFlags.Static)
If (curEvent IsNot Nothing) Then
Dim secret As Object = curEvent.GetValue(Nothing)
' Retrieve the current event
Dim eventsProp As PropertyInfo = GetType(Component).GetProperty("Events", BindingFlags.NonPublic Or BindingFlags.Instance)
Dim events As EventHandlerList = DirectCast(eventsProp.GetValue(ctl, Nothing), EventHandlerList)
If (Not IsNothing(events(secret))) Then
Dim handler As [Delegate] = events(secret)
Dim method As MethodInfo = handler.Method
If (targetMethod = method.Name) Then
Return True
End If
End If
End If
Next
End Function
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Dim isTheMethodUsed As Boolean = HasAttachedHandler2(Button1, "Button1_Click")
End Sub
End Class
答案 3 :(得分:0)
在做了一些研究,测试并耗尽了我的知识和这方面的免费信息后,我不得不放弃所要求的修复。以下是我的结论:
FieldInfo
,假设每个事件都有关联的Field
,what does not seem to be the case。 EventHandlerList
似乎包括所有事件。不幸的是,它是一个特殊的字典,只能通过依赖定义给定Object
的{{1}}来检索值(在(转换的)Hans Passant代码中:Event
)。< / LI>
Dim secret As Object = curEvent.GetValue(Nothing)
,而是FieldInfo
(EventInfo
)。除了计算Dim curEvent As EventInfo = GetType(Control).GetEvent(evnt.Name)
变量以及secret
所需的索引之外,所有代码都可以正常使用此更改。令人惊讶的是,这个问题的解决方案并不容易:显然,只有events
可以提供正确的索引(?!)。secret
中提取信息而不使用EventHandlerList
(code on this line);或将secret object
/ secret object
替换为其他变量,例如:EventHandlerList
,PropertyDescriptorCollection
或EventDescriptorCollection
。如果有人可以纠正/扩展此帖子以帮助(我或其他人)清除此问题,那将是非常好的。