Tips and Tricks

  Below are notes and tips arising from discussions at past meetings. many of the v5.5 tips are now quite old but it seemed wrong to assume that no one now used v5.5.

 

New!

v7.01

Passing Properties Between Forms

We thought we knew how to do this but it took us half an hour to get it right! So, in case you too are unsure......

Assume a Parent and a Child form. From the Parent we want to instantiate the Child, setup variables in the Child form and assign values to them. Then open the Child form in such a way that the Child has access to the values.

In the Parent we define a button with an OnClick event called PUSHBUTTON1_OnClick.

function PUSHBUTTON1_onClick

      // set procedure to the Child form so that the compiler has access to the class       // definition (listed below)

set proc to child.wfm additive

     // Instantiate the form, which includes a text control called "title".
     // The form property "efnum" is NOT created by this command.
     // The variable "child" holds the address of the Child form

oChild = new childform()
oChild.title.text = 'Success!' // set the Title text property to "Success!"

     // CREATE a property of the Child form called "efnum" & set its value to 3

child.efnum =3

     // Since the Child form is to be opened modally and modal forms cannot be
     // MDI forms, ensure that the Child's mdi property is false

oChild.mdi = False

     // Open the Child form as a modal form (Modal forms are executed when they
     // called, they do not wait for execution until the end of the current event).
     // The first event to happen is the form_open event (if the Readmodal() method
     // is overridden) and since the form property "efnum" now exists it can be used
     // in this event ... and of course any subsequent event.

oChild.readmodal()

     // The form continues to exist while there is an extant reference to it
     // The reference persists in the variable "child", as proved in the MSGBOX()

MSGBOX('Figure entered is: '+oChild.efnum,'Show result',48)

oChild.release()    // To ensure full recovery of GDI resources release the form
oChild = ""     // and then stub it out by assigning a null value
return

In the Child, any passed properties need to be triggered before the form is opened. So start the Class .... lines with the default readmodal() method set to your own "form_open" function. This must always end by calling default (or "Super") readmodal() method.

class childForm of FORM
with (this)
readmodal = class::FORM_OPEN
etc. etc.

The Form_Open function comprises:

function form_open
 * Inspect(this) // optional, see below
 form.entryfield1.value = form.efnum
return CHILDFORM::readmodal() // now open the form modally

The Inspect(this) line, commented out above, is useful for checking which properties and values have actually been passed. The next line puts the passed value into an entryfield. This is not necessary for the text object as the Parent form had set a specific value for this rather than pass a value through a variable, as with the Entryfield.

this.TITLE = new TEXT(this)
with (this.TITLE)
height = 1
left = 5.1429
top = 1.2727
width = 30
text = ""
endwith

When tested this did in fact show the passed "Success!" and not the single space as above

A pushbutton on the child form calls:

function PUSHBUTTON1_onClick
form.efnum = form.entryfield1.value
form.close()
return

If the figure in the entryfield is changed before clicking the button, the Message Box that pops up in the Parent form will show the new value. And so proves that values can be passed both ways between forms.

A point to watch. A modal form is used above as execution in the Parent form halts until the Child form is closed. The revised entryfield value is then known to the Parent form and shown. If the Child form is not opened modally, execution does not halt and the Message box will show the value passed to the child, not the value returned. Worth remembering, it can often lead to initially inexplicable errors! Also, remember to put: readmodal = Class::Form_Open. If this line starts with open (as it will if the Designer is used), the line will be ignored if the form is called with a readmodal().

Example parent and child forms (Tips1.zip)  (2k)

See also below for a further example of he use of passed properties

 

New!

v7.01

Defining Objects with Macros.

This does not appear to be possible using the Object.... = new ....... construct favoured by v7.01 However the Define method normally used in v5.5 can still be used and does allow object names to be formed in a loop with macros. Although the method shown below does work, any suggestions for doing it a better way will be very welcome!

A Selector.wfm form is used to popup with a variable number of buttons where a user choice is required. Below is an example of code used to call the form and handle the returned value:

function PBUTSelect_onClick

Local cSequence,sSeq

// popup form with buttons for various tasks

Set Procedure to Selector.wfm additive
sSeq = New SelectorForm() // instantiate form

