Is it possible to dynamically specify the type of event when using AddHandler?

I am trying to write a clever little function here that will add a specified delegate as an event handler to all the controls in a collection for any dynamic event. What I'm trying to do is write this as a completely generic function so that I could possibly use it in various different projects (perhaps including it in some sort of tools library).

Basically I want to specify a group of controls, the delegate to handle the event, and the type of event to handle. The problem that I'm running up against is that I can't figure out how to dynamically specify the event at run time.

Here's my 'work-in-progress' sub:

Private Sub AddHandlerToControls(controlList As ControlCollection, eventToHandle As EventHandler, eventHandlerDelegate As Func(Of Object, EventArgs), Optional filterList As List(Of Type) = {})
    For Each controlInList As Control In controlList
        If controlInList.HasChildren Then
            AddHandlerToControls(controlInList.Controls, controlInList.MouseEnter, eventHandlerDelegate, filterList)
        End If
        If filterList.Count > 0 Then
            If filterList.Contains(controlInList.GetType) = False Then
                Continue For
            End If
        End If
        AddHandler controlInList.MouseEnter, eventHandlerDelegate
    Next
End Sub

Ideally I would like to use the eventToHandle parameter there at the end in the AddHandler statement instead of specifically using controlInList.MouseEnter. Like this:

AddHandler eventToHandle, eventHandlerDelegate

That way I could call this function dynamically in a form.load method, and call it sort of like how I did earlier in the sub where it's recursively calling itself for child controls. Somehow say "for this list of controls I would like to use this delegate as the 'MouseEnter' event handler". Like So:

AddHandlerToControls(Me.Controls, control.MouseEnter, MouseEnterHandlerDelegate, new List(Of Type) {TextBox, ComboBox})

This could just be wishfull thinking, I'm starting to think that this isn't quite possible at this level of 'genericness', but it's an interesting enough problem that I thought I should at least ask.

Edit for solution:

Jon Skeet's suggestion of using Reflection ended up working for me. Here's the final function:

Private Shared Sub AddHandlerToControls(controlList As Control.ControlCollection, eventToHandle As String, eventHandlerDelegate As MethodInfo, Optional filterList As List(Of Type) = Nothing)
    For Each controlInList As Control In controlList
        If controlInList.HasChildren Then
            AddHandlerToControls(controlInList.Controls, eventToHandle, eventHandlerDelegate, filterList)
        End If
        If Not filterList Is Nothing Then
            If filterList.Contains(controlInList.GetType) = False Then
                Continue For
            End If
        End If

        Dim dynamicEventInfo As EventInfo = controlInList.GetType.GetEvent(eventToHandle)
        Dim handlerType As Type = dynamicEventInfo.EventHandlerType
        Dim eventDelegate As [Delegate] = [Delegate].CreateDelegate(handlerType, eventHandlerDelegate)
        dynamicEventInfo.AddEventHandler(controlInList, eventDelegate)
    Next
End Sub

And how I call it and the delegate used:

AddHandlerToControls(Controls, "MouseClick", GetType(MainFrm).GetMethod("MouseClickEventDelegate"), New List(Of Type) From {GetType(TextBox), GetType(ComboBox)})

Shared Sub MouseClickEventDelegate(sender As Object, eventArgs As EventArgs)
    sender.SelectAll()
End Sub

This allowed me to set all text boxes and combo boxes on my form (there's quite a few) to select all text when clicked into, in about 20 lines of code. The best part is that if I add any in the future, I won't have to worry about going back to add this handler, it'll be taken care of at run time. It may not be the cleanest solution, but it ended up working pretty well for me.

Jon Skeet
people
quotationmark

Two options:

  • Specify a "subscription delegate" via a lambda expression. I wouldn't like to guess at what this would look like in VB, but in C# it would be something like:

    (control, handler) => control.MouseEnter += handler;
    

    Then you just need to pass each control to the delegate.

  • Specify the event name as a string, and use reflection to fetch the event and subscribe (Type.GetEvent then EventInfo.AddEventHandler).

people

See more on this question at Stackoverflow