1.5 Messages

Messages

Objects communicate with each other. They do so by sending messages. Objects are always responsible for their own actions. If one object (such as a user interface object) wants another object to do something, the interface object won't take any action other than telling the second object to execute some behavior. This is called sending messages. Basically every kind of method call is a message.

You've already seen a couple of typical messages:

Screen.ReserveSeats( 2 )

Screen.TurnOnLights()

Messages consist of three fundamental parts. The first part is the name of the message receiver. The second part is the message that's passed along. This actually is the name of the method that will be executed. The third part contains the parameters that are passed to the referenced method.

In addition to that, there is usually a response to a message. This is the return value that the called method sends back.

Events

Messages can originate from different sources. Objects may send messages internally. These messages are usually fired in the same sequence and at the same relative point in time. But another kind of message can be sent at any point in time, no matter in what state the system currently is. These messages, called events, are usually caused by user interaction or they come from the operating system.

Visual FoxPro relies heavily on events. A modern Visual FoxPro application usually creates an application object and then switches into a waiting mode (READ EVENTS). Everything that happens from this point on is originated by an event.

As already mentioned, events are usually created by some kind of user interaction with the computer, through the mouse or keyboard input. But a couple of events are originated by the operating system. These are usually unpleasant events such as errors. And of course, some events are originated by the way the program flows. For example, there are events that fire whenever an object is destroyed or created, or whenever an object changes size or position.

Events vs. event methods

When an event occurs, Visual FoxPro automatically fires a method that's associated with that event. For example, when the user clicks an object, the Click event happens and the object's Click method fires.

As mentioned above, Visual FoxPro provides a set of classes you can subclass. I'll discuss base classes in more detail later on. All you need to know about them now is that they all come with a set of predefined event methods. You can subclass these classes and add your behavior to them.

Don't confuse these methods with events. An event method is just a method that happens to have the same name as the event, and it gets fired automatically when an event happens. You can also fire those methods yourself, just like any other method. However, this does not mean that the actual event fires, which might be a significant difference. As an example, MouseDown events often have assigned methods that require the mouse position to be on top of the object to which the method belongs. If you simply fire this method programmatically, this may or may not be the case.

While this is a logical problem that can be resolved by smart and preventive programming techniques, there are some great differences in internal behavior between handling a real event and firing an event method manually. An example that makes this obvious is the KeyPressEvent. This event always fires when the user presses a key while the focus is in a data entry field. However, if you fire this method programmatically, you won't be able to simulate a keystroke because the operating system doesn't know that the method associated with that event exists.

Access and assign methods

Access and assign methods are new features in Visual FoxPro 6.0, and they are also two of my personal favorites. Basically, they are events that fire when somebody tries to change or access a property. You can trap these events and actually manipulate return and assigned values. Other than that, access and assign methods are regular methods that allow you to do everything you can do with other methods.

Let's say our Screen class has a property called DoorLocked. This property specifies whether the entrance door is locked. By simply changing the value of this property, we lock and unlock the door.

Of course, not everyone should be able to change this property. Also, not everyone should be able to ask for the status of this property. So let's see how we can accomplish that using access and assign methods:

DEFINE CLASS SecureScreen AS Screen
DoorLocked = .F.


FUNCTION DoorLocked_Assign( Locked )
IF oUser.Administrator
THIS.DoorLocked = Locked
ELSE
MessageBox( "Only administrators can lock and unlock doors!" )

ENDIF
RETURN
ENDFUNC

FUNCTION DoorLocked_Access
IF oUser.Administrator
RETURN THIS.DoorLocked
ELSE

MessageBox( "Only administrators can check the door status!" )

RETURN .NULL.

ENDIF
ENDFUNC
ENDDEFINE

As you can see, access and assign methods are regular methods that have the same name as the property, in addition to the phrase _Access or _Assign. In assign methods, the new value is passed as a parameter. Access methods return the value the user tries to access.

In the example above, I created a subclass of the Screen class to keep the sample simple. Of course, I could have added these methods in the original class. Most likely I would do it this way in a real-life scenario. In line 2 of each method, I check for a property called Administrator in an oUser object. You might wonder where this object comes from. Well, I just assume it exists somewhere in memory. Let's just leave it at that in favor of the simplicity of the example.

Once the program focus is in an access or assign method, the property can be accessed directly. We could now use our class as we would have before. In our example, we could access the DoorLocked property in the following manner:

Screen1.DoorLocked = .F. && We open the door

Whenever we execute this line, the assign method fires in order to check if we are an administrator, and it may or may not allow us to assign that value.

The same applies for property access. We could ask for a value like this:

? Screen1.DoorLocked

If we are not an administrator, this would return .NULL., since that would be the return value from the assign method for non-administrators.

So whenever you try to access a property or assign a value to it, Visual FoxPro checks if there is an access or assign method, and if so it is executed instead of talking to the property directly. There is only one exception: Once the program focus is in an access or assign method, Visual FoxPro flips an internal flag that tells it that the methods should now be bypassed. Otherwise we could never assign a new value to a property from within the assign method, because we would only end up firing the method again when we tried to finally assign the value. This seems obvious, but it can get tricky in more complex scenarios. Keep in mind that these methods can do everything regular methods can do, including sending messages and calling other methods. Of course, these methods might then access the same property, but since we are in this special state now, access and assign methods will be bypassed. This can lead to some unexpected behavior and very hard-to-find bugs, so watch out!

