Chapter 6 : Screen Builder
Microsoft FoxPro Support Engineers
Microsoft Corporation
Created: December 15, 1993
Abstract
This section discusses Screens and the Screen Builder and contains the following topics:
·	Introduction
·	Quick Screens
·	Screen Objects
·	Screen Sets
·	Generator Directives
·	Preprocessor Directives
·	Additional Information
Introduction
The FoxPro Screen Builder is the best tool to use in creating custom applications. The screen builder is versatile and has limitless possibilities.
To help create screens right the first time, here are some helpful hints:
·	The Screen Builder does not have scroll bar capabilities and can only access fields that fit on one screen.
·	A screen cannot be re-sized while there is an active READ associated with it.
Quick Screens
The FoxPro screen builder is designed to allow maximum flexibility in creating data input screens. Data is input via GET objects. The fastest way to create an input screen is by selecting the Quick Screen option from the Screen menu pad. The Quick Screen dialog gives you the ability to:
·	Place all or selected fields from your database onto the screen in either column or row order.
·	Place titles with the fields. FoxPro uses the field name as the title. Titles are text objects and can be moved and edited.
·	Add the database name or alias to the fields.
·	Replace the field name with a memory variable.
After the fields are placed on the screen, you have the opportunity to format them. By double-clicking on the field, the Field dialog appears, and you can access the field level clauses for editing.
Screen Objects
The screen builder allows you to create popups and list boxes and other objects with relative ease. This section will discuss some of the more popular screen objects as well as some new options in FoxPro for Windows:
·	SAY and GET fields
·	Push Buttons
·	Edit Regions
·	List Boxes
·	Lines, Boxes and Circle Objects
·	Screen Picture Objects
IMPORTANT:     Naming an object is critical to your application. Any object that requires a variable name (i.e. Push Buttons, Radio Buttons, Check Boxes, Popups, List Boxes and Spinners) should never be given a database field name. Doing this can cause unpredictable results, such as blank selections in list boxes or error messages.
SAY and GET Fields
By default, all fields are placed on the screen as GETS or input fields. They may be changed to SAY or output fields to prevent editing. Other field options include:
·	Select Field on Entry. This highlights or “selects” the whole field for editing.
·	Initially disable. Fields can be initially disabled. To re-enable the field you can issue a SHOW GET <field name> ENABLE.
·	Several formatting options are available. These can be found on pages L3-23,24,33-35 of the Language Reference. For example, a logical field can be formatted as a "Y" or "N" rather than the default "T" or "F" by placing a "Y" (without the quotes) in the format box.
GET Color
The color of an individual GET can be changed by using the pen and fill colors from the Object menu pad.
GET colors can also be changed programmatically by adding the following Message clause to the object:
" " Color <color pair list>
This will allow you to specify the color of the GET as well as the object's border, etc. The quotes are necessary to satisfy the message clause. This needs to be entered as an Expression, not a Procedure.
NOTE:     If using this method of changing the color on the Message clause, the pen and/or fill color must not be modified from the menu. This will cause two COLOR clauses to be generated for the GET and an error message will occur.
		
In the above example, the color pairs have been set to a blank color. Any color can be used. Changing the color on a GET is quick and easy way to create a password field on a login screen.
Playing Sounds on @...SAY Fields
Sound files (.WAV) can also be included in screens via placement in a general fields. They can be played from an application by either double-clicking on the .WAV icon, or by using the VERB command.
The command verb can be either a numeric expression <expN> or a character expression <expC>. Command verb numbers start at 0 and are available for each installed object linking and embedding (OLE) server application. This is determined by the OLE server application, not FoxPro.
To play a file from within FoxPro, add it to an expression as:
Myole verb 2
-or-
myole VERB "PLAY"
This can also be used as an @...SAY. For example:
@ 3,3 SAY <General Field> VERB "PLAY".

