During the course of your programming, if you have used classes at all you will probably have also used the keyword WithEvents. WithEvents is used when declaring an object variable, to indicate that you also wish to receive events raised by that object. It is nice because you can then use the object variable just like a control on a form, except that it can be used in a class.
Private WithEvents myObject As SomeClass
You have probably also used arrays of object variables too. This comes in handy when you want to manage multiple objects cleanly, as you can loop through them and carry out the same actions to each in turn (See this thread for an explanation of arrays).
Private myObjects() As SomeClass
Now the trouble comes when you, as happens eventually, want to combine the two. You need to handle events from your objects, but you also need to have an array of them, and handle events from all of the object instances. Since programmers are inherently logical, you'd try this:
Private WithEvents myObjects() As SomeClass
... and VB will give you a nice, helpful, error message that tells you exactly why it isn't allowed.Now really the reason why you cannot use WithEvents on an array of objects is quite simple - if you had a single event handler for each object, you would have no way of knowing which object fired the event. You will probably have noticed that event handlers for controls that are part of control arrays will have an extra Index parameter - this is because control arrays were invented before the days of COM and it required some fair hacking on Microsoft's part to add the Index property to control events under the COM system. This same hacking could not be done for arrays of objects, because the objects do not have Index properties - your array simply holds references to each object.
So do we give up? No... of course there is always a solution
In this case it's one that sounds complex, but in reality (and you'll see an example of it) it's not too hard. I am going to offer two solutions - one for classes that you create yourself, and one for those that you do not have the source to.
![Wink](http://www.vbforums.com/images/smilies/wink.gif)
If you have the source it's easy. First you need to get rid of all events - since we can't use them. You can replace the RaiseEvent calls with calls to named methods in your event-handling class or form, that will form the new event handlers. Now you run into the first potential problem which is that these methods may not exist. To ensure that they do, we will have to require that the event-handling class/form Implements an interface class which defines all of our events and their parameters. Implementing an interface means that the class/form will have to contain all the event handling methods, so we eliminate the possibility that they do not exist when we are trying to fire an event.
You define an "event" in the interface class like this:
Sub MyEvent(AnyParams)
End Sub
Note that it is not necessary for the function body to be empty, but whatever you put in there we are not going to use anyway.End Sub
Once you've defined all events you then need to know where to send them. For that purpose we can define a property in our custom class. Note that we type this property as the type of our event interface - that forces potential event handling classes to contain the proper event handlers and eliminates any possibility of errors on our side - it is up to "them" to ensure that they meet the requirements
![Smilie](http://www.vbforums.com/images/smilies/smile.gif)
Private mCallback As IMyEventInterface
Property Set Callback(newObj As IMyEventInterface)
Set mCallback = newObj
End Property
Property Get Callback() As IMyEventInterface
Set Callback = mCallback
End Property
Property Set Callback(newObj As IMyEventInterface)
Set mCallback = newObj
End Property
Property Get Callback() As IMyEventInterface
Set Callback = mCallback
End Property
Now to raise an event we can simply call the appropriate event handler in our "callback" class. Of course, we must first check to see if it exists - if no callback class has been set, we cannot call anything (obviously)
' raise an event
If (Not mCallback Is Nothing) Then _
mCallback.MyEvent anyParams
If (Not mCallback Is Nothing) Then _
mCallback.MyEvent anyParams
And finally we want to implement an array of the classes - and now you can see the final solution that does NOT use WithEvents
![Smilie](http://www.vbforums.com/images/smilies/smile.gif)
Implements IMyEventInterface
Private myObject() As SomeClass
Private Sub Form_Load()
ReDim myObject(5)
For i = 0 To 5
Set myObject(i) = New SomeClass
Set myObject(i).Callback = Me
Next i
End Sub
Private Sub IMyEventInterface_MyEvent(AnyParams)
' our event handler
End Sub
Private myObject() As SomeClass
Private Sub Form_Load()
ReDim myObject(5)
For i = 0 To 5
Set myObject(i) = New SomeClass
Set myObject(i).Callback = Me
Next i
End Sub
Private Sub IMyEventInterface_MyEvent(AnyParams)
' our event handler
End Sub
Now you might recall I mentioned a solution for classes for which you do not have the source code for, such as classes in DLLs. Well here you can apply a bit of creative adaptation of the first solution. My SomeClass class is now going to a "wrapper" class for the closed-source class - so called because it will contain a reference to an instance of this class, but we will be using the wrapper class to access it. The instance of the closed-source class will need to be declared using WithEvents, because you can't change the way it raises events. However you still have the same setup in place for your callback and interface (you will need to include in your interface all events of the closed-source class that you handle in your wrapper class) - so you can declare an array of wrapper classes and use them to handle the events from the "wrapped" classes.
You will need to also add a property so that you can access the wrapped class.
Private WithEvents mWrapped As WrappedClass
Private mCallback As IMyEventInterface
' Callback() property snipped...
Property Set Wrapped(newObj As WrappedClass)
Set mWrapped = newObj
End Property
Property Get Wrapped() As WrappedClass
Set Wrapped = mWrapped
End Property
' for each event handled, raise a new one by the callback method
Private Sub mWrapped_SomeEvent()
If (Not mCallback Is Nothing) Then _
mCallback.SomeEvent
End Sub
Now you use the wrapper class, something like this:
Private mCallback As IMyEventInterface
' Callback() property snipped...
Property Set Wrapped(newObj As WrappedClass)
Set mWrapped = newObj
End Property
Property Get Wrapped() As WrappedClass
Set Wrapped = mWrapped
End Property
' for each event handled, raise a new one by the callback method
Private Sub mWrapped_SomeEvent()
If (Not mCallback Is Nothing) Then _
mCallback.SomeEvent
End Sub
Implements IMyEventInterface
Private myObject() As SomeClass
Private Sub Form_Load()
ReDim myObject(5)
For i = 0 To 5
Set myObject(i) = New SomeClass
Set myObject(i).Callback = Me
Set myObject(i).Wrapped = New WrappedClass
Next i
End Sub
Private Sub IMyEventInterface_SomeEvent()
' event handler
End Sub
Private myObject() As SomeClass
Private Sub Form_Load()
ReDim myObject(5)
For i = 0 To 5
Set myObject(i) = New SomeClass
Set myObject(i).Callback = Me
Set myObject(i).Wrapped = New WrappedClass
Next i
End Sub
Private Sub IMyEventInterface_SomeEvent()
' event handler
End Sub
Hopefully that gives you the idea of how to handle events raised from arrays of objects, both for your own and for compiled classes. So that you can see I'm not spouting bilgewater I have also included a demonstration of the first technique. You will need an extraction program such as WinZip or 7-Zip to unzip the file, and then run the extracted project. I have commented the code so that you can (hopefully!) understand what's going on ![Smilie](http://www.vbforums.com/images/smilies/smile.gif)
![Smilie](http://www.vbforums.com/images/smilies/smile.gif)