如何正确地将事件处理程序添加到控件的 EventHandlerList?
How to properly add a Event Handler to the EventHandlerList of a Control?
我正在尝试编写一个 class 其构造函数需要对控件/组件的引用,以及控件中事件的名称 class。目的是通过在 运行-time:
添加事件处理程序,从引用控件的实例动态订阅指定事件
Public NotInheritable Class ExampleType(Of T As Component)
Public ReadOnly Property Target As T
Public Sub New(target As T, eventName As String)
Me.Target = target
Dim eventsProperty As PropertyInfo =
GetType(Component).GetProperty("Events",
BindingFlags.DeclaredOnly Or
BindingFlags.ExactBinding Or
BindingFlags.Instance Or
BindingFlags.NonPublic,
Type.DefaultBinder,
GetType(EventHandlerList),
Type.EmptyTypes, Nothing)
Dim eventHandlerList As EventHandlerList =
DirectCast(eventsProperty.GetValue(target, BindingFlags.Default,
Type.DefaultBinder, Nothing,
CultureInfo.InvariantCulture),
EventHandlerList)
Dim eventHandler As New EventHandler(AddressOf Me.Target_Handler)
eventHandlerList.AddHandler(eventName, eventHandler)
End Sub
Private Sub Target_Handler(sender As Object, e As EventArgs)
Console.WriteLine("Test")
End Sub
End Class
用法示例:
Dim target As NumericUpDown = Me.NumericUpDown1
Dim eventName As String = NameOf(NumericUpDown.ValueChanged)
Dim example As New ExampleType(Of NumericUpDown)(target, eventName)
问题是,在上面的示例中,当在这种情况下引发 Me.NumericUpDown1.ValueChanged
事件时,永远不会到达 Target_Handler
方法,除非我从代码中调用事件处理程序方法(使用:eventHandlerList(eventName).DynamicInvoke(target, Nothing)
)
我做错了什么?如何解决我的问题?。提前致谢。
我认为这个方法可以简化为 EventInfo object from the Component instance Type, using the GetEvent() method, then adding a new Delegate, using the EventInfo.AddEventHandler() method, passing the delegate type returned by the the EventInfo
object itself, in the EventInfo.EventHandlerType 属性(定义了这个事件使用的事件处理程序类型)。
AddEventHandler()
方法需要添加新事件委托的类型实例和委托对象:可以使用 Delegate.CreateDelegate 方法创建此委托,该方法接受处理程序方法作为字符串.
Target 类型是定义 Delegate 的 class Instance,因此您的 ExampleType
class.
的当前 Instance
应该这样做:
这里不处理异常:至少验证是否.GetEvent(eventName)
returns null
Imports System.ComponentModel
Imports System.Reflection
Public NotInheritable Class ExampleType(Of T As Component)
Public ReadOnly Property Target As T
Public Sub New(target As T, eventName As String)
Me.Target = target
Dim eventNfo = target.GetType().GetEvent(eventName)
' Or
' Dim eventNfo = GetType(T).GetEvent(eventName)
eventNfo.AddEventHandler(target, [Delegate].CreateDelegate(
eventNfo.EventHandlerType, Me, NameOf(Me.Target_Handler))
' Or
' eventNfo.AddEventHandler(Target, New EventHandler(AddressOf Target_Handler))
End Sub
Private Sub Target_Handler(sender As Object, e As EventArgs)
Console.WriteLine("Test")
End Sub
End Class
处理本地(静态)EventHandlerList 的替代方法:
Public NotInheritable Class ExampleType(Of T As Component)
Implements IDisposable
Private Shared m_EventList As EventHandlerList = New EventHandlerList()
Private m_Delegate As [Delegate] = Nothing
Private m_Event As Object = Nothing
Public Sub New(target As T, eventName As String)
Me.Target = target
AddEventHandler(eventName)
End Sub
Public ReadOnly Property Target As T
Private Sub AddEventHandler(eventName As String)
m_Event = eventName
Dim eventNfo = Target.GetType().GetEvent(eventName)
m_Delegate = [Delegate].CreateDelegate(eventNfo.EventHandlerType, Me, NameOf(Me.Target_Handler))
m_EventList.AddHandler(m_Event, m_Delegate)
eventNfo.AddEventHandler(Target, m_EventList(m_Event))
End Sub
Private Sub RemoveEventHandler()
Dim eventNfo = Target.GetType().GetEvent(m_Event.ToString())
eventNfo?.RemoveEventHandler(Target, m_EventList(m_Event))
m_EventList.RemoveHandler(m_Event, m_Delegate)
m_Delegate = Nothing
End Sub
Private Sub Target_Handler(sender As Object, e As EventArgs)
Console.WriteLine("Test")
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
Public Sub Dispose(disposing As Boolean)
If disposing AndAlso m_Delegate IsNot Nothing Then
RemoveEventHandler()
End If
End Sub
End Class
你可以拥有:
Private example As ExampleType(Of Component)
Private example2 As ExampleType(Of Component)
' [...]
example = New ExampleType(Of Component)(Me.NumericUpDown1, NameOf(NumericUpDown.ValueChanged))
example2 = New ExampleType(Of Component)(Me.TextBox1, NameOf(TextBox.TextChanged))
然后对每个对象(或这些对象的列表)调用 Dispose()
以删除处理程序并清理本地 EventHandlerList。相对于每个组件的 EventHandlerList
可以通过反射访问,但是,如前所述,不会包含控件的所有事件委托的 ListEntry 对象。
example.Dispose()
example2.Dispose()
我只是想分享我能够编写的这些方法扩展,并根据@Jimi 回答中关于 EventInfo
用法的建议使其适用于 System.ComponentModel.Component
class,并且除了他的回答:
Component.GetEvent(字符串,布尔值)作为 EventInfo
Component.TryGetEvent(字符串,布尔值)作为 EventInfo
Component.GetEvents(布尔值)作为 IReadOnlyCollection(Of EventInfo)
Component.GetSubscribedEvents(布尔值)作为 IReadOnlyCollection(Of EventInfo)
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Gets all the events declared in the source <see cref="Component"/>.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <example> This is a code example.
''' <code language="VB.NET">
''' Dim ctrl As New Button()
''' Dim events As IReadOnlyCollection(Of EventInfo) = ctrl.GetEvents(declaredOnly:=True)
'''
''' For Each ev As EventInfo In events
''' Console.WriteLine($"Event Name: {ev.Name}")
''' Next
''' </code>
''' </example>
''' ----------------------------------------------------------------------------------------------------
''' <param name="component">
''' The source <see cref="Component"/>.
''' </param>
'''
''' <param name="declaredOnly">
''' If <see langword="True"/>, only events declared at the
''' level of the supplied type's hierarchy should be considered.
''' Inherited events are not considered.
''' </param>
''' ----------------------------------------------------------------------------------------------------
''' <returns>
''' All the events declared in the source <see cref="Component"/>
''' </returns>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
<Extension>
<EditorBrowsable(EditorBrowsableState.Always)>
Public Function GetEvents(component As Component, declaredOnly As Boolean) As IReadOnlyCollection(Of EventInfo)
If declaredOnly Then
Const flags As BindingFlags = BindingFlags.DeclaredOnly Or
BindingFlags.Instance Or
BindingFlags.Public Or
BindingFlags.NonPublic
Return (From ev As EventInfo In component.GetType().GetEvents(flags)
Order By ev.Name Ascending).ToList()
Else
Return (From ev As EventInfo In component.GetType().GetEvents()
Order By ev.Name Ascending).ToList()
End If
End Function
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Gets a list of events declared in the source <see cref="Component"/>
''' that are subscribed to a event-handler.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <example> This is a code example.
''' <code language="VB.NET">
''' Dim ctrl As New Button()
''' AddHandler ctrl.Click, Sub() Console.WriteLine("Test")
''' AddHandler ctrl.DoubleClick, Sub() Console.WriteLine("Test") ' declaredOnly:=True
'''
''' Dim subscribedEvents As IReadOnlyCollection(Of EventInfo) = ctrl.GetSubscribedEvents(declaredOnly:=True)
''' For Each ev As EventInfo In subscribedEvents
''' Console.WriteLine($"Event Name: {ev.Name}")
''' Next ev
''' </code>
''' </example>
''' ----------------------------------------------------------------------------------------------------
''' <param name="component">
''' The source <see cref="Component"/>.
''' </param>
'''
''' <param name="declaredOnly">
''' If <see langword="True"/>, only events declared at the
''' level of the supplied type's hierarchy should be considered.
''' Inherited events are not considered.
''' </param>
''' ----------------------------------------------------------------------------------------------------
''' <returns>
''' A list of events declared in the source <see cref="Component"/>
''' that are subscribed to a event-handler.
''' </returns>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
<Extension>
<EditorBrowsable(EditorBrowsableState.Always)>
Public Function GetSubscribedEvents(component As Component, declaredOnly As Boolean) As IReadOnlyCollection(Of EventInfo)
Dim events As IReadOnlyCollection(Of EventInfo) =
GetEvents(component, declaredOnly)
Dim subscribedEvents As New List(Of EventInfo)
For Each ev As EventInfo In events
If component.GetEventHandlers(ev.Name).Any() Then
subscribedEvents.Add(ev)
End If
Next ev
Return subscribedEvents
End Function
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Gets a <see cref="EventInfo"/> that match the specified event name
''' declared in the source <see cref="Component"/>.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <example> This is a code example.
''' <code language="VB.NET">
''' Dim ctrl As New Button()
'''
''' Dim ev As EventInfo
''' Try
''' ev = ctrl.GetEvent(NameOf(Button.MouseDoubleClick), declaredOnly:=True)
''' Console.WriteLine($"Event Name: {ev.Name}")
'''
''' Catch ex As ArgumentException When ex.ParamName = "eventName"
''' Console.WriteLine($"No event found matching the supplied name: {ev.Name}")
'''
''' End Try
''' </code>
''' </example>
''' ----------------------------------------------------------------------------------------------------
''' <param name="component">
''' The source <see cref="Component"/>.
''' </param>
'''
''' <param name="eventName">
''' The name of the event.
''' </param>
'''
''' <param name="declaredOnly">
''' If <see langword="True"/>, only events declared at the
''' level of the supplied type's hierarchy should be considered.
''' Inherited events are not considered.
''' </param>
''' ----------------------------------------------------------------------------------------------------
''' <exception cref="ArgumentException">
''' No event found matching the supplied name: {eventName},
''' </exception>
''' ----------------------------------------------------------------------------------------------------
''' <returns>
''' The resulting <see cref="EventInfo"/>
''' </returns>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
<Extension>
<EditorBrowsable(EditorBrowsableState.Always)>
Public Function GetEvent(component As Component, eventName As String, declaredOnly As Boolean) As EventInfo
Dim ev As EventInfo = TryGetEvent(component, eventName, declaredOnly)
If ev Is Nothing Then
Throw New ArgumentException($"No event found matching the supplied name: {eventName}", paramName:=NameOf(eventName))
End If
Return ev
End Function
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Tries to get a <see cref="EventInfo"/> that match the specified event name
''' declared in the source <see cref="Component"/>.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <example> This is a code example.
''' <code language="VB.NET">
''' Dim ctrl As New Button()
''' Dim ev As EventInfo = ctrl.TryGetEvent(NameOf(Button.MouseDoubleClick), declaredOnly:=True)
''' If ev IsNot Nothing Then
''' Console.WriteLine($"Event Name: {ev.Name}")
''' Else
''' Console.WriteLine($"No event found matching the supplied name: {ev.Name}")
''' End If
''' </code>
''' </example>
''' ----------------------------------------------------------------------------------------------------
''' <param name="component">
''' The source <see cref="Component"/>.
''' </param>
'''
''' <param name="eventName">
''' The name of the event.
''' </param>
'''
''' <param name="declaredOnly">
''' If <see langword="True"/>, only events declared at the
''' level of the supplied type's hierarchy should be considered.
''' Inherited events are not considered.
''' </param>
''' ----------------------------------------------------------------------------------------------------
''' <returns>
''' The resulting <see cref="EventInfo"/>,
''' or <see langword="Nothing"/> (null) if no event found matching the supplied name.
''' </returns>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
<Extension>
<EditorBrowsable(EditorBrowsableState.Always)>
Public Function TryGetEvent(component As Component, eventName As String, declaredOnly As Boolean) As EventInfo
Dim events As IReadOnlyCollection(Of EventInfo) =
GetEvents(component, declaredOnly)
Return (From ev As EventInfo In events
Where ev.Name.Equals(eventName, StringComparison.OrdinalIgnoreCase)
).SingleOrDefault()
End Function
此外,我编写了一个辅助函数来获取事件字段名称的可能变体列表(如果与此结合,这是必不可少的:)
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Gets a list of possible variants for an event field name.
''' <para></para>
''' Example event name: ValueChanged
''' <para></para>
''' Result field names: EventValueChanged, EventValue, EVENT_VALUECHANGED, EVENT_VALUE, ValueChangedEvent, ValueEvent
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <param name="eventName">
''' The name of the event.
''' <para></para>
''' Note: the name is case-insensitive.
''' </param>
'''
''' <returns>
''' A list of possible variants for an event field name.
''' </returns>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Private Function GetEventFieldNameVariants(eventName As String) As IReadOnlyCollection(Of String)
' Example input event name:
' ValueChanged
'
' Resulting field names:
' EventValueChanged, EventValue (Fields declared in 'System.Windows.Forms.Control' class.)
' EVENT_VALUECHANGED, EVENT_VALUE (Fields declared in 'System.Windows.Forms.Form' class.)
' ValueChangedEvent, ValueEvent (Fields (auto-generated) declared in other classes.)
Dim names As New List(Of String) From {
$"Event{eventName}", ' EventName
$"EVENT_{eventName.ToUpper()}", ' EVENT_NAME
$"{eventName}Event" ' NameEvent
}
If eventName.EndsWith("Changed", StringComparison.OrdinalIgnoreCase) Then
names.Add($"Event{eventName.RemoveEnd(0, 7)}") ' EventName
names.Add($"EVENT_{eventName.RemoveEnd(0, 7).ToUpper()}") ' EVENT_NAME
names.Add($"{eventName.RemoveEnd(0, 7)}Event") ' NameEvent
End If
Return names
End Function
Public Function RemoveEnd(input As String, startIndex As Integer, length As Integer) As String
Return source.Remove((input.Length - startIndex - length), length)
End Function
我正在尝试编写一个 class 其构造函数需要对控件/组件的引用,以及控件中事件的名称 class。目的是通过在 运行-time:
添加事件处理程序,从引用控件的实例动态订阅指定事件Public NotInheritable Class ExampleType(Of T As Component)
Public ReadOnly Property Target As T
Public Sub New(target As T, eventName As String)
Me.Target = target
Dim eventsProperty As PropertyInfo =
GetType(Component).GetProperty("Events",
BindingFlags.DeclaredOnly Or
BindingFlags.ExactBinding Or
BindingFlags.Instance Or
BindingFlags.NonPublic,
Type.DefaultBinder,
GetType(EventHandlerList),
Type.EmptyTypes, Nothing)
Dim eventHandlerList As EventHandlerList =
DirectCast(eventsProperty.GetValue(target, BindingFlags.Default,
Type.DefaultBinder, Nothing,
CultureInfo.InvariantCulture),
EventHandlerList)
Dim eventHandler As New EventHandler(AddressOf Me.Target_Handler)
eventHandlerList.AddHandler(eventName, eventHandler)
End Sub
Private Sub Target_Handler(sender As Object, e As EventArgs)
Console.WriteLine("Test")
End Sub
End Class
用法示例:
Dim target As NumericUpDown = Me.NumericUpDown1
Dim eventName As String = NameOf(NumericUpDown.ValueChanged)
Dim example As New ExampleType(Of NumericUpDown)(target, eventName)
问题是,在上面的示例中,当在这种情况下引发 Me.NumericUpDown1.ValueChanged
事件时,永远不会到达 Target_Handler
方法,除非我从代码中调用事件处理程序方法(使用:eventHandlerList(eventName).DynamicInvoke(target, Nothing)
)
我做错了什么?如何解决我的问题?。提前致谢。
我认为这个方法可以简化为 EventInfo object from the Component instance Type, using the GetEvent() method, then adding a new Delegate, using the EventInfo.AddEventHandler() method, passing the delegate type returned by the the EventInfo
object itself, in the EventInfo.EventHandlerType 属性(定义了这个事件使用的事件处理程序类型)。
AddEventHandler()
方法需要添加新事件委托的类型实例和委托对象:可以使用 Delegate.CreateDelegate 方法创建此委托,该方法接受处理程序方法作为字符串.
Target 类型是定义 Delegate 的 class Instance,因此您的 ExampleType
class.
应该这样做:
这里不处理异常:至少验证是否.GetEvent(eventName)
returns null
Imports System.ComponentModel
Imports System.Reflection
Public NotInheritable Class ExampleType(Of T As Component)
Public ReadOnly Property Target As T
Public Sub New(target As T, eventName As String)
Me.Target = target
Dim eventNfo = target.GetType().GetEvent(eventName)
' Or
' Dim eventNfo = GetType(T).GetEvent(eventName)
eventNfo.AddEventHandler(target, [Delegate].CreateDelegate(
eventNfo.EventHandlerType, Me, NameOf(Me.Target_Handler))
' Or
' eventNfo.AddEventHandler(Target, New EventHandler(AddressOf Target_Handler))
End Sub
Private Sub Target_Handler(sender As Object, e As EventArgs)
Console.WriteLine("Test")
End Sub
End Class
处理本地(静态)EventHandlerList 的替代方法:
Public NotInheritable Class ExampleType(Of T As Component)
Implements IDisposable
Private Shared m_EventList As EventHandlerList = New EventHandlerList()
Private m_Delegate As [Delegate] = Nothing
Private m_Event As Object = Nothing
Public Sub New(target As T, eventName As String)
Me.Target = target
AddEventHandler(eventName)
End Sub
Public ReadOnly Property Target As T
Private Sub AddEventHandler(eventName As String)
m_Event = eventName
Dim eventNfo = Target.GetType().GetEvent(eventName)
m_Delegate = [Delegate].CreateDelegate(eventNfo.EventHandlerType, Me, NameOf(Me.Target_Handler))
m_EventList.AddHandler(m_Event, m_Delegate)
eventNfo.AddEventHandler(Target, m_EventList(m_Event))
End Sub
Private Sub RemoveEventHandler()
Dim eventNfo = Target.GetType().GetEvent(m_Event.ToString())
eventNfo?.RemoveEventHandler(Target, m_EventList(m_Event))
m_EventList.RemoveHandler(m_Event, m_Delegate)
m_Delegate = Nothing
End Sub
Private Sub Target_Handler(sender As Object, e As EventArgs)
Console.WriteLine("Test")
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
Public Sub Dispose(disposing As Boolean)
If disposing AndAlso m_Delegate IsNot Nothing Then
RemoveEventHandler()
End If
End Sub
End Class
你可以拥有:
Private example As ExampleType(Of Component)
Private example2 As ExampleType(Of Component)
' [...]
example = New ExampleType(Of Component)(Me.NumericUpDown1, NameOf(NumericUpDown.ValueChanged))
example2 = New ExampleType(Of Component)(Me.TextBox1, NameOf(TextBox.TextChanged))
然后对每个对象(或这些对象的列表)调用 Dispose()
以删除处理程序并清理本地 EventHandlerList。相对于每个组件的 EventHandlerList
可以通过反射访问,但是,如前所述,不会包含控件的所有事件委托的 ListEntry 对象。
example.Dispose()
example2.Dispose()
我只是想分享我能够编写的这些方法扩展,并根据@Jimi 回答中关于 EventInfo
用法的建议使其适用于 System.ComponentModel.Component
class,并且除了他的回答:
Component.GetEvent(字符串,布尔值)作为 EventInfo
Component.TryGetEvent(字符串,布尔值)作为 EventInfo
Component.GetEvents(布尔值)作为 IReadOnlyCollection(Of EventInfo)
Component.GetSubscribedEvents(布尔值)作为 IReadOnlyCollection(Of EventInfo)
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Gets all the events declared in the source <see cref="Component"/>.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <example> This is a code example.
''' <code language="VB.NET">
''' Dim ctrl As New Button()
''' Dim events As IReadOnlyCollection(Of EventInfo) = ctrl.GetEvents(declaredOnly:=True)
'''
''' For Each ev As EventInfo In events
''' Console.WriteLine($"Event Name: {ev.Name}")
''' Next
''' </code>
''' </example>
''' ----------------------------------------------------------------------------------------------------
''' <param name="component">
''' The source <see cref="Component"/>.
''' </param>
'''
''' <param name="declaredOnly">
''' If <see langword="True"/>, only events declared at the
''' level of the supplied type's hierarchy should be considered.
''' Inherited events are not considered.
''' </param>
''' ----------------------------------------------------------------------------------------------------
''' <returns>
''' All the events declared in the source <see cref="Component"/>
''' </returns>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
<Extension>
<EditorBrowsable(EditorBrowsableState.Always)>
Public Function GetEvents(component As Component, declaredOnly As Boolean) As IReadOnlyCollection(Of EventInfo)
If declaredOnly Then
Const flags As BindingFlags = BindingFlags.DeclaredOnly Or
BindingFlags.Instance Or
BindingFlags.Public Or
BindingFlags.NonPublic
Return (From ev As EventInfo In component.GetType().GetEvents(flags)
Order By ev.Name Ascending).ToList()
Else
Return (From ev As EventInfo In component.GetType().GetEvents()
Order By ev.Name Ascending).ToList()
End If
End Function
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Gets a list of events declared in the source <see cref="Component"/>
''' that are subscribed to a event-handler.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <example> This is a code example.
''' <code language="VB.NET">
''' Dim ctrl As New Button()
''' AddHandler ctrl.Click, Sub() Console.WriteLine("Test")
''' AddHandler ctrl.DoubleClick, Sub() Console.WriteLine("Test") ' declaredOnly:=True
'''
''' Dim subscribedEvents As IReadOnlyCollection(Of EventInfo) = ctrl.GetSubscribedEvents(declaredOnly:=True)
''' For Each ev As EventInfo In subscribedEvents
''' Console.WriteLine($"Event Name: {ev.Name}")
''' Next ev
''' </code>
''' </example>
''' ----------------------------------------------------------------------------------------------------
''' <param name="component">
''' The source <see cref="Component"/>.
''' </param>
'''
''' <param name="declaredOnly">
''' If <see langword="True"/>, only events declared at the
''' level of the supplied type's hierarchy should be considered.
''' Inherited events are not considered.
''' </param>
''' ----------------------------------------------------------------------------------------------------
''' <returns>
''' A list of events declared in the source <see cref="Component"/>
''' that are subscribed to a event-handler.
''' </returns>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
<Extension>
<EditorBrowsable(EditorBrowsableState.Always)>
Public Function GetSubscribedEvents(component As Component, declaredOnly As Boolean) As IReadOnlyCollection(Of EventInfo)
Dim events As IReadOnlyCollection(Of EventInfo) =
GetEvents(component, declaredOnly)
Dim subscribedEvents As New List(Of EventInfo)
For Each ev As EventInfo In events
If component.GetEventHandlers(ev.Name).Any() Then
subscribedEvents.Add(ev)
End If
Next ev
Return subscribedEvents
End Function
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Gets a <see cref="EventInfo"/> that match the specified event name
''' declared in the source <see cref="Component"/>.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <example> This is a code example.
''' <code language="VB.NET">
''' Dim ctrl As New Button()
'''
''' Dim ev As EventInfo
''' Try
''' ev = ctrl.GetEvent(NameOf(Button.MouseDoubleClick), declaredOnly:=True)
''' Console.WriteLine($"Event Name: {ev.Name}")
'''
''' Catch ex As ArgumentException When ex.ParamName = "eventName"
''' Console.WriteLine($"No event found matching the supplied name: {ev.Name}")
'''
''' End Try
''' </code>
''' </example>
''' ----------------------------------------------------------------------------------------------------
''' <param name="component">
''' The source <see cref="Component"/>.
''' </param>
'''
''' <param name="eventName">
''' The name of the event.
''' </param>
'''
''' <param name="declaredOnly">
''' If <see langword="True"/>, only events declared at the
''' level of the supplied type's hierarchy should be considered.
''' Inherited events are not considered.
''' </param>
''' ----------------------------------------------------------------------------------------------------
''' <exception cref="ArgumentException">
''' No event found matching the supplied name: {eventName},
''' </exception>
''' ----------------------------------------------------------------------------------------------------
''' <returns>
''' The resulting <see cref="EventInfo"/>
''' </returns>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
<Extension>
<EditorBrowsable(EditorBrowsableState.Always)>
Public Function GetEvent(component As Component, eventName As String, declaredOnly As Boolean) As EventInfo
Dim ev As EventInfo = TryGetEvent(component, eventName, declaredOnly)
If ev Is Nothing Then
Throw New ArgumentException($"No event found matching the supplied name: {eventName}", paramName:=NameOf(eventName))
End If
Return ev
End Function
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Tries to get a <see cref="EventInfo"/> that match the specified event name
''' declared in the source <see cref="Component"/>.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <example> This is a code example.
''' <code language="VB.NET">
''' Dim ctrl As New Button()
''' Dim ev As EventInfo = ctrl.TryGetEvent(NameOf(Button.MouseDoubleClick), declaredOnly:=True)
''' If ev IsNot Nothing Then
''' Console.WriteLine($"Event Name: {ev.Name}")
''' Else
''' Console.WriteLine($"No event found matching the supplied name: {ev.Name}")
''' End If
''' </code>
''' </example>
''' ----------------------------------------------------------------------------------------------------
''' <param name="component">
''' The source <see cref="Component"/>.
''' </param>
'''
''' <param name="eventName">
''' The name of the event.
''' </param>
'''
''' <param name="declaredOnly">
''' If <see langword="True"/>, only events declared at the
''' level of the supplied type's hierarchy should be considered.
''' Inherited events are not considered.
''' </param>
''' ----------------------------------------------------------------------------------------------------
''' <returns>
''' The resulting <see cref="EventInfo"/>,
''' or <see langword="Nothing"/> (null) if no event found matching the supplied name.
''' </returns>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
<Extension>
<EditorBrowsable(EditorBrowsableState.Always)>
Public Function TryGetEvent(component As Component, eventName As String, declaredOnly As Boolean) As EventInfo
Dim events As IReadOnlyCollection(Of EventInfo) =
GetEvents(component, declaredOnly)
Return (From ev As EventInfo In events
Where ev.Name.Equals(eventName, StringComparison.OrdinalIgnoreCase)
).SingleOrDefault()
End Function
此外,我编写了一个辅助函数来获取事件字段名称的可能变体列表(如果与此结合,这是必不可少的:
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Gets a list of possible variants for an event field name.
''' <para></para>
''' Example event name: ValueChanged
''' <para></para>
''' Result field names: EventValueChanged, EventValue, EVENT_VALUECHANGED, EVENT_VALUE, ValueChangedEvent, ValueEvent
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <param name="eventName">
''' The name of the event.
''' <para></para>
''' Note: the name is case-insensitive.
''' </param>
'''
''' <returns>
''' A list of possible variants for an event field name.
''' </returns>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Private Function GetEventFieldNameVariants(eventName As String) As IReadOnlyCollection(Of String)
' Example input event name:
' ValueChanged
'
' Resulting field names:
' EventValueChanged, EventValue (Fields declared in 'System.Windows.Forms.Control' class.)
' EVENT_VALUECHANGED, EVENT_VALUE (Fields declared in 'System.Windows.Forms.Form' class.)
' ValueChangedEvent, ValueEvent (Fields (auto-generated) declared in other classes.)
Dim names As New List(Of String) From {
$"Event{eventName}", ' EventName
$"EVENT_{eventName.ToUpper()}", ' EVENT_NAME
$"{eventName}Event" ' NameEvent
}
If eventName.EndsWith("Changed", StringComparison.OrdinalIgnoreCase) Then
names.Add($"Event{eventName.RemoveEnd(0, 7)}") ' EventName
names.Add($"EVENT_{eventName.RemoveEnd(0, 7).ToUpper()}") ' EVENT_NAME
names.Add($"{eventName.RemoveEnd(0, 7)}Event") ' NameEvent
End If
Return names
End Function
Public Function RemoveEnd(input As String, startIndex As Integer, length As Integer) As String
Return source.Remove((input.Length - startIndex - length), length)
End Function