Push Buttons
Push buttons are one of the most versatile and popular objects on a screen. The three types of push buttons - Normal, Invisible and Picture - give you a wide range of options for different types of interface.
TIP:     Push button colors are controlled by Windows. You can, however, change the color of a push button within FoxPro by creating a solid colored .BMP and attach it to a push button using the Picture option in the push button dialog box.
Push buttons (like radio buttons and check boxes) have an option to use a picture instead of the FoxPro generated graphic. The picture must be in .BMP format, and readable by Windows Paintbrush. 
When FoxPro for Windows loads a .BMP file for a picture button it checks the directory for a corresponding .MSK or “mask” file. FoxPro displays the portions of the .BMP file that correspond to black areas of the .MSK file and makes transparent the portions of the .BMP file that correspond to white areas of the .MSK file. If no .MSK file is found, all white areas of the .BMP file are made transparent.
To create a FoxPro for Windows .MSK file for a .BMP file:
1.	Open the .BMP file in Microsoft Windows Paintbrush or another bitmap-editing utility.
2.	Make the areas of the bitmap that you want to see on the button BLACK. If there are any white areas on the bitmap that should be displayed as white on the button, make them BLACK also. Make the remainder of the .MSK file white
			
3.	Save the file as a monochrome bitmap. Change the file's extension to .MSK.
4.	Save the .MSK file in the same directory as the .BMP file.
Edit Region
Some clauses for @...GETs are not available through the Screen Builder dialog boxes. Some of these can be placed on the Message clause. For example, to create a scrolling Edit Region that cannot be modified, place the NOMODIFY clause on the Message clause as an Expression.
		
List Box
You can use FoxPro to create a multiple select list box. There are several different methods of creating a multiple-selection list object.
The following example demonstrates how to make selections in an array. (In the example, the array is called "test".) The selections are marked by a check mark (CHR(251)), and then the selection is displayed when the Done button is chosen. The VALID routine for the push button sorts the array and stores the selected options in a variable. All options that were selected are stored in the "checked" variable.
CLOSE DATABASE
CLEAR

USE tutorial\customer
*** Create Array test to store the field 'cno' from database
COPY TO ARRAY test FIELDS cno
*** Store a Space as the first character of every element of the array.
FOR i = 1 TO ALEN(test)
   test(i) = " " + test(i)
ENDFOR
*** Initialize variables
selected = 0       && Variable to store number of selected items
checked = " "      && Variable to store selected items

DEFINE WINDOW test FROM 0,0 TO 17,20 TITLE "Test Application"
MOVE WINDOW test CENTER
ACTIVATE WINDOW test
@ 2,2 GET choice FROM test DEFAULT 1 SIZE 10,10 ;
   VALID myval()
@ 14,2 GET button PICTURE "@* Done" VALID done() DEFAULT 1
READ CYCLE
RELEASE WINDOW test

*** VALID procedure for list box
PROCEDURE myval
   *** Test to see if item has been selected by checking first character
   IF SUBSTR(test(choice),1,1) = " "
      *** Store check-mark as first character of array element when item is selected.
      test(choice) = CHR(251) + SUBSTR(test(choice),2)
      *** Increment number of items selected
      selected = selected + 1
   ELSE
      *** Store a space as first character of array element to unselect item.
      test(choice) = " " + SUBSTR(test(choice),2,LEN(test(choice))-1)
      *** Decrement number if items selected
      selected = selected - 1
   ENDIF
   *** Reset current object to list box
   _CUROBJ = OBJNUM(choice)

*** VALID procedure for Done push button
PROCEDURE done
   *** Sort selected items to top if array
   result = ASORT(test,1,ALEN(test),1)
   *** Store selected items to variable 'checked'
   FOR i = 1 to selected
   *** Substitute any desired code to store selected options
       checked = SUBSTR(test(i),2) + " " + checked
   ENDFOR
   RELEASE temp
   *** Display selected options
   WAIT WINDOW "You selected " + checked
   CLEAR READ
