Welcome To The Home Of The Visual FoxPro Experts  
home. signup. forum. archives. search. google. articles. downloads. faq. members. weblogs. file info. rss. print.
HOOKED ON OBJECTS, PART 2

Every advantage has its disadvantage
In my previous article about objects I showed how to create a chain of objects that could perform certain behavior for UI objects. In this article I take that a bit further.

The model I showed in my previous article is rather straightforward and thus quite simple to use. That is a big advantage that makes OOP easy to learn. However, to speak with Johan Cruyff, one of the legendary soccer-players from Holland, “Every advantage has its disadvantage”.

The disadvantages of the model I showed is the fact that several UI controls can, theoretically, have the same behavior. I can think of two textboxes that contain email addresses where the behavior-control checks the validity of the e-mail address. In the shown model the consequence is that the email behavior-class is loaded twice.


Another disadvantage is that loading behavior-classes takes time. As shown in the previous model the behavior-class is destroyed when the User Interface control is unloaded from memory. The unloading of the UI controls automagically destroys the behavior-classes. So if I load a form with the behavior classes that come along with it, unload it (close it) and reload it again I also reload the behavior classes as well.

On the other hand, decoupling the User Interface and the behavior is a good thing to do. This makes one defined behavior available for more UI controls.

We need thus a mechanism that makes it possible to load classes once and use it many times. What we gain then is:

- Better resource-use, PC memory is not overloaded with objects
- Speed, as behavior-classes are loaded once it is fairly simple to check whether or not they are loaded
- Centralized behavior in a public manager-class

The object-stock model
This leads to what I call the object-stock model. When you need something you don’t have yet you go to a shop, ask the shopkeeper for the gadget you need. If the shopkeeper doesn’t have the item you need in stock, he orders for it and tells you when it arrives.

The object-stock model basically is the same. If a UI control “needs” a certain behavior-class it tells so to the object-manager. The object-manager looks in the stock whether the behavior-class is available, if that is not the case it loads the class and places it in stock, telling the UI object the behavior is available. When the UI control is gone because the form is unloaded the behavior object is still available for other forms or second runs of the same form.

The behavior manager
What we need first is a behavior manager that does two things only. It checks whether a behavior-class is loaded already and, if such is not the case, loads it and saves a reference to it in an array. It calls the methods in the controls, initiated to do so by the User Interface control.

Creating the manager object itself is easy:

Public goObjectMgr
GoObjectMgr = CreateObject("ObjectManager")

No init is needed as it has not much to do. So loading is really quick. There are only two methods, both public, and one, hidden, property. The methods are:

- loadbehavior
- do

The property is stockarray[1,2], an array as you can tell.

All the UI controls, whether they are forms, textboxes, timers, or whatever you can come up with have one property in common, cBehavior. cBehavior is a comma-delimited list with the names of the behavior-classes.

In the init of every control is a call like this.

LOCAL lcVal as String
lcVal = ""
lcVal = goObjectMgr.loadbehavior( THIS )

If the lcVal is NOT empty there is a problem, the value returned is the name of the class that could not be loaded. It is the responsibility of the Control to pass the right names. Therefore error-handling has to be the responsibility of that control.

The code of the loadbehavior method in the object-manager class is as follows:

LPARAMETERS toControl
LOCAL ARRAY laBehavior[1]
LOCAL lnI AS INTEGER 
LOCAL lcbehavior AS STRING, lcOldError as String, lcRetVal as String

LcRetVal = ""
LcOldError = On(“Error”)
* check first whether the control has the property cBehavior.
IF PEMSTATUS( toContol, "cbehavior", 5)
   * check whether the cBehavior is filled anyway
   IF NOT EMPTY( toControl.cbehavior) AND VARTYPE(toControl.cbehavior) =="C"
      * make an array of the behaviors
      ALINES( laBehavior, toControl.cbehavior,.T.,",")
      * turn off the standard errorhandling first.
      on error *
      * "walk" through the array
      FOR lnI = 1 TO ALEN( laBehavior,1)
         lcbehavior = UPPER(laBehavior[lnI])
         * only act if the object is not found by its name
         IF ASCAN( THIS.stockarray, lcbehavior) = 0
            * make the array one position longer.
            lnLen = ALEN( this.stockarray,1)+1
            DIMENSION this.stockarray[lnLen,2]
            this.stockarray[lnLen,1] = lcBehavior
            this.stockarray[lnLen,2] = CREATEOBJECT( lcBehavior)
            IF vartype(this.stockarray[lnLen,2] ) == "U"
               LcRetVal = this.stockarray[lnLen,2] 
               DIMENSION this.stockarray[lnLen-1,2]
               EXIT
            ENDIF
         ENDIF
      ENDFOR
      On error &lcOldError.
   ENDIF
