How to use the WithEvents keyword with a global variable?

I am trying to declare a variable in a VB6 module as follows:

Public WithEvents MyObject As MyClass 

Help files say WithEvents can only be used in class modules. Why can't it be used in .bas modules?

The code I complete has an object declared globally in the module. I want to add WithEvents to this declaration, but I need the object to be global because there are many other forms, etc. Relate to the object. How can I achieve this with minimal code breaking?

+4
source share
4 answers

Write a class that takes your global object as a parameter and absorbs its events.

 ' Class MySink Private WithEvents m_oSink As MyClass Friend Sub frInit(oSink As MyClass) Set m_oSink = oSink End Sub Private Sub m_oSink_MyEvent() '--- implement event End Sub 

Create an instance of this class in your .bas module.

 Public g_oMyObject AS MyClass Private m_oMySink As MySink Sub Main() Set g_oMyObject = New MyClass Set m_oMySink = New MySink m_oMySink.frInit g_oMyObject End Sub 
+5
source

Events in VB are a wrapper around a rather complex COM mechanism. Essentially in this mechanism, everything involved should be a COM class that implements the IConnectionPoint or IConnectionPointContainer interfaces. The BAS module is part of the old BASIC and is not implemented as a COM class, unlike the VB class. I believe that they could reimplement the BAS file in COM as an object with a singleton type (for example, with the MultiUse global classes), but they did not. Thus, unfortunately, BAS files cannot use WithEvents.

As for the simplest solution to your problem, you need to take advantage of the fact that object variables are just references to the object, not the object itself. I assume that you want your event messages to fall on the highest level object that you need, so that you have the means to control the bit of the application that interests you. Identify this place. For example, it is possible that you have a basic form that you are sure will be the first object loaded and the last unloaded. As part of initializing this object, pass a reference to your global object and set this to the level variable of the WithEvents module. Now you are in control.

BUT!! And it is very important! You must make sure that you delete this link well in advance of closing the application, just in case you have circular links to your form (or something else). A Form_Unload event would be ideal in this case.

+2
source

I'm not sure how big your application is, but there are several ways to do this. Here I would do, although it may take about 15 minutes to reorganize your application to use it.

First, copy all public and global methods and members from your module into a class called (for example) clsApplication.

Secondly, in a now empty module (let's call it modGlobal) declare Public Property Get Application() As clsApplication . Go ahead and add the setter too.

Thirdly, in your Sub Main() that runs your program, add this as your first line Set modGlobal.Application = New clsApplication .

Now you are replacing your module with a global class that can listen for events occurring at the application level. In the rest of the application, you can get a global state like this Application.Config or Application.GetUser() , or whatever you still have globally.

Of course, you can only apply this to the variable in which you want to use WithEvents, but you really should look at getting the code from the modules anyway.


Just saw the comments on @Mark. The minus approach is what I insisted on. If the event class is MyEventSource, then create the MyEventSourceListener class with the Target property to which you are passing the object and declaring it privately WithEvents. Then MyEventSourceListener can receive events and redirect them back to your module.

I don't like this because it is hacked and returns the code back to the module, but this may be the most appropriate approach.

+1
source

Case for a simple approach

Sometimes it is very useful for a class or even UserControl (which is really just a special class) to have properties and methods for widespread use throughout the program.

If the main problem is the widespread use of “utility” methods, which are stateless (for example, concomitant web communications that have URL encoding methods, object encodings, etc.), this can be pretty cheap just to have an additional global instance declared "without" events that you are not raising to load a heavy internal state (arrays, collections, etc.). Since your program still needs code, and only one copy is in memory, the second instance can be quite cheap.

Another option is to separate these “general” operations and even property values ​​into a separate class or static (BAS) module.

But sometimes you have a rather complicated UserControl or class that you do not want to configure by refactoring into separate modules or otherwise changing. Perhaps you support common standard versions for use in different projects. Perhaps this is a third-party library in which you do not even have a source. Whatever.

To consider something

This leads us to another method that may work for you, although in the immortal words of Irving Mainway it is "not for blind children." If you are experienced enough to use this, then this should already have happened to you, but none of us has the perfect memory (if we really ever found the time to read this wonderful guide).

So maybe this will not work for you for some reason, and you have already considered and discarded this idea.

The object of your container (Form, UserControl, parent class, etc.) can carefully establish a global link to an instance of the object in question when it initializes and deletes the link when it is completed. This should be done with some caution in order to avoid circular or orphan links, which may interfere with the unloading of other objects or even your entire program.

This should not be probable or carelessly used by Johnny GoTo. But if you know what you are doing in VB, and you actually read and understood the parts of the manual that discuss this technique and its pitfalls, this can be useful.

Suppose you have Form1 as the object to run the program. In Form1, you have an instance of the UserControl "WebWiz" that you wrote, named WebWiz1. But in another place (three or twelve more forms?) You want to call the WebWiz method, which converts a set of standard values ​​of the MIME type to file extensions. Only Form1 needs to handle WebWiz1 events, and WebWiz is pretty heavy for internal state, so you do not want to create additional instances for this method only.

In a static module, you can simply declare:

Public gWebWiz As WebWiz

When you run Form1 Initialize or Load events (or for a class after creating an instance of WithEvents ), you can:

Set gWebWiz = WebWiz1

In the 1st form event handler:

Set gWebWiz = Nothing

Then your program has a global link that it can use for different purposes.

Looks easy?

Well, if your programs using this are written as the correct "tree" of code, starting with Sub Main or the main form, you usually do not need to be very careful. But if you are one of those people who wrote like “Pinball Master” and are wondering, “How do I find all my open forms and unload them?” then you:

  • You have more work to avoid programs that freeze and
  • This is probably actually not the case.
+1
source

All Articles