FoxPro uses list boxes extensively. One of the most popular uses of list boxes is the dual list box, where information from one list box is placed in a second list. There is an excellent example of this procedure in ADDUSER.SCX, found in the C:\FOXPROW \SAMPLE\ORGANIZE\SCREENS directory.
Line, Box and Circle Objects
The screen builder will allow you to easily create visual amenities to a screen. It comes complete with Line, Box, and Circle draw features to create screens that have an eye catching effect and are more aesthetically pleasing 
Creating "3 D" effects is done by using the Line Draw tool to create a box around the screen, then selecting the lines and changing their colors to make contrast with the screen color. Color and thickness is maintained under Object, Pen and Object, Pen Color.
	
Screen Picture Objects
The Screen Builder provides the opportunity to use pictures on screen. There are a few things to keep in mind when using pictures:
·	The file or picture must be in bitmap (.BMP) format.
·	You must be able to open the .BMP under Windows Paintbrush application. If Paintbrush cannot read the file, FoxPro will not be able to read the file.
·	Always use bitmaps that are at the lowest common denominator. For example, your user may not have the ability to run 256 colors, so you will need to use a 16 color .BMP.
The Screen Picture object button contains two push buttons:
·	File - allows a selected file to be displayed.
·	Field - allows a general field to be displayed or played as an OLE object.
In addition to using a general field, the Field option also allows you to use a variable that contains a .BMP name. Since this button is intended for OLE objects, you must include the word "BITMAP" after the variable name. For example:
		
Without the word "BITMAP" added, the contents of the variable "mybmp" will be displayed on the screen.
If a bitmap is placed as a Screen Picture object, it will be static and cannot be changed. It is possible, however, to edit the object if it is in a general field by doing the following:
1.	On a Screen Picture object, insert the picture by selecting Field as the option and choosing the general field that contains the picture.
2.	Place an Push Button on the top-left corner of the picture.
3.	In the Push Button dialog box, select Invisible button and give the button a name in the Variable prompt.
4.	Select the Valid Clause button, and in the code snippet window, type:

MODIFY GENERAL <gen field name>
5.	Choose the OK button twice to return to the Screen Builder.
6.	Re-size the button so that it covers the whole picture.
Now, when you run the screen, you can double click on the picture and it will allow you to modify the general field. If you double click again, Paintbrush will run and allow you to change the actual picture.
Screen Sets
Sometimes, data input requires more than one screen. In FoxPro, you can create multiple screens and generate them together as a "Screen Set". Here are the steps to accomplish that:
1.	Create the first screen, save and close it.
2.	Create the second screen, save and close it as well.
3.	Open the first screen (<<FILENAME.SCX>>) and Select Program, Generate. From the Generate Screen dialog, select the Add option and add the second screen.
4.	Select the Arrange push-button and arrange the screens so that the first screen covers the second screen. This will give a seamless look to your application.
5.	Generate the screens. This creates a "Screen Set".
Tips about Screen Sets:
·	Screen sets operate under one READ. This can save READ levels for other options in your application. Remember, you only have five available READ levels in FoxPro.
·	A Screen set will activate all of the windows created when the .SPR is run. That means all windows will be displayed one by one as the screen set is painted on the screen.
·	Screen Sets allow for easier movement between screens. To move between screens, you can:
1.	Create a push-button that changes the value of _CUROBJ to a particular get on a following screen.
2.	Tab past the last get on the first screen to select the first get on the next screen.
3.	Use the PageUp and PageDown keys.
Integrating a BROWSE Screen with a @...GET Screen
A screen design that is popular is the inclusion of a BROWSE window into a data entry screen. In order to accomplish this, three screens must be created.
IMPORTANT:     A GET (parent) window and a BROWSE (child) window cannot cohabitate on the desktop. The GET window will capture FoxPro’s focus because of the READ in effect and it will be impossible to toggle the two windows. The code sample below gives an example of how to create a large, blank parent window to hold the GET and the BROWSE windows.
The procedure below describes how to coordinate a parent data window with a child BROWSE window using the power tools. The screen and its controls are designed in such a way that as you move through the parent database, both the parent and child tables update. In this example, the CUSTOMER and INVOICES databases are used to establish a one-to-many relationship. Both the CUSTOMER and INVOICES databases are located in the FoxPro TUTORIAL subdirectory.
1.	Use the View window to set up a one-to-many relationship between the CUSTOMER (parent) database and the INVOICES (child) database. Select the parent database by clicking it.
2.	Create a quick screen.
3.	Save the environment and add the following code into the following code options in the Screen Layout dialog box:
Setup Code
DEFINE WINDOW child FROM 17,1 to 24,70 PANEL;
FLOAT GROW
ACTIVATE WINDOW child
SELECT INVOICES
BROWSE IN WINDOW child NOWAIT SAVE NOMODIFY LAST
SELECT CUSTOMER
Cleanup Code
RELEASE WINDOW child
Activate Code
SELECT CUSTOMER
4.	Resize the window so that it fits around the fields without leaving a lot of empty space.
5.	Save the screen as TEST.SCX and then close it.
6.	From the File menu, choose Open, and set the file type to Screen.
NOTE:     Before proceeding with this next step, make a backup of the CONTROL1.SCX and CONTROL1.SCT files
7.	Select CONTROL1.SCX (located in your FoxPro SAMPLE\ORGANIZE\SCREENS subdirectory) and then choose Open.
8.	Double-click one of the push buttons on the screen and select the Valid code snippet check box.
9.	The Valid code snippet is shown below. Modify the code by adding the two designated lines.

