# Visual Basic > Visual Basic FAQs >  Classic VB - Why can't I use WithEvents on arrays of objects?

## penagate

_Note: To answer this question fully would require that you have an indepth knowledge of how events work in the COM world, and then you'd already know the answer. Regardless, I'm not going to require that of anyone_ 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  :Wink:  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.
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 SubNote 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.
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: 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

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

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: 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


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: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

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: 




 Attached Files



WithEvents for arrays, by Penagate.zip 
(3.8 KB, 5118 views)










					
						Last edited by penagate; Feb 17th, 2006 at 03:33 AM.
					
					
				










  Reply With Quote
















Jan 14th, 2008, 11:21 PM


#2






Merri



				View Profile
			



				View Forum Posts
			



				Visit Homepage
			







				VB6, XHTML & CSS hobbyist
			















Join Date Oct 2002
Location Finland
Posts 6,654








					Re: Classic VB - Why can't I use WithEvents on arrays of objects?
				



Another code that doesn't require Implements, just one Class and a Form. This alternative solution relies on CallByName.

By performance this is likely to be somewhat slower than Penagate's sample above, simply by judging by the use of CallByName and a Variant array.
						






					
						Last edited by Merri; Jan 15th, 2008 at 02:39 PM.
					
					
				










  Reply With Quote

















Jan 22nd, 2009, 01:18 PM


#3






Daniel Elkins



				View Profile
			



				View Forum Posts
			







				Lively Member
			





Join Date Jan 2009
Posts 90








					Re: Classic VB - Why can't I use WithEvents on arrays of objects?
				



							What about an Index parameter in the "events"?

Would be nice in the tutorial, pretty easy to get it working but might not be for newbies.
						






					
						Last edited by Daniel Elkins; Jan 22nd, 2009 at 05:40 PM.
					
					
				










  Reply With Quote

















Sep 27th, 2009, 01:18 AM


#4






DigiRev



				View Profile
			



				View Forum Posts
			







				"Digital Revolution"
			









Join Date Mar 2005
Posts 4,471








					Re: Classic VB - Why can't I use WithEvents on arrays of objects?
				



							Would also be a lot less-confusing if you at least put the name of the file that the code goes in...
						











  Reply With Quote

















Feb 18th, 2015, 12:09 PM


#5






Bonnie West



				View Profile
			



				View Forum Posts
			



				Visit Homepage
			







				Default Member
			


















Join Date Jun 2012
Location InIDE
Posts 4,060








					Re: Classic VB - Why can't I use WithEvents on arrays of objects?
				



							Here's another variation that combines the interface class and custom class into one:

Option Explicit   'In Class1

Private m_Callback As Class1
Private m_Index    As Long

Public Sub SomeEvent(ByVal Index As Long, ByVal Param As Long, ByRef RetVal As String)

End Sub

'The Friend scope modifier prevents the following properties and methods from
'being Implemented yet still allows them to be accessible within the project

Friend Property Get Callback(Optional ByVal Index As Long) As Class1
    Set Callback = m_Callback
End Property
Friend Property Set Callback(ByVal Index As Long, ByRef RHS As Class1)
    m_Index = Index
    Set m_Callback = RHS
End Property

Friend Sub TriggerAnEvent(ByVal Param As Long)
    Dim Frm As VB.Form, RetVal As String

    Debug.Assert Not m_Callback Is Nothing      'If code stops here, the Callback property must be set first!
    m_Callback.SomeEvent m_Index, Param, RetVal

    Set Frm = m_Callback
    Frm.Print RetVal
End Sub
Option Explicit   'In Form1

Implements Class1

Private m_Class1(1 To 3) As New Class1

Private Sub Class1_SomeEvent(ByVal Index As Long, ByVal Param As Long, RetVal As String)
    Print Index, Param, ;
    RetVal = Switch(Index = 1&, "vbLeftButton", _
                    Index = 2&, "vbMiddleButton", _
                    Index = 3&, "vbRightButton")
End Sub

Private Sub Form_Load()
    Dim i As Long

    For i = 1& To 3&
        Set m_Class1(i).Callback(i) = Me
    Next

    AutoRedraw = True
    Caption = "Simulating WithEvents for Arrays of Objects via Implements Demo"
    Print "Index", "Button", "Const"
End Sub

Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    m_Class1(Choose(Button, 1&, 3&, , 2&)).TriggerAnEvent Param:=Button
End Sub




 Attached Files



WithEvents via Implements.zip 
(2.3 KB, 773 views)










					
						Last edited by Bonnie West; Feb 18th, 2015 at 03:35 PM.
					
					
				

On Local Error Resume Next: If Not Empty Is Nothing Then Do While Null: ReDim i(True To False) As Currency: Loop: Else Debug.Assert CCur(CLng(CInt(CBool(False Imp True Xor False Eqv True)))): Stop: On Local Error GoTo 0
My CodeBank ContributionsDeclare Sub CrashVB Lib "msvbvm60" (Optional DontPassMe As Any)









  Reply With Quote





















