RaiseEvent

team lib

So we have now seen how objects such as controls on a form (or a form itself) can source events, and we have discovered how to sink those events inside our own classes using the WithEvents keywords. But how can we create our own events and why might we want to?

The RaiseEvent keyword is the answer to how. The why is just a matter of imagination . Let us give you an example of how we used RaiseEvent one time to get your imagination cranking.

We needed to interface a bar code reader to an Access database and we needed ironclad control over the process so we decided to use a serial port bar code reader.

The bar code reader we selected connected to a COM (serial) port and transmitted a string of characters with a Carriage Return terminator at the end of the bar code. In order to use this however we needed to somehow "talk to" the serial port. We discovered that Visual Basic (that is, Visual Basic such as Visual Basic 6, not VBA) comes with a ComCtl OCX, a control that can be dropped on a form, and which can monitor a single serial port. The ComCtl OCX raises events that basically tell us that a character or set number of characters have been received on the COM port.

In order to provide black box encapsulation and a simple interface to the database system, we created a class that could initialize the baud rate, start and stop bits, and other parameters of the ComCtl OCX, turn on and off receiving data on the COM port, as well as sink the events that the ComCtl OCX generated (using WithEvents of course!).

The problem though is that we wanted the ComCtl and its class to deal only with the data coming from (and going to) the serial port, not deal with the bar coding stuff itself. In other words, we wanted to be able to use that same class for any application that needed to talk to the COM port, not build a class that could only interface the COM port to a bar code reader. Done correctly we would have a ComCtl class that could be used for interfacing a bar code reader to one system, while the exact same class could interface a serial modem to another system, or a gauge multiplexer to a different system.

We could then build a second class that understood bar code stuff and could "listen" for messages from my ComCtl class saying that data had arrived. The ComCtl class handled the port, by setting it up, getting the data from the ComCtl OCX and broadcasting a message that data had arrived, while my bar code class listened for messages saying that data had arrived and processed the data however it wanted to.

In order to do that we needed our ComCtl class to be able to raise its own events when it was finished getting data from the ComCtl OCX. It turns out this is absolutely trivial by using the RaiseEvent keyword!

A Simple Example

In order to keep things simple we're not going to use the ComCtl class. What we will do instead is create a class that can broadcast messages for other classes. Once we have that working we will set up two forms that can talk to each other using this message class. So, when we type in a textbox on FormA, it will appear on FormB. Type in a textbox in FormB and it will be displayed on FormA. This will all be done by using RaiseEvent s in our Message class (the event source) and WithEvents in our forms (the event sinks). All this in two lines of code and it will roll into one everything we have learned in this chapter. By the way, we actually use this class in our own systems to solve some tricky inter-form communications problems.

Try It OutRaiseEvent example

In order to save space and time, we have created a database called WithEvents.mdb to demonstrate the concepts. Let's get started.

  1. Create a new class module and enter the following code, then save it, calling it clsMsg :

       Option Compare Database     Option Explicit     Public Event Message(varFrom As Variant, varTo As Variant, _     varSubj As Variant, varMsg As Variant)     Public Event MessageSimple(varMsg As Variant)     Function Send(varFrom As Variant, varTo As Variant, _     varSubj As Variant, varMsg As Variant)     RaiseEvent Message(varFrom, varTo, varSubj, varMsg)     End Function     Function SendSimple(varMsg As Variant)     RaiseEvent MessageSimple(varMsg)     End Function   
  2. Now insert another module and enter the following code:

       Option Compare Database     Option Explicit     Public gclsMsg As clsmsg     Function InitClsMsg()     Static blnInitialized As Boolean     If blnInitialized = False Then     Set gclsMsg = New clsmsg     blnInitialized = True     End If     End Function   

    Save the module as basInitMsg.

  3. Now create two forms frm1 and frm2 , each with just two textboxes in each form. Call one of the textboxes txtSend and the other txtReceive . Change the caption property of each box to Send Message and Receive Message respectively:

    click to expand
  4. Open the code view for frm1 and add the following code:

       Option Compare Database     Option Explicit     Private WithEvents fclsMsg As clsMsg     Private Sub Form_Close()     Set fclsMsg = Nothing     End Sub     Private Sub Form_Open(Cancel As Integer)     InitClsMsg     Set fclsMsg = gclsMsg     End Sub     Private Sub txtSend_AfterUpdate()     fclsMsg.Send "frm1", "frm2", "Just a test", txtSend.Value     End Sub     Private Sub fclsMsg_Message(varFrom As Variant, varTo As Variant, _     varSubj As Variant, varMsg As Variant)     If varTo = "frm1" Then     txtReceive = varMsg     End If     End Sub   
  5. The code for frm2 is almost identical with just the following lines altered :

     ... Private Sub txtSend_AfterUpdate()   fclsMsg.Send "frm2", "frm1", "Just a test", txtSend.Value   End Sub Private Sub fclsMsg_Message(varFrom As Variant, varTo As Variant, _                             varSubj As Variant, varMsg As Variant)   If varTo = "frm2" Then   txtReceive = varMsg   End If End Sub 

    Save both the forms and close them.

  6. Now, reopen frm1 and frm2 in Form view. In the Send Message box on frm2 type in anything you want. Notice that as soon as you press Enter the message is immediately displayed on frm1 . Likewise click into the Send Message textbox on frm1 and type in anything you want. Notice that it is immediately displayed on frm2 .

    click to expand

How It Works

 Public Event Message(varFrom As Variant, varTo As Variant, _                      varSubj As Variant, varMsg As Variant) Public Event MessageSimple(varMsg As Variant) 