SET SKIP TO          && Add this line
DO CASE
CASE act2 = 1
GO TOP
CASE act2 = 2
SKIP -1
     IF BOF()
        GO TOP
     ENDIF
CASE act2 = 3
SKIP 1
     IF EOF()
       GO BOTTOM
     ENDIF
CASE act2 = 4
GO BOTTOM
CASE act2 = 5
APPEND BLANK
CASE act2 = 6
CLEAR READ
ENDCASE

SET SKIP TO invoices     && This line is optional.

SHOW GETS
RETURN 0
10.	Save the screen as MYCONTRL.SCX.
11.	From the File menu, choose Open, and then set the type to Screen.
12.	Select TEST.SCX and then choose Open. 
13.	From the Program menu, choose Generate.
14.	Choose Add. 
15.	Select MYCONTRL.SCX and then choose OK.
16.	Choose Arrange and arrange the two screens to be toward the top of the window. Try not to overlap the screens.
17.	Choose OK. 
18.	Choose Generate. 
19.	In the Command window, issue the following command:

DO test.spr
Notice the three screens come up. Choosing Next from the control screen displays the updated parent screen. The related records of the child in the browse are also updated.
Avoiding “Too Many READs in Effect” Error
Many developers are now writing applications that require many screens. A good way to move between screens is through the use of a push-button.
Valid Clause of Push-button
_CUROBJ = OBJNUM(<First field on second screen>)
A frequent way of changing screens when each screen is its own .SPR is the following:
Valid Clause of Push-button:
DO screen.spr
This works, but it will advance the READ level, and after five READs, the error "Too Many READs in Effect" will appear. There is a way to escape this trap, by setting the screen name as a variable, and using a named expression to activate the screen, while clearing the READ. Below is an example
Setup Code
todo = " "	& Initializes the variable 
Valid Clause of Push-button
todo = "screenname.spr" 
CLEAR READ
Cleanup Code
IF todo= " "
   RETURN
ELSE
   DO (todo)	& Activates the new screen