sSeq.PbNumb = 6 // set number of buttons
sSeq.Pb1 = "Booking Ref"' // set text for buttons
sSeq.Pb2 = "Start Date"
sSeq.Pb3 = "Invoice No."
sSeq.Pb4 = "Client Ref."
sSeq.Pb5 = "Property Ref"
sSeq.Pb6 = "Number Only"
sSeq.PbTitle= "Select required Sequence" // set title for form

sSeq.Readmodal() // open form modally

cSequence = sSeq.Select // receive returned value
sSeq.Release()
sSeq = '' // stub out

Do Case

Case cSequence = 'B'

// do whatever
// etc, etc.

The called Selector form has a "readmodal = Class::Form_Open" line (see tip above) which calls:

function form_open
Local cTitle,nCtr
Private cSctr // define as Private as it will be used as a macro

height = 3 + (form.PbNumb*1.5) // calc form height from no of buttons specified cTitle = form.PbTitle // form title
form.rrlabel1.text = cTitle

For nCtr = 1 to form.pbnumb // loop for each required button

cSctr = lTrim(Str(nCtr,1)) // make the loop counter into a string

      // use macro to define button names

DEFINE PUSHBUTTON PUSHBUTTON&cSctr OF THIS;
PROPERTY;
Top 2.0+(nCtr*1.5),;
Text form.Pb&cSctr,; // get the passed text for each button
OnClick CLASS::PBUT&cSctr,; // define an OnClick event for each button
height 1.0909,;
left 15.0,;
width 16

next

SELECTORFORM::readmodal() // now open the form, modally, as normal

return

The pushbutton OnClick events defined above (six in this example) each put a relevant value in the form.select property that is used by the calling form to identify the choice made by the user.

NB, do not open a form with objects defined in a loop in the Designer, it is liable to do unwanted things with it!

NOTE. The full code for Selector.wfm will be included in a User Report Generator set of programs that will be placed in the Library when it is ready.

 

v5.x and v7.01
Process Sequence

It is natural to assume that lines of code carrying out a succession of data manipulation tasks will be processed in sequence. In DOS yes but in Windows not necessarily. Consider this example from v7.01

function RRADDBUTTON1_onClick
  Local p,fProp,cPropid,fCust,fn,cCustId

  If Form.NoteNo > 5

  // Notebook may not be on pages 6 or 7 when Adding - call function to check       and change page if necessary. Also checks that current row is saved
     CLASS::NOTEBOOK1_onSelChange()

Endif

  // call function to BeginAppend, disable Navigation buttons, enable fields etc.
RRADDBUTTON::OnClick()

What actually happens is that NOTEBOOK1_OnSelChange() runs after RRADDBUTTON::OnClick() has completed, not before. So it sees the new blank record and immediately saves it. This prevents the user from selecting a Cancel button and gradually accumulates blank records in the table. The solution was to have a new function, similar to NOTEBOOK1_onSelChange() that in this case just changed the notebook page without doing a save. It did not then matter whether it was processed before or after.

A similar but more complex situation has been seen in v5.5. In this instance a SEEK was attempted before some earlier code had created the index! The solution here was to divide the processing code into sections, do each section from a  Case ... statement, the whole inside a loop that incremented a counter so that the Do Case called a fresh section on each loop.

It is worth watching out for occurrences of this kind. The effects or the cause of any errors are not obvious and can prove very frustrating.

 

v5.x - Converting Fox to dBASE - Indexes

 Fox has an instruction for deleting all indexes. How is this done in dBASE?

    Each index has to be deleted in turn, and then re-created.

A program called CREINDEX on the dBASE Forum automatically extracts index information from a table, deletes the indexes and re-creates them.

One member recommends that each project should have its Re-Index program which contains the necessary code to delete and recreate all indexes.

 

v5.x - Changing the number of tabs on a TabBox

The number and tabs on a TabBox can be changed at any time simply by changing the TabBox.DataSource, eg

    form.tabbox1.datasource = 'ARRAY {"TABBOX1","Tab 2"}'

If the number of tabs is reduced, the original pages are not lost, and can be recovered by re-setting the datasource at a later time.

Remember to set Form.page to the desired page before starting, and use the OnLeftMouseUp event of the TabBox to set the page number, eg
       form.pageno = form.tabbox1.cursel

   
   
   
   

 Home Page

 Articles       Demos      Library      Meetings       Members