Of course, restricting access is only one possibility offered by this feature. Let me give you another example: Let's say our movie theater is doing so well, we finally made enough money to buy another theater at the other end of town. Of course, because we're good businessmen, the theater we purchased is modern and already has some software to manage the screens. Unfortunately, the software is not compatible with ours. But of course we want to manage both theaters with the same application. Let's just assume that using the new theater's software is out of the question. But our new theater has features our software doesn't support. We somehow need to link these features to our application.

Let's say the screen class in the other application does not have a DoorLocked property. Instead, there are three methods that allow locking doors, unlocking doors and asking for the current door status. Unfortunately, our application does not support that because it expects the property. So what can we do?

Well, first of all we can add the DoorLocked property, just as a dummy. Then we'll add access and assign methods for this property. We can then use these methods to reroute the call to the messages the class supports:

DEFINE CLASS TheirScreen AS Custom
DoorLocked = .NULL. && This is just a dummy

FUNCTION DoorLocked_Assign( Lock )
IF Lock
* their method

THIS.LockDoor()
ELSE
* their method

THIS.UnlockDoor()
ENDIF
ENDFUNC

FUNCTION DoorLocked_Access
RETURN THIS.GetDoorStatus()
ENDFUNC

FUNCTION LockDoor
** Original code would be here
ENDFUNC

FUNCTION UnlockDoor
** Original code would be here
ENDFUNC

FUNCTION GetDoorStatus
** Original code would be here
ENDFUNC
ENDDEFINE

Now we can simply link the foreign object in our application without going through any major rewrite. So what are we waiting for? Let's buy another movie theater from the money we saved on the software!

As you can imagine, access and assign methods are important for object reuse, but they also make maintenance tasks and changes a lot easier. I remember when we first started to use the Outline control that shipped with Visual FoxPro 3.0. It allowed creating tree structures just as in the Windows 3.1 File Manager. To fill it with data, you had to send messages and assign values to properties. After all, this was a tricky process.

But shortly after Visual FoxPro 3.0 was released, Windows 95 was introduced, and it replaced the ugly Outline object with the new TreeView class. Of course, all the customers wanted the Windows 95 look and feel, and I ended up rewriting all my code because the TreeView object handled almost everything through methods, and all the property and method names had changed. This was the first time I wished for access and assign methods. Using them, I could have simply redirected all the calls to the new methods. Writing an Outline-compatible TreeView class would have been a piece of cake.

You can also use access and assign methods to create your own events. A good example would be the TextBox base class. It has a property called Value, which reflects the current content of the textbox, and it has a couple of events that fire whenever the value changes, like InteractiveChange (which fires when the user enters something) or ProgrammaticChange (which fires when the value is assigned programmatically). However, the value of the textbox can change for other reasons. For example, if the textbox is tied to a field in a database, the value would change whenever the record pointer moves. Unfortunately, no event would fire then. We can create our own event, as demonstrated in the following example:

DEFINE CLASS MyTextbox AS Textbox
FUNCTION Value_Assign( Value )
THIS.Value = Value
THIS.OnChange
ENDFUNC

FUNCTION OnChange
* This is only a placeholder
ENDFUNC
ENDDEFINE

In this example, we fire a method called OnChange every time the value property changes. This includes scenarios where the user enters something (which would also fire an InteractiveChange event), when a value is assigned programmatically (which would also fire the ProgrammaticChange event) or when the textbox's control source is a field in a table and the record pointer is moved. In subclasses of this class, the OnChange method can be treated like every other kind of event method.

This_Access

This method has a special status. Unlike other access methods, it is very generic. It uses the THIS pointer to trap every property access, no matter which one it is. The parameter that is passed to this method is the name of the property that is accessed. You cannot reroute the call to a different property. This still has to be done in the access method for this particular property. However, you can actually return a completely different object reference.

Imagine a form that has a command button. In the THIS_ACCESS method of the form, we put the following code:

FUNCTION THIS_ACCESS
LPARAMETERS lcMember
RETURN THIS.Command1
ENDFUNC

This code reroutes every property call from the form to the button. When accessing the form's caption property, you'd actually talk to the button instead. This also works when assigning a value to a property, because you access the object pointer to assign a value to a property. For this reason, there is no need to create an assign method for the THIS pointer.

You can also use this assign method to add properties on the fly. VBScript demonstrates the usefulness of this ability. The idea is to add a property on the fly in case somebody tries to assign a value to a property that doesn't exist. Here's the code that accomplishes that:

FUNCTION THIS_ACCESS
LPARAMETERS lcMember
IF NOT PEMStatus(this,lcMember,5)
this.AddProperty(lcMember,.NULL.)
ENDIF
RETURN THIS
ENDFUNC



Advanced Object Oriented Programming with Visual FoxPro 6. 0
Advanced Object Oriented Programming with Visual FoxPro 6.0
ISBN: 0965509389
EAN: 2147483647
Year: 1998
Pages: 113
Authors: Markus Egger

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