ENDIF
This strategy allows for the user to go from .SPR to .SPR without ever advancing the read level.
Generator Directives
Generator Directives are commands that communicate solely with GENSRN.PRG, FoxPro screen program generator. They do not appear in generated screen program code. Generator directives must be placed in the Setup code for a screen and only one directive of each type can be included in the code.
SECTION 1|2
These directives allow you to split the setup code for a screen into 2 sections. Setup Code Section 1 is generated at the beginning of the .SPR program. Section 2 is generated after the DEFINE WINDOW commands and before the Screen Layout commands.
An example would be to split the setup code if you were using a PARAMETER statement or an ON ERROR statement in the screen program. Below is an example using the #Section command:
#SECTION 1
PARAMETER x,y,z
<other commands>
#SECTION 2
<other commands>
#READCLAUSES
This allows the user to specify clauses not available through the Screen Builder to be placed at the end of a READ command. Typical clauses are TIMEOUT, SAVE, OBJECT, NOMOUSE, and COLOR.
#ITSETEXPRESSION
Allows you to specify a single character that can be used to indicate picture clauses, window titles and window footers as expressions rather than literal strings. The example below uses the #ITSETEXPRESSION clause in conjunction with the #SECTION 1. This is necessary because #ITSETEXPRESSION is applicable to windows defined in the screen layout section of the Screen Builder.
ITSETEXPRESSION ~
SECTION 1
varname = "Fred"
*window title can be defined as <varname>. When the window is displayed, Fred 
*appears as the window title.
#WNAME <string>
This directive allows you to substitute the current window name for <string> wherever it occurs in code. This allows for the use of generic code which is independent of the unique window name generated by GENSCRN.
Here's an example:
Setup Code:
#WNAMEfred
In the ACTIVATE clause in the window:
IF WONTOP('fred')
	<statements>
ELSE
	<statements>
ENDIF
A unique name is substituted for 'fred’. It's not necessary to know the unique name. The following code will appear in the generated screen program (.SPR)
IF WONTOP('_PV60IAGH') 		&unique named window
	<statements>
ELSE
	<statements>
ENDIF
#WCLAUSES <clauses>
The #WCLAUSES screen generator directive lets you add DEFINE WINDOW clauses. The #WCLAUSES directive must be in the screen setup snippet of the screen whose window definition you want to modify. #WCLAUSES has no effect if you do not check the Define Windows check box in the Generate Screen dialog. Note that the setup snippet can contain only one #WCLAUSES directive. If more than one #WCLAUSES directive is included in the screen setup snippet, FoxPro uses only the first one.
#REDEFINE
By default, GENSCRN generates code to check for the existence of a window before defining it. #REDEFINE allows you to suppress the commands that check for that existence and trick FoxPro into automatically redefining the window.
#INSERT <file>
The #INSERT menu and screen generator directive inserts the contents of a text file into generated menu or screen code. Use #INSERT to include a file in a generated menu or screen program. For example, use #INSERT to place #DEFINE ... #UNDEF statements from a file in the beginning of a generated menu or screen program. This directive can appear anywhere in any snippet; it does not have to be in the setup code snippet.
The file must be a text file. The generator searches for the file in the current directory, in the FoxPro directory and subdirectories and along the MS-DOS path. If the file cannot be found, the screen generator comments your code - indicating that the file could not be found - and then continues the generation process. This file cannot include other screen generator directives.
#NAME <snippet name>
This screen and menu generator directive names a snippet produced by clauses such as SHOW, WHEN and VALID, whether the snippet is produced at the object or READ level. The #NAME directive must appear at the top of a snippet, before any code statements. Blank lines or comments can precede the #NAME directive. The snippet name must conform to the rules for naming procedures and functions. Remember, FoxPro does not check for duplicate names.
#NOREAD
Include the #NOREAD screen generator directive in a screen setup snippet to prevent the generation of the READ command. When #NOREAD is included in a screen setup snippet, controls are displayed but are not activated when you execute the screen code.
#NOREAD PLAIN
This screen generator directive works much the same way that #NOREAD does except that it suppresses everything except @ ... SAY, @ ... GET and @ ... EDIT, comments and #REGION statements.
Preprocessor Directives
Preprocessor directives are designed to include source code at compile time.
#IF ... #ENDIF
Conditionally includes source code at compile time.
#IF <expN1> | <expL1>
	<statements>