Quick Navigation
Visual Basic FAQs
Top

Site Areas
Settings
Private Messages
Subscriptions
Who's Online
Search Forums
Forums Home
Forums
Visual Basic

Visual Basic .NET

VB.net CodeBank


Visual Basic 6 and Earlier

CodeBank - Visual Basic 6 and earlier


Universal Windows Platform and Modern Windows Experience
Xamarin
Mobile Development
ASP, VB Script
Office Development
Database Development
Reporting
API
Games and Graphics Programming

Game Demos


COM and ActiveX
Network Programming
Visual Basic FAQs
Slow Chat with the Microsoft Visual Basic team


.NET and More

ASP.NET And ASP.NET Core
Visual Basic .NET
MVC .Net
C#
Microsoft Azure and Cloud Dev
WPF, WCF, WF
.NET Architecture and Design
Silverlight


General

General Developer Forum
IoT, IoE, and Maker Forum
Testers and Testing
Application Testing
Application Deployment
Linux Development
General PC
VBForums Coding Contests

Contest Entries


Code It Better
Maths Forum


Other Languages

Other BASIC
C and C++
Java
PHP
XML, HTML, Javascript, Web and CSS
jQuery
Assembly
Other Programming Languages


VBForums CodeBank

CodeBank - Visual Basic .NET
CodeBank - Visual Basic 6 and earlier
CodeBank - ASP / ASP.NET / MVC / Web API
CodeBank - C#
CodeBank - C++
CodeBank - Java / J#
CodeBank - PHP
Codebank - Game Programming
Codebank - Mobile Development
CodeBank - JavaScript
Codebank - Cascading Style Sheets (CSS)
CodeBank - Other


VBForums UtilityBank

UtilityBank - Utilities
UtilityBank - IDE Add-Ins
UtilityBank - Components
UtilityBank - Tutorials
UtilityBank - Other


Projects

Project Requests
Project Communication Area


Jobs

Just VB Jobs
Open Positions (Jobs)
Looking for Work


Community

Forum Feedback
General Discussion / Chit Chat

World Events


Forum Test Area









«
Previous Thread
			|
			Next Thread
»





VBForums
Visual Basic
Visual Basic FAQs
 Classic VB - Why can't I use WithEvents on arrays of objects?




Tags for this Thread



implements, 
		
			withevents



View Tag Cloud







				Posting Permissions
			



You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
 



BB code is On
Smilies are On
[IMG] code is On
[VIDEO] code is On
HTML code is On


Forum Rules











Contact Us
VB Forums
Top

----------


## Merri

Another code that doesn't require Implements, just one Class and a Form. This alternative solution relies on CallByName.

By performance this is likely to be somewhat slower than Penagate's sample above, simply by judging by the use of CallByName and a Variant array.

----------


## Daniel Elkins

What about an Index parameter in the "events"?

Would be nice in the tutorial, pretty easy to get it working but might not be for newbies.

----------


## DigiRev

Would also be a lot less-confusing if you at least put the name of the file that the code goes in...

----------


## Bonnie West

Here's another variation that combines the interface class and custom class into one:

Option Explicit   'In Class1

Private m_Callback As Class1
Private m_Index    As Long

Public Sub SomeEvent(ByVal Index As Long, ByVal Param As Long, ByRef RetVal As String)

End Sub

'The Friend scope modifier prevents the following properties and methods from
'being Implemented yet still allows them to be accessible within the project

Friend Property Get Callback(Optional ByVal Index As Long) As Class1
    Set Callback = m_Callback
End Property
Friend Property Set Callback(ByVal Index As Long, ByRef RHS As Class1)
    m_Index = Index
    Set m_Callback = RHS
End Property

Friend Sub TriggerAnEvent(ByVal Param As Long)
    Dim Frm As VB.Form, RetVal As String

    Debug.Assert Not m_Callback Is Nothing      'If code stops here, the Callback property must be set first!
    m_Callback.SomeEvent m_Index, Param, RetVal

    Set Frm = m_Callback
    Frm.Print RetVal
End Sub
Option Explicit   'In Form1

Implements Class1

Private m_Class1(1 To 3) As New Class1

Private Sub Class1_SomeEvent(ByVal Index As Long, ByVal Param As Long, RetVal As String)
    Print Index, Param, ;
    RetVal = Switch(Index = 1&, "vbLeftButton", _
                    Index = 2&, "vbMiddleButton", _
                    Index = 3&, "vbRightButton")
End Sub

Private Sub Form_Load()
    Dim i As Long

    For i = 1& To 3&
        Set m_Class1(i).Callback(i) = Me
    Next

    AutoRedraw = True
    Caption = "Simulating WithEvents for Arrays of Objects via Implements Demo"
    Print "Index", "Button", "Const"
End Sub

Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
    m_Class1(Choose(Button, 1&, 3&, , 2&)).TriggerAnEvent Param:=Button
End Sub

----------

