Version: 0.00.01 - last update: Sunday, September 27, 2009, 14:45:00
All you have to know about how to monitor your top-level VFP designer session.
Intro
Many VFP-developers who are going to code some cool tools that work with a Form- or a Class-Designer session are faced with a common problem: How to monitor the existence of that designer session? Well, the first thing we have to do in such a case is to get a reference to the designer session in question. I wrote another article about this issue, you can read here.
The Problem
After having retrieved a reference to the object we have to deal with, wouldn’t it be nice to be noticed whenever another designer session gets activated (comes on top)? This can be done using Win-Event binding, but is only necessary/useful if we want to create a behaviour like VFP’s property window, which always shows the property set of the current active designer session (on top of VFP’s windows stack). In all other scenarios it is sufficient to be noticed when someone closes our designer session, that is, when we loose the reference to our target object within the designer host form.
I’ve seen a lot of older solutions (before Win-Event binding was introduced) using a timer that checks the existence of the captured object reference. Let me tell you, using a timer in VFP’s development environment we should try to avoid by all means! On the other hand, using Win-Event binding isn’t fiddling at all. Fortunately, there is a much simpler solution!
_ASSIGN() Comes to Our Rescue
When closing any VFP-Designer session, FoxPro does its best to release all references to the inner objects. This is the “event” we can hook on! All we have to do is to store the object reference of our target inside the designer window to a property of our tool class instance. Next, we have to create an _ASSIGN() method for that property. You might guess, what comes next…
At the moment, VFP shuts down the designer window, all inner objects get released by VFP, storing NULL values to any variable or property that holds a reference in addition. The only thing we have to do within our _Assign() method is to check the incoming value.
The Solution
DEFINE CLASS MyMonitor AS Custom PROTECTED oReference AS Object oReference = NULL PROTECTED FUNCTION INIT LPARAMETERS toRef AS Object LOCAL ARRAY la[3] IF NOT VARTYPE(toRef, .F.) == "O" IF ASELOBJ(la,3) > 0 toRef = la[1] ELSE toRef = NULL ENDIF ENDIF THIS.oReference = m.toRef ENDFUNC PROTECTED PROCEDURE oReference_assign LPARAMETERS toObject AS Object LOCAL IsObject AS Boolean *\\ check if we've stored an obj.-ref. IsObject = VARTYPE(THIS.oReference) == "O" *\\ do the assignment THIS.oObject = toObject *\\ in case we are really releasing an obj.-ref. IF ISNULL(m.toObject) AND m.IsObject = RAISEEVENT(THIS, "EvtTargetReleased") ENDIF ENDPROC PROCEDURE EvtTargetReleased *\\ This is an event you can bind to *\\ or fill with code in your subclass ENDPROC ENDDEFINE
Utilizing an ASELOBJ() within the object’s INIT() isn’t the optimal solution. I’ve done it here to have an additional “fall-back behaviour”. You may want to keep the obj.-ref. retrieval code outside of the class – that’s absolutely okay! Utilizing the class within a larger context could be done like so:
loMySuperCoolTool = CREATEOBJECT("MySuperCoolToolClass") WITH m.loMySuperCoolTool IF VARTYPE(m.loTarget) == "O" .AddObject("oMonitor", "MyMonitor", m.loTarget) ELSE .AddObject("oMonitor", "MyMonitor") ENDIF = BINDEVENT(.oMonitor, "EvtTargetReleased",; loMySuperCoolTool, "OnEvtTargetReleased",2) ENDWITH