We declare two different public events, Message and MessageSimple . The Message event has full to / from / subject / message parameters whereas the MessageSimple has only a single simple message parameter.

 Function Send(varFrom As Variant, varTo As Variant, _               varSubj As Variant, varMsg As Variant)   RaiseEvent Message(varFrom, varTo, varSubj, varMsg) End Function Function SendSimple(varMsg As Variant)   RaiseEvent MessageSimple(varMsg) End Function 

Then there are two methods of the class, the Send and the SendSimple . Notice that each method accepts the same parameters as the event declared in the header and just passes them on to the RaiseEvent inside of the method.

We need to have a global variable for the message class as well as an init function, all this is stored in the basInitMsg module. This will allow us to have a single class instance that gets and sends messages, and everybody that uses the class will just get a pointer to this global variable.

 Option Compare Database Option Explicit Public gclsMsg As clsmsg Function InitClsMsg()   Static blnInitialized As Boolean   If blnInitialized = False Then     Set gclsMsg = New clsmsg     blnInitialized = True   End If End Function 

Notice that InitClsMsg has a static boolean flag that stores whether or not we have already initialized the class so that we only do it once. This allows many different forms or classes to call the Init so that they can be set up in any order and only the first one that calls the function actually performs the initialization.

Finally the two demo forms, frm1 and frm2, both have pretty much identical code. Notice that we dimension a fclsMsg variable inside the form using the WithEvents keyword. This tells the form's class that the fclsMsg object will be sourcing events and we want to sink those events inside of this (the form's) class.

 Option Compare Database Option Explicit Dim WithEvents fclsMsg As clsmsg 

In the Open event we call the InitClsMsg function, which will initialize the message class if that has not been done yet, and then we get a pointer to that global message class.

 Private Sub Form_Open(Cancel As Integer)   InitClsMsg   Set fclsMsg = gclsMsg End Sub 

Close simply cleans up behind us.

 Private Sub Form_Close()   Set fclsMsg = Nothing End Sub 

The txtSend textbox is used to transmit messages so in the AfterUpdate we call fclsMsg 's Send method, passing a string saying who we are ( frm1 ), who we are sending the message to ( frm2 ), what the message is about ( Just a test ), and the message itself which is the value of the txtSend (whatever was typed in).

   Private Sub txtSend_AfterUpdate()     fclsMsg.Send "frm1", "frm2", "Just a test", txtSend.Value     End Sub   

And finally we sink the Message event that fclsMsg raises. Notice that every message sent on this message channel will cause this event to fire, even the messages that we send. Since that is the case, we need to only listen to or look for the messages sent to frm1 (if we are looking at the code in frm1 ). Obviously frm2 would only look for messages sent to frm2 . When a message comes in with our name in the varTo parameter we place it into the txtReceive and the message will be displayed.

   Private Sub fclsMsg_Message(varFrom As Variant, varTo As Variant, _     varSubj As Variant, varMsg As Variant)     If varTo = "frm1" Then     txtReceive = varMsg     End If     End Sub   

And that's it!

One other thing to think about is that the messages don't have to be text. Because we are passing into variant parameters, the message could be a number, string, even a pointer to a word document or a recordset. As long as the receiving class knows what is coming and how to use it, it can be passed over this message channel. And of course, your class can simply raise its own event directly, it doesn't need to use a message channel like this.

And finally, notice that using WithEvents we don't have the problem of having to have the recipient there to take the message. We could have done this inter-form communication simply by writing code that poked the values directly into the textbox on the other form. But what happens if the other form is closed? Well, we would get a run-time error. Using WithEvents / RaiseEvent, if the other form is closed nothing happens. Again, we can build a system that sends a message without even having the receiver loaded. Clean, encapsulated, and doesn't depend on the existence of the matching interface to work. "I've done my part, if anyone is interested here's the data."

You might be scratching your head asking when you would ever really use this message class maybe never. This was really just intended as a simple demo without any clutter to demonstrate how WithEvents and RaiseEvent s work together to form a complete system. Furthermore it demonstrates that more than one class can sink an event ! You could have one or a hundred forms watching the message channel and all of them will receive the message. Which one, if any, actually uses the message is up to you. In fact you could have a class that isn't even on a form watching the message channel and doing something when it gets a message. My bar code processor is a classic example of that.

On the other hand, imagine a form that is hidden when open. Its purpose is to watch the free disk space (using its timer event, once an hour ) and transmit a message to the database application when the space gets too low. The form's class simply sends a "Low Disk" message using the message class above. An error logger class that logs the disk usage in a table is sinking events from the message class and when it sees a message addressed to itself, logs the problem and generates an e-mail to the network administrator warning them that they need to free up some disk space before the database fails.

The point is that we are looking at classes as black box systems with complex internal workings but a simple interface. WithEvents and RaiseEvent allow us to communicate directly from one object to another, or from one object to many other objects if that is what we need. Each class (object) doesn't need to know about who will use the event, nor how the event will be used (or even if it will be used). The object simply does its job and raises an event saying "I have finished my task", possibly passing parameters as well. Whether anyone is listening at the moment or using the event to trigger other actions is entirely irrelevant to the object that raises the event. This is no different from a combo box that has a dozen or more events. The combo is capable of raising its events regardless of whether anyone uses them. If no one is sinking those events, no problem, but if someone needs them they are there. Thus the combo doesn't know, nor care, who may be listening to its events. Furthermore the combo doesn't know, nor care how any of its events will be used.

 
team lib


Beginning Access 2002 VBA
Beginning Access 2002 VBA (Programmer to Programmer)
ISBN: 0764544020
EAN: 2147483647
Year: 2003
Pages: 256

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net