2.4 Differences between classes and forms

Differences between classes and forms

Classes (I'll talk about subclasses of the Form base class later in this chapter) and forms have many similarities. If you look at the Form and Class Designers, you'll discover that they look almost the same. As a matter of fact, they are pretty much the same internally. But there are several differences between real forms and classes that are subclasses of forms. Both have advantages, too, which makes it hard to decide which one to use.

Instantiating forms

The first big difference between classes and forms is how they are instantiated. I already described how classes are instantiated. Form classes cannot be members of other container classes (FormSets are the rule-proving exception, but I'll talk about these later). For this reason, forms are usually stand-alone objects. (Sometimes they are attached to objects that use properties, as an object reference, but that doesn't really make a difference.) So the only way to instantiate a form class is to use the CreateObject() and NewObject() functions (not the .NewObject() method!).

In order to use one of these functions, you have to provide a reference name for the new object, as in the following example:

oForm = CreateObject("Form")

Form classes always instantiate with the Visible property set to .F., so in order to make the form visible you have to issue one of the following commands:

oForm.Visible = .T.
oForm.Show()

Both would have the same result. Until Visual FoxPro 5.0, I preferred to use the Show() method. This would allow me to add additional functionality if I needed some behavior every time a form was displayed. Now, I tend to use the first option (setting the property) because I can always add behavior using an assign method. Setting the property is a little faster, too (about 25 percent, according to my performance tests).

So far, this has been easy. You created an object and displayed it. However, it gets even easier once you start using forms instead of classes. Here's how you would create a form:

DO FORM Form.scx

This command makes FoxPro look for a form called Form.scx (the .scx is optional). It creates the form and displays it right away. I guess FoxPro figures that forms don't do any good unless they are visible. With classes, this is a totally different story. In fact, most classes never become visible or don't even have a visible property or any visible appearance.

However, you might want to run a form and leave it invisible. The DO FORM command lets you do that as well:

DO FORM Form.scx NOSHOW

This would create the form object, just as the CreateObject() or NewObject() functions do, and keep it invisible. You can make the form visible later on using the method or property introduced above. In order to do that, you need to know the object's name, which might be tricky every now and then. To make this easier, I recommend using the NAME clause wherever possible:

DO FORM Form.scx NOSHOW NAME oForm
** Lots of code goes here
oForm.Visible = .T.

If you can't use the NAME clause (for reasons described later), I recommend using the Forms() collection, which FoxPro automatically maintains as a property of _Screen (see the Collections discussion in this chapter). You could try to use the form's name (name of the .scx file), but I don't recommend that because you might end up talking to the wrong form, especially when instantiating the same form twice.

This brings us quickly to the next point: How do you run multiple instances of the same form? With real forms it's easy. All you have to do is issue the DO FORM command several times:

DO FORM Form.scx
DO FORM Form.scx

This would start two instances of the same form. They would be two different and fully functional objects. Because these forms are regular FoxPro objects, both need to have object references, of course. But you don't have to worry about them because FoxPro creates these references itself. Every one of these references has to be unique, which is the tricky part, because once you instantiate a form multiple times, these reference names will change. That's the reason why I recommended using the Forms() collection instead. Using the NAME clause wouldn't work either, because you'd have to use a new name each time so you couldn't approach this generically.

Creating multiple instances of form classes introduces a similar problem as with the NAME clause. The reference you use has to be unique, so in order to instantiate two form classes you would do something like this:

oForm1 = CreateObject("Form")
oForm2 = CreateObject("Form")

This would create two forms of the same class, just as in the example with the DO FORM above. Unfortunately, this code is not generic, because every time I create a new instance I have to think of another reference name. Another way of doing this is to use an array as the object reference, like so:

DIMENSION ApplicationForms(2)
ApplicationForms(1) = CreateObject("Form")
ApplicationForms(2) = CreateObject("Form")

I could take this code and put it in a method to make it more generic. The method and the array could be members of a form-handler object, which leads me to my first example of how objects work together:

 

 

 

DEFINCE CLASS FormManager AS Custom
DIMENSION ApplicationForms(1)

FUNCTION NewForm( FormClass, FormClassLib )
* We check if we have to redimension the array
IF Alen(THIS.ApplicationForms,1)=1 AND NOT ;
VarType(THIS.ApplicationForms(1))="O"
DIMENSION THIS.ApplicationForms( Alen(THIS.ApplicationForms,1) + 1 )
ENDIF

* Now we create the new form and show it
IF Empty(FormClassLib)
* The classlib has to be set already
THIS.ApplicationForms(Alen(THIS.ApplicationForms,1)) =;
CreateObject(FormClass)
ELSE
* We also deal with the classlib
THIS.ApplicationForms(Alen(THIS.ApplicationForms,1)) =;
NewObject(FormClass,FormClassLib)
ENDIF

* We check if the form has been created
IF VarType(THIS.ApplicationForms(Alen(THIS.ApplicationForms,1)))="O"
* Form has been created
THIS.ApplicationForms(Alen(THIS.ApplicationForms,1)).Visible = .T.
* We return a reference to the new form
RETURN THIS.ApplicationForms(Alen(THIS.ApplicationForms,1))

ELSE
* Form hasn't been created,
* so we have to delete the reference from the array
IF Alen(THIS.ApplicationForms,1) = 1
* The array only has one item, so we can't make it any smaller
THIS.ApplicationForms(1)=.NULL.
ELSE
* We shrink the array
DIMENSION THIS.ApplicationForms(Alen(THIS.ApplicationForm,1) - 1 )
ENDIF
RETURN .NULL.
ENDIF
ENDFUNC
ENDDEFINE

In a real-life scenario, this class would be instantiated when the application started, and it would always be available. To create a new form, I would pass the form's class name. The class library is optional, which makes the code somewhat more complex but easier to use (I like to make my code more complex so it's easier to use for others). When the form has been created, the manager returns an object reference to the new form. The following example shows how this works:

