The Form Template
The basic form template for this application is a flat file form. Most, if not all, applications have one or more of these, and some apps have dozens. So it's immediately useful, while still possessing sufficient simplicity to make the code easy to understand. When you catch on to the principles, more complex templates and data access methods should be easy to build.
What's a little tricky is understanding how form templates and the data tier interact. During the Depression, my father said that a humorous way of dismissing the difficult situation at hand was to say "If we had some ham we could have ham and eggs. If we had some eggs...." The form template and the data tier are each written with the other's function in mind. The reusable components go in the data tier, and the calls to use these components go in the template.
For example, Listing 3.5 shows the class code for my FlatFileForm template. I'll comment it as we go.
Listing 3.5 The FlatFileForm Class
MainTable = .F. && Name of the main table for the form KeyField = .F. && Name of the Primary Key for the Main Table KeyValue = .F. && Value in the KeyField of the current record Beforeadd = .F. && Record number before adding (used when Add is canceled) SearchForm = .F. && Name of a "pick one" form called by the Find button Adding = .F. && True at "Save" time if "Add" button was clicked Inputfields = "MYTEXT,MYCHECK,MYEDIT,MYCOMBO,MYSPIN,MYDATE" * Classes to enable/disable Beforeadd = 0 && Record pointer value before an add or edit begins PROCEDURE Buttons && Turns form buttons on or off as needed LPARAMETERS OnOff WITH THISFORM .cmdAdd.Enabled = OnOff .cmdFind.Enabled = OnOff .cmdClose.Enabled = OnOff .cmdEdit.Enabled = OnOff AND RECCOUNT() > 0 .cmdDelete.Enabled = OnOff AND RECCOUNT() > 0 .cmdSave.Enabled = NOT OnOff .cmdCancel.Enabled = NOT OnOff .cmdClose.Cancel = OnOff .cmdCancel.Cancel = NOT OnOff ENDWITH ENDPROC PROCEDURE Inputs && Enables/Disables form controls LPARAMETERS OnOff WITH THISFORM FOR EACH Ctrl IN .Controls IF UPPER ( Ctrl.Class ) $ UPPER ( .InputFields ) Ctrl.Enabled = OnOff ENDIF ENDFOR .Buttons ( NOT OnOff ) ENDWITH ENDPROC PROCEDURE Load * Runs when an instance of this form class is instantiated WITH THISFORM IF EMPTY ( .MainTable ) MESSAGEBOX( [No main table specified], 16, [Programmer error], 2000 ) RETURN .F. ENDIF oDataTier.CreateCursor ( .MainTable, .Keyfield ) ENDWITH ENDPROC PROCEDURE Init && Runs after buttons have been instantiated THISFORM.Buttons ( .T. ) ENDPROC PROCEDURE Unload && Closes table or cursor opened by this form WITH THISFORM IF USED ( .MainTable ) USE IN ( .MainTable ) ENDIF ENDWITH ENDPROC PROCEDURE cmdAdd.Click && Adds a new record, autopopulating the key field WITH THISFORM cNextKey = oDataTier.GetNextKeyValue ( .MainTable ) SELECT ( .MainTable ) .BeforeAdd = RECNO() CURSORSETPROP( [Buffering], 3 ) APPEND BLANK IF TYPE ( .KeyField ) <> [C] cNextKey = VAL ( cNextKey ) ENDIF REPLACE ( .Keyfield ) WITH cNextKey .Refresh .Inputs ( .T. ) .Adding = .T. ENDWITH ENDPROC PROCEDURE cmdEdit.Click && Initiates an edit of the current record WITH THISFORM SELECT ( .MainTable ) .BeforeAdd = RECNO() CURSORSETPROP( [Buffering], 3 ) .Inputs ( .T. ) .Adding = .F. ENDWITH ENDPROC PROCEDURE cmdDelete.Click && Deletes the current record WITH THISFORM IF MESSAGEBOX( [Delete this record?], 292, _VFP.Caption ) = 6 oDataTier.DeleteRecord ( .MainTable, .KeyField ) DELETE NEXT 1 GO TOP .Refresh ENDIF ENDWITH ENDPROC PROCEDURE cmdSave.Click * Saves data in local cursor and then remotely (if not DBF) WITH THISFORM SELECT ( .MainTable ) TABLEUPDATE(.T.) CURSORSETPROP( [Buffering], 1 ) .Inputs ( .F. ) oDataTier.SaveRecord( .MainTable, .KeyField, .Adding ) ENDWITH ENDPROC PROCEDURE cmdCancel.Click && Cancels an Edit or Add WITH THISFORM SELECT ( .MainTable ) TABLEREVERT(.T.) CURSORSETPROP( [Buffering], 1 ) .Inputs ( .F. ) IF BETWEEN ( .BeforeAdd, 1, RECCOUNT() ) GO ( .BeforeAdd ) ENDIF .Refresh ENDWITH ENDPROC PROCEDURE cmdFind.Click * If they're using DBF and no search form is defined, use BROWSE WITH THISFORM IF EMPTY ( .SearchForm ) AND oDataTier,AccessMethod = [DBF] SELECT ( .MainTable ) .BeforeAdd = RECNO() ON KEY LABEL ENTER KEYBOARD CHR(23) ON KEY LABEL RIGHTCLICK KEYBOARD CHR(23) BROWSE NOAPPEND NOEDIT NODELETE ON KEY LABEL ENTER ON KEY LABEL RIGHTCLICK IF LASTKEY() = 27 IF BETWEEN ( .BeforeAdd, 1, RECCOUNT() ) GO ( .Beforeadd ) ENDIF ENDIF ELSE DO FORM ( .SearchForm ) TO RetVal IF NOT EMPTY ( RetVal ) oDataTier.GetOneRecord ( .MainTable, .KeyField, RetVal ) .Refresh .Buttons ( .T. ) ENDIF ENDIF ENDWITH ENDPROC PROCEDURE cmdClose.Click && Da Svidaniya THISFORM.Release ENDPROC
How to Use the FlatFileForm Template
To use this template, you do the following eight steps:
Set Field Mappings to use Pinter.VCX and the appropriate controls (MyText, MyChk, and so on) for the data types you use in your tables.
Type the following in the command window:
Open the form, add the DBF that's the MainTable to the Data Environment, drag and drop the fields to the form, and remove the table from the Data Environment.
Click on Tab Order and pick Rows, and then move the cmdAdd and cmdEdit objects to the top of the Tab Order list.
Set the form's MainTable and KeyField properties to the table name and key field name, respectively.
Remove any fields that you don't want users to enter or edit, like CreateDate or RecordID. (If you prefer, you can browse your SCX, find the controls you want to make noneditable, and change the value in the Class field of the SCX to noedit, which is a disabled TextBox control in pinter.vcx. Because it doesn't appear in the InputFields property list of editable fields, it never gets enabled.)
If you want the KeyField value to appear on the screen, add a label somewhere on the form, and add this code in the form's Refresh method:
Add a DO FORM "name" bar to the menu. Recompile and run it, and it ought to work, except for the search form, which is next.
CREATE FORM "name" AS FlatFileForm FROM Pinter;
THISFORM.txtKeyField.Caption = ; TRANSFORM ( EVALUATE ( THISFORM.MainTable + [.] ; + THISFORM.KeyField ))
I've followed my instructions and built the little Customer form shown in Figure 3.2 in about 90 seconds.
That's a pretty simple form template, and you might wonder if it really does everything you need. It does if you include the data tier and the search form.
Figure 3.2 A sample customer form based on the FlatFileForm class.