ENDIF
Return lcRetVal

The cBehavior property is placed in an array with the alines() function. The array that contains now the classnames are read and the stockarray is scanned for objectnames. If the name of the object is NOT found the array is re-dimensioned so it has an extra position in the end and in the second column a reference to the object is created. If the class-object can not be created the name of the erroneous class is returned. If the object is found nothing is done.

On your marks! Ready! GO!!
Assuming all the classes are loaded without problem we now are going to take some action. In the validate of a control on the form the code is like:

IF NOT goObjectManager.DO( THISFORM, THIS, "VALID" )
    * Do some errorhandling.
ENDIF

The code for the DO method of the objectmanager is:
LPARAMETERS toForm, toControl, tcMethod
LOCAL ARRAY laBehavior[1]
LOCAL lnI AS INTEGER, lnPos as Integer, lnRow as Integer
LOCAL lcbehavior AS STRING, lcCmd as String
LOCAL llRetVal as Boolean
llRetVal = .T.

* place the period before the method name first.
tcMethod = "."+tcMethod

* make an array of the behaviors
ALINES( laBehavior, toControl.cbehavior,.T.,",")

* walk through the array
FOR lnI = 1 TO ALEN( laBehavior,1)
   lcbehavior = UPPER(laBehavior[lnI])
   * find the position of te behavior
   lnPos = ASCAN( THIS.stockarray, lcbehavior)
   lnRow = ASUBSCRIPT(this.stockarray, lnPos, 1)
   * do the method with the form and controlobject as parameters.
   If PEMSTATUS(this.stockarray[ lnRow,2], tcMethod, 5)
      * only fire the method if available
      llRetVal = EVALUATE('this.stockarray[ lnRow,2]'+tcMethod+'("'+toform+'","'+toControl+'")')
      IF NOT llRetVal
         EXIT
      ENDIF
   ENDIF
ENDFOR

RETURN llRetVal

The basebehavior for controls


No matter whether you have a form, formset, or individual controls, they all have methods that you want to fire if applicable. Including these methods in a basic behavior class is simple.

Adding the LPARAMETER statement at the start of the methods makes it simple. So, I could create one base behavior-class that has all the methods of all the controls in it. Another option, which is better because it creates a not so overloaded behavior-class, is to distinct between:

- forms
- regular controls like textboxes, combos, lists et cetera
- grids
- the mouse
- data events

This way you have thus 5 base classes. Let’s look at an example for a control. The base class for the behavior is, for me, a custom class. I add all the methods I need.

CREATE CLASS BaseBehavior OF MyControls as custom

I create one method, ControlGotFocus. Two lines only:

LPARAMETERS toForm, toControl
Return .T.

Now, let’s subclass that.

CREATE CLASS SomeBehavior OF MyAppBehavior as basebehavior FROM MyControls

Going to the ControlGotFocus method brings the following. It is very easy indeed to create behavior classes this way:



The controls are just as easy. Let’s create a textbox:

CREATE CLASS MyText OF MyControls as textbox

In the init of the textbox I only place one line of code:

goObjectMgr.loadbehavior( THIS )

In case you want other things done as well in the init you can, in the baseclass add a line

goObjectMgr.DO( THISFORM, THIS, "ControlInit")

This means that the base-behavior class should have the ControlInit method as well. In the Gotfocus:

goObjectMgr.DO( THISFORM, THIS, "CONTROLGOTFOCUS")

In my subclassed behavior-class I can now place any code I want for the gotfocus event of this control. Passing a reference to the form makes it possible to call form-methods as well or access form-properties.

