Bit of background info first - I've been struggling with a problem for a while now. How could you have one single event handler to handle every control (or all of a particular type of control) on a form? I've never been able to find a complete solution online either. The two 'easy' methods have glaring issues. For sake of example, let's say we want every text box to change its border color when it gains focus. The code for changing one text box border on focus is easy:
Private Sub TextBox0_GotFocus() TextBox0.BorderColor = vbBlue End Sub
But doing this for every single text box causes a problem. The brute-force option would be to manually add a handler to every text box, and have them all call a master handler (http://sharetext.org/SWek). That creates a lot of duplicate code, fast. And it's time-consuming, boring, easy to forget to do... and as a programmer I'm too lazy to do it like this.
The other option is to set the handler for every text box to a constant expression in the form designer (eg
=AnyTextBox_HandleGotFocus()), but doing so makes you lose which text box actually got the focus:
Private Sub AnyTextBox_HandleGotFocus() ' ??? Which text box just fired this? I have no way of knowing! End Sub
I'd need to loop through the form's entire control collection every single time this thing fires. I also lose the ability to grant specific behaviour to an individual control. Unless I whack a massive
Switch statement inside the loop or something. Shudder.
After a long time rolling it around in my head, I think I have a solution. It isn't perfect, but the caveats of this are much smaller than the caveats of the above.
First off, we want to build a collection of the controls we want to use. We can then loop through it and assign all of their event handlers to a single handler programatically. This is the only loop in this code.
Private iTextBoxes As Collection Private Sub Form_Load() BuildControlCollection Me, iTextBoxes, eTextBox Dim lTextBox As TextBox For Each lTextBox In iTextBoxes lTextBox.OnGotFocus = "=AnyTextBox_GotFocus(" & lTextBox.Name & ")" Next lTextBox End Sub
Not relevant to the question, but
BuildControlCollection here turns
iTextBox into a collection of all the text boxes on the form. Don't worry about the details of my Hungarian notation either.
Next, let's add the 'master' event handler:
Public Function AnyTextBox_GotFocus(ByRef mpTextBox As TextBox) mpTextBox.BorderColor = RGB(100, 150, 215) FireControlSingleEvent mpTextBox, "GotFocus" End Function
FireControlSingleEvent is the important part (the name is a bit awkward, suggestions welcome!) - this fires the control's 'unique' event handler, so I can add handling for a specific control.
Private Function FireControlSingleEvent(ByRef mpControl As Control, _ ByVal ipEventProcName As String) Try: On Error GoTo Catch Dim iProcName As String iProcName = Me.Controls(mpControl.Name).EventProcPrefix & "_" & ipEventProcName Select Case ipEventProcName Case "Click", "AfterUpdate", "Change", "GotFocus", "LostFocus", "Enter": CallByName Me, iProcName, VbMethod Case "KeyPress": CallByName Me, iProcName, VbMethod, mLastKeyPressedAscii Case Else: Debug.Print "Multi event handling cannot support the " & ipEventProcName & " event." End Select GoTo Finally Catch: If Err.Number = 2465 Then Debug.Print "Procedure " & Forms("Form1").Controls(mpControl.Name).EventProcPrefix & "_" & ipEventProcName & _ " does not exist or is private" Else Err.Raise Err.Number End If Finally: On Error GoTo 0 End Function
This attempts to call a method named with the standard convention, according to the control and event procedure name it's given. For example, if it's handed
GotFocus, it will attempt to call
TextBox0_GotFocus(). The error handling stops it crashing if
TextBox0_GotFocus() doesn't exist, removing the necessity for having empty handlers for every single control.
Select statement is there as I haven't worked out how to handle every single event yet - only events that don't need arguments work with this code, so things like mouse events are currently out.(
KeyPress is a special case - by turning
Key Preview on at the form level, I can capture the KeyAscii required into
mLastKeyPressedAscii in the form's
Form_KeyPress event, and pass that)
- As mentioned above, event handlers that take arguments - such as
MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)- aren't compatible without losing the data that would be passed to the parameters.
TextBox0_GotFocus()-type handlers must be altered to
Any suggestions or improvements on this code? There's a few bits I'm iffy about - always nervous about using errors as an intentional part of the code structure, for eg. Feedback welcome.