LOCAL NewForm, NewForm2
NewForm = oFormManager.NewForm( "MyForm", "Forms.vcx" )
NewForm2 = oFormManager.NewForm( "Form")

However, I really need to check if the reference that has been returned is an object. If an error occurred and the form couldn't be created, the form manager would return .NULL. instead of a real object reference. This also makes the code above a bit more complex than it would be in its purest form. But still, the form manager is not quite ready yet. Keep in mind that you might want to pass parameters to the new form, which wouldn't be possible this way. In fact, passing parameters with a form manager usually turns out to be a real nightmare, so I won't go into details about this problem.

In the example above, I created two local variables that I used as references for the new forms. However, I wouldn't really need them. They are just convenient in case I want to talk to the new forms. They won't cause any troubles, either, because they go out of scope (since they are local) and won't result in outstanding object references. The form itself is kept alive by the array that's a member of the form manager. This requires that the form manager stay alive until the application finishes; otherwise, all the forms would disappear, too.

As you can see, this is a lot of work compared to the simple DO FORM command above. So there are definite advantages to real forms when it comes to instantiation, especially multiple instantiation.

Current score: forms 1, form classes 0.

DataEnvironment

When I started to write this book, I was under the impression that I wouldn't have to write about data at all. However, there are DataEnvironments that are somewhat object oriented (after all, they have their own FoxPro base class), even though you can't subclass them or do other cool things.

DataEnvironments can be attached only to forms not to form classes. It's just as easy as that. No long explanations, no tricks for getting around this restriction. Form classes simply can't have attached data environments. Of course, you can still open tables (or other data sources) yourself and, contrary to popular opinion, form classes can also have their own data sessions. You, as the developer, just have to create them. For obvious reasons, this makes it a lot harder to work with form classes than with regular forms.

Current score: forms 2, form classes 0.

Inheritance

After discovering all the disadvantages of form classes, there must be something that's good about them, and there is: subclassing! Forms cannot be subclassed, but classes obviously can. This is a pretty good reason to use form classes instead of forms, and to deal with all the disadvantages. As a matter of fact, most gurus actually use form classes.

This is a big advantage for the form classes. It's hard to give a score, but I'd say that the form classes tied the ball game (at least). So the decision becomes even more difficult. But do you really have to decide?

Combining form classes and forms

As I discussed earlier, forms cannot be subclassed. However, they can be at the end of the inheritance tree, which means that you can create a form class, subclass it several times, and create a form based on this class.

This combines the advantages of both form classes and regular forms. I usually create all my forms in classes, create a form based on those classes (and don't change a single property, method or member object there), and add a data environment. Whenever I need a subclass, I simply create it in my inheritance tree, create a new form based on that class and add a new data environment there (which is somewhat redundant, but not everything can be perfect).

This way, I combine all the advantages. I have subclassing, I have data environments, and I also have the ease of instantiation using the regular DO FORM command.

That should be an easy sale!



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