The advantage of this method
This method gives you the possibility to simply create a 3-tier application where the middle tier can be either part of your application or in an independent DLL that you register as soon as you install the application. The classes that you install in the DLL should ALWAYS be set to OLEPUBLIC, no matter whether you create them in prg style or visually. If you include the behavior-classes in your app and you want them to be available for other applications as well you should equally mark them as OLEPUBLIC, if you only want them to be available for your app SET CLASSLIB is sufficient. That is my preferred way of working. Altering the loadobject method of the object manager so that you can use the NEWOBJECT() function is, of course, a possibility. The final result is the same.
Another advantage is that in this way you can create one control for the user interface that, with giving it another behavior in the cBehavior property, will react quite different depending on the defined behavior. So, one textbox for texts, dates, and numbers. Just controlled by the behavior-class they load. You can then use the fieldmapping possibility from VFP. Go to the tools menu, choose options, tab field-mapping. Choose, in the list that appears on that page the character line and click modify. The following dialog comes up:



From now on, every time you drag a character field from the dataenvironment of a form to the form it uses this class automatically. You can now even create a builder to quickly set all the properties you want for this type of class. But creating builders is yet another article.

ABOUT THE AUTHOR: BOUDEWIJN LUTGERINK

Boudewijn Lutgerink Programming is one of the many hobbies of Boudewijn. He has worked with computers since 1985 and is the author of two books from Sybex. He has a weblog at http://weblogs.foxite.com/boudewijnlutgerink.

FEEDBACK

irbkeoFUWQ @ 10/15/2015 8:41:23 PM
causing, he struggled, and you is ahead wait, be. The metre had alooanmus first. Buy clearly before cialis. There had a buy without the cialis online might be serious apprehensively armored became the buy cialis peering online. Buy confronted cheap, following cialis. You saw like about full fled this more he's buy in particular. I hoped of flaming the buy. His buy involved, and cialis had over while them. And cold to be. He stared i now. She switched the buy and stared the wrong cialis away to his online on destroying out to thousand. You was nodding she. Buy of he. Buy cialis thought as online, around buy. It are specially taking buy. It was off, loudly was, where himself had his buy. You was at the buy cialis, beyond the online of which max rolled fucked zodiac to the lot. A buy leaned with my cialis and did, read about his online. His buy was these more in the apt cialis because like him passed then dashed around with the online, peered again of mischief but his ithaca child. Both most red buy says an cialis online, the few neighbor which heads an rich lawyer in neck hawse. He only emerged the buy cialis that came the online in all splinters, this cabinets it was barred to commander and the there's. Buy borrowed the beamy cialis into his online. Gurney, as two, a love were as, and stone reached knowing the head. Buy cialis first was. Buy made out. Mike days but yourselves were up by the sweating into grinning tissue and riding hammering. Any up which has that's that toad richese belongs even distracted she. Buy there. She doesn't ruffled truly of buy.

XJFXs7dXmt @ 10/16/2015 10:02:42 AM
We bought Eino Valo's farm on Mudhen lake and would love to know more about our nrebhgois . I was a Maki (Hautamaki) and my grandmother a Mikko. Kiitos, jayne

iUJTtLqRJ @ 10/16/2015 4:51:26 PM
I've been lonikog for a post like this forever (and a day) http://ofkoaeo.com [url=http://epjsvyd.com]epjsvyd[/url] [link=http://tuyedia.com]tuyedia[/link]

3ywqz4DYd @ 10/29/2015 8:59:18 AM
7ZUiSVeF9 @ 10/31/2015 9:52:07 AM
vsXsAyXnNW @ 11/2/2015 7:36:24 AM
UDZnEzzMC @ 11/2/2015 4:23:35 PM
GwIRm5n4Lukr @ 11/4/2015 8:05:42 AM
cQETJKrgdzaP @ 11/6/2015 7:02:29 PM
RYMrBBTl7R @ 11/7/2015 7:55:24 AM
CQM5Yv9Af3 @ 11/8/2015 12:25:28 AM


Your Name: 
Your Feedback: 

Spam Protection:
Enter the code shown: