Hacker’s Guide to Visual FoxPro
An irreverent look at how Visual FoxPro really works. Tells you the inside scoop on every command, function, property, event and method of Visual FoxPro.
These methods, added in VFP 6, make it easier to write good OOP code. Every property of an object can have an Access method and an Assign method. The property’s Access method fires whenever the property is read (accessed) while its Assign method fires whenever the property is changed (assigned).
PROCEDURE property_Access
PROCEDURE property_Assign
LPARAMETERS uNewValue
One of the key OOP themes is encapsulation, the idea that an object should know how to take care of itself. These methods take that idea one step further and let a property take care of itself.
Ordinarily, when a property value is used or changed, there’s nothing we can do about it because no event fires. When properties are bound to controls, we can take advantage of the control’s events to notice changes to the bound properties, but that still leaves us with no way to respond to the use of properties or to changes to properties not bound to controls. Similarly, if we know a property is being used or changed in code, we can write more code at that point to deal with the change. But it requires us to know that the property is used or changed and to make sure we remember to add the code every single time.
Access and Assign methods take a different point of view. They belong to the property and monitor everything that happens to that property. When anyone looks at the property’s value, the property’s Access method fires. When anyone saves a value to the property, the property’s Assign method fires. This means we can respond immediately to both uses and changes of a property.
Why do we care? Ever had one property that should be updated whenever another one gets changed? Ever wanted to keep track of the number of times a value is read? Ever wanted to create a read-only public property? Until now, we’ve always had to handle these situations by writing code wherever the property gets changed or wherever it gets used. Now we can encapsulate that code with the property itself so that we don’t have to remember to copy the right code or call the right routine every time.
Not only do Access and Assign methods give you new events to hook into, they also let you control what the outside world can see and do with a property. A property’s Access method is called before the value is provided to the code using the property—you can jump in and change what gets returned. Specifically, the code that uses the property sees the return value of the Access method, not the actual value stored in the property. So you can do things like have a different internal representation of a property than what the world sees.
As for the Assign method, it too gets called before the operation actually occurs. The new value is passed as a parameter. It’s up to you whether the value actually gets assigned to the property. You can do any validity checking you want (so only a limited set of values are accepted), ignore the new value altogether (creating a de facto read-only property), change the input, or whatever. When you add an Assign method for a property, it gets created with a line setting the property to the parameter value, but you can change it if you want.
Access and Assign methods don’t just apply to properties; there’s also a special This_Access method that fires at the object level. This can lead to some very, er, interesting and powerful behaviors, like the fourth example below. In that example, if a property of an object does not exist, when an attempt is made to access it, the object creates the property on the fly! This_Access even fires when a method of the object is called.
As with the property-specific Access and Assign methods, This_Access fires first. It returns an object reference—the actual behavior that was requested is applied to that object. That’s right—you can redirect a method call or a request for a property value or an attempt to assign a value to an entirely different object than the one mentioned in the call. It seems to us that you’d want to do such a thing only very rarely. Littering your code with this kind of thing is the path to ravioli code (the OOP equivalent of spaghetti code—code that’s impossible to trace and understand).
We suspect there’s a fair amount of overhead in calling these methods, so don’t create them indiscriminately. Save them for those properties where they really make a difference.
On to practical matters. You create Access and Assign methods through the New Property dialog or the Edit Property/Method dialog. Each contains check boxes for Access and Assign. Checking either one adds that method for that property. The name of the method is propertyname_Access or propertyname_Assign. You can also create Access and Assign methods using the New Method dialog. Just watch your spelling.
You can even create Access and Assign methods for built-in properties of classes. Use the Edit Property/Method dialog. To do this for forms (created with the Form Designer), you have to create the methods using New Method—the form's built-in properties don't show up in the Edit Property/Method dialog. That's probably because forms aren't really sure whether they're objects or classes. |
Once you create the methods, they appear in the usual places for methods: the Methods tab of the property sheet, the drop-down list in the method-editing window, and so forth. Because they already contain code, though, they’re always in the top group in the method editor.
VFP 7 Service Pack 1 fixes a bug in the original release of VFP 7 (but not previous versions of VFP): Storing a reference to an object in a property of another object with an Assign method left a dangling reference to the first object (the one being stored). Dangling references are bad, because they prevent objects from being released properly (for example, forms won't close even when you click on the Close box). |
* Make BackColor read-only
PROCEDURE backcolor_assign
LPARAMETERS vNewVal
* Throw away the new value
RETURN
* Limit a custom property to values between 1 and 10
PROCEDURE myproperty_assign
LPARAMETERS vNewVal
IF VARTYPE(vNewVal)<>"N"
ERROR 1732 && "Data type invalid for this property"
ELSE
IF BETWEEN(vNewVal,1,10)
This.MyProperty = m.vNewVal
ELSE
ERROR 46 && "Expression evaluated to an illegal value"
ENDIF
ENDIF
RETURN
* Make sure a property contains the right value when it's used.
* In this case, perimeter is meant to be the perimeter of a
* quadrilateral, so it should always be the sum of the
* four sides.
PROCEDURE perimeter_access
This.Perimeter = This.Side1 + This.Side2 + ;
This.Side3 + This.Side4
RETURN This.Perimeter
* Demonstration of on-the-fly property creation using
* This_Access
oDear = CreateObject("cusTestAA")
? oDear.False && Displays .F. for the newly created property
oDear.BackColor = RGB(255,255,255)
? oDear.BackColor && Shows the numeric equivalent of white
release oDear
return
DEFINE CLASS cusTestAA as Custom
PROCEDURE This_Access
LPARAMETERS cMember
* Determine if the member already exists
IF PemStatus(This, cMember, 5) && Already exists, okay
ELSE
This.AddProperty(cMember)
ENDIF
RETURN This
ENDDEFINE