[#ELIF <expN2> | #ELIF <expL2>
	<statements>
	 ... 
#ELIF <expNn> | #ELIF <expLn>
	<statements>]
[#ELSE
	<statements>]
#ENDIF
Remarks
#IF ...  #ENDIF can improve the readability of source code, reduce compiled program size and improve performance in some cases. When the #IF ... #ENDIF structure is compiled, successive logical or numeric expressions within the structure are evaluated; the evaluation results determine which set of FoxPro statements (if any) are included in the compiled code.  
Clauses

#IF <expN1> | <expL1>
	<statements>
The numeric expression <expN1> or logical expression <expL1> is evaluated. If the expression is numeric and non-zero or the expression is logical and true (.T.), the statements immediately following #IF are included in the compiled code. The #IF ... #ENDIF structure is exited, and the first program line following #ENDIF is then compiled. If the expression following #IF is numeric and 0 or logical and false (.F.), the statements immediately following #IF are not included in the compiled code. In this case, any following #ELIF directives are evaluated. 
CAUTION:     Do not use system memory variables for <expN1> or <expL1>. System memory variables are not evaluated until run time.

[#ELIF <expN2> | #ELIF <expL2>
	<statements>
#ELIF <expNn> | #ELIF<expLn>
	<statements>]
If <expN1> is 0 or <expL1> is false, the #ELIF directives are then evaluated. The first #ELIF expression <expN2> or <expL2>, if present, is evaluated. If <expN2> is non-zero or <expL2> is true, the statements immediately following #ELIF are included in the compiled code. The #IF ... #ENDIF structure is exited, and the first program line following #ENDIF is then compiled. If <expN2> is 0 or <expL2> is false, the statements following #ELIF are not included in the compiled code, and the next #ELIF directive is evaluated. 
[#ELSE
	<statements>]
If no #ELIF directives are included or if those that are included evaluate to 0 or false, the presence or absence of #ELSE determines whether any additional statements are included in the compiled code:
If #ELSE is included, the statements following #ELSE are included in the compiled code.
If #ELSE isn't included, none of the program statements between #IF and #ENDIF is included in the compiled code. The #IF ... #ENDIF structure is exited, and compilation continues on the first program line following #ENDIF.
#DEFINE ... #UNDEF
Creates and releases compile-time constants.
Syntax:
#DEFINE <constant name> <expr>
#UNDEF <constant name>
Remarks
The #DEFINE and #UNDEF preprocessor directives are used to create compile-time constants in programs. By creating constants with #DEFINE instead of using system memory variables, you can reduce memory consumption, increase performance and simplify programs. To create a constant with #DEFINE, specify the constant's name with <constant name> and its value with <expr>. When the program is compiled, text substitution is performed and the constant value expression is substituted for the constant name wherever it appears in the program. You can stop the substitution for the constant by issuing #UNDEF.
Substitution occurs only in program lines that follow the #DEFINE statement that creates the constant and that precede the #UNDEF statement for that constant. The constant is available only to the program that creates the constant.
Clauses

<constant name>
To create a compile-time constant with #DEFINE, specify its name with <constant name>. The constant name must be a legitimate FoxPro name that begins with a letter or an underscore and consists of up to 10 letters, digits or underscores. To improve program readability and simplify debugging, use a standard naming convention for your constant names. 
CAUTION:     Do not use FoxPro key words for constant names.
To stop text substitution for a constant created with #DEFINE, issue #UNDEF <constant name>.
<expr>
Specify the value of the compile-time constant with <expr>. <expr> can be a name or an expression that evaluates to a character, numeric, date or logical value. Caution  Do not use system memory variables for <expr>. System memory variables are not evaluated until run time.
Additional Information
Additional information concerning the use of Screens and the Screen Builder tool can be found by looking at the following articles:

Q96174	Q103583		
Q96290	Q104005		
Q96358	Q104054		
Q99092	Q106489		
Q100558	Q106696