The basic idea here is to attach a special right-click mouse event to our Text objects. When the user right-clicks on a Text object, we present a dialog, in which he/she can change various properties of the Text object: font, color, position, and so on — even the text itself. The user’s preferences get saved to a table. Henceforth, whenever the form opens, the user’s preferences are read from the table and are applied to the Text object.
The idea is not complicated. All that remains is to implement it so that it performs smoothly and does not add to a developer’s burden. The answer, of course, is to use a custom class. In fact more than one, as we shall see.
We’ll begin by creating the table
to store the users’ preferences.
Structure for table ConfigTx.DBF Field Field Name Type
Length Dec Index
|
|
Each record in this table will constitute the preferences for a single Text object on a single form.
The purpose of the ObjName field is to identify which Text object on which form each record applies to. For example, MYTESTFORM.MYTEXT1 would mean that the record applies to the MyText1 object in the MyTestForm class.
The rest of the fields (2 through 15) obviously correspond to the properties of the Text object that we’ll let the user mess around with.
This table and its MDX do not have to be deployed with your applications. If it’s absent, the table will be created automatically when the user right-clicks on any configurable Text object.
The custom text class
It’s time to define our custom
Text class.
Class zText(parentObj) of Text(parentObj) custom with( this )
Function Init
endclass |
|
What’s the “Init” method?
It’s like an onOpen event, but it’s preferable for two reasons:
1. If we used an onOpen event, it would fire after the form becomes visible, so the user would first see the Text object displayed without his preferences, then his preferences would be applied and the look of the object would change right before his eyes. Uggg-ly! Our Init “event” will “fire” before the form opens - which will be faster as well as cleaner-looking. How will the Init fire? We’ll deal with that in a moment.
2. Developers might want to use the Text object’s onOpen event for other purposes — and in fact they might already have done so. By staying clear of the onOpen event, we make it safer and easier for developers to apply this technology retroactively to applications that have already been developed.
Getting the Init() method to fire automatically
We need to ensure that the text
object’s Init() method
will fire each time the form opens. To accomplish that, we’ll build
a custom form class that employs a very useful trick: extending the behavior
of the Open() and
ReadModal() methods.
The added behavior is essentially to examine each object on the form, and
fire its Init() method
if it has one. Here’s the complete source code for the custom form
class:
// Pete's zForm (VdB7 base form) class // Thanks to Romain and Bowen for their ideas. // // Things happen in the following order, each time you call form.open() or form.readModal(): // 1. Form.init() method executes. // 2. Init() methods of objects on the form execute, one by one. // (But only the first time the form is opened.) // 3. Form.AfterControlInits() method executes. // 4. The form becomes visible. // 5. Form.OnOpen() method executes. // 6. OnOPen() methods of objects on the form execute, one by one. // // Bear in mind that the CONSTRUCTOR section of your form fires ONLY when you do // <something> = new WhateverForm() class zForm of Form custom with (this)
Proc Open
Proc ReadModal
Func BeforeOpen
Proc Init
Proc AfterControlInits
Proc RunControlInits(InitialObject)
endclass |
|
The preferences dialog
The dialog form that gets invoked when the user right-clicks on a Text object is called ConfigTx.wfm . I won’t bother showing or explaining its source code in this article (it’s fairly mundane), but here’s what it looks like (the form entitled “Adjust properties of text object”):
The ConfigTx.wfm dialog also features a few very nice goodies (and these classes are included in bu07rorl.zip):
Color picker — an Entryfield class for specifying Foreground/Background colors, that includes an embedded wrench button to select the color combination from a nifty color-palette dialog.How to use these classesFont picker — a Container class that includes controls for fontName, fontSize, fontBold, and fontItalic, plus an embedded wrench button to let the user specify all those things via a standard font selection dialog.
Zoom editor — an Editor class that includes a button to invoke a dialog that lets the user view or edit the text on a much larger surface (or even full-screen), with the ability to Save or Cancel changes.
FlashButton — a Pushbutton class that “lights up” as the mouse moves over it.
To make all the
Text objects user-configurable
in any of your own applications, all you have to do is:
1. Include
the files in bu07rorl.zip (minus
Demo.wfm) in the project.
2. set
procedure to zControl.cc additive at
the start of your Main.prg
or
your Setup.prg.
3. Make
sure that all your forms inherit from
zForm.
4. Make
sure that all your Text objects
inherit from zText.
Note: If you always use
custom classes in your applications (as you should!), Steps 3 and 4 should
require only two tiny changes to your library of custom classes:
Change this: Class MyText(oForm) of Text(oForm) to this: Class MyText(oForm) of zText(oForm) | |
and:
Change this: Class MyBaseForm of Form custom to this: Class MyBaseForm of zForm custom | |
This is a good illustration of
why it’s so much better to always
use custom classes throughout
your applications — even if your custom class definitions are behaviorally
identical to the native classes. For example:
class MyEntryfield(o) of Entryfield(o) custom endclass |
|
Why bother with this? The
answer is that sometime in the future, you’ll be able to universally change
the behavior of all the entryfields
throughout
your application(s),
by making a small change to the custom class. You’ll just need to
edit one CC file, instead of having to edit 300 WFMs. For example,
suppose your users wanted all the entryfields to have a yellow background
when they get focus. This is so easy — if you’ve been using custom
classes all along:
class MyEntryfield(o) of Entryfield(o) custom ColorHighlight = 'N/RG+' // yellow background when in focus. endclass |
|
Just add one line, and rebuild and redeploy the EXE. That’s one of the benefits of OOP.
The classes discussed in this article are just small examples of the leverage you can get from good object-oriented programming.
Click here to download the complete source code and the Demo.wfm. Unzip bu07rorl.zip into a new folder, change to that folder in dBASE, and run Demo.wfm to try it out.