Applications in character mode (VIO mode) Foundation
This section describes the most important commands, functions, and dialog concepts used in programming VIO applications. With only a few exceptions, all relevant language elements of Xbase++ are compatible with Clipper. Programmers familiar with Clipper can just read the "Keyboard and mouse" and "The default Get system" sections of this chapter. Note that the functionality of a VIO application is guaranteed in hybrid mode as well as in GUI mode.
The simplest form of data input and output is unformatted. Data is input or output at the current position of the screen cursor or print head. Xbase++ provides a set of commands for unformatted input and output. These are listed in the following table:
Command | Description |
---|---|
? | ?? | Output the result of one or more expressions |
ACCEPT | Input characters at the current cursor position |
DISPLAY | Output the contents of a database file |
INPUT | Input an expression at the current cursor position |
LIST | Output the contents of a database file |
SET ALTERNATE | Turn output to a file on or off |
SET COLOR | Set the screen color |
SET CONSOLE | Turn screen output on or off |
SET PRINTER | Turn printer output on or off |
TEXT...ENDTEXT | Output one or more lines of text |
TYPE | Output the contents of any file |
WAIT | Input a single character |
The three commands ACCEPT, INPUT and WAIT provide unformatted input. WAIT accepts a single keystroke while ACCEPT and INPUT allow any number of characters to be entered. Input via ACCEPT and INPUT is ended when the user presses the Enter key. Characters entered using INPUT are considered as an expression and are compiled using the macro operator (an error in the expression leads to a runtime error). Characters entered using the ACCEPT command remain unchanged and can be assigned to a memory variable as a character string.
The most commonly used commands for unformatted output are the single and the double question marks (? or ??). These are equivalent to the functions QOut() and QQOut(), respectively. The results of one or more expressions can be output using these commands. The default output device is the screen. The results of the expressions can also be saved in a file (after SET ALTERNATE ON) or sent to a printer (after SET PRINTER ON). If the command SET CONSOLE OFF is called before output, screen output is suppressed. After screen output has been suppressed, the screen output must be reactivated using SET CONSOLE ON after output to the file and/or printer is complete.
The commands LIST and DISPLAY are both used to output records from a database file. The command TYPE outputs the contents of any text file. The options TO PRINTER and TO FILE are valid with all three of these commands, so simultaneous output to a printer or a file can be performed without calling SET PRINTER ON or SET ALTERNATE ON. The screen output of these commands can be suppressed by first calling SET CONSOLE OFF.
The command SET COLOR changes the color for the display of screen output. The command is not really an output command, but allows the color of the output to be modified.
Detailed descriptions of the commands for unformatted input and output, including program examples, are found in the reference documentation.
Formatted input and output allows the exact position on the screen or printer (the row and column) for data input or output to be specified. Xbase++ includes commands and functions for formatted input and output. The commands are translated by the preprocessor to the equivalent function, which means that the difference between commands and functions is just a difference in syntax. The command syntax sometimes allow more readable program code, since many commands imply several function calls and the command syntax provides additional keywords. The following table lists the most important functions and commands for formatted input and output:
Command/function | Description |
---|---|
@ | Position screen cursor and delete screen line |
@...BOX | Output box on the screen |
@...CLEAR | Delete screen area |
@...GET | Input data |
@...SAY | Output data |
@...TO | Output box on the screen |
CLEAR | Delete entire screen |
Col() | Return column position of screen cursor |
DispOut() | Output the result of an expression, and update cursor position |
DispOutAt() | Like DispOut() but cursor position not updated |
DevOut() | Output expression results on the current output device |
Row() | Return row position of screen cursor |
MaxCol() | Return maximum number of columns on the screen |
MaxRow() | Return maximum number of rows on the screen |
PCol() | Return current column position of the print head |
PRow() | Return current row position of the print head |
SaveScreen() | Save screen area |
SET DEVICE | Specify current output device |
SetColor() | Set or return screen color |
SetPos() | Change screen cursor position |
SetPrc() | Set row and column coordinates of the print head |
RestScreen() | Redisplay a saved screen area |
Some of these functions and commands affect only the position of the screen cursor but are included because the cursor coordinates mark the position where data is displayed. Other functions and commands manage the screen itself. In VIO mode, the origin (0, 0) of the coordinate system is the upper left corner of the screen or window. The lower right corner (MaxRow(), MaxCol()) represents the largest coordinate values that are visible. The position of the cursor is set by specifying the Row() and Col() to either the command @ or the function SetPos(). The screen is generally cleared using CLEAR at the start of each program prior to displaying anything for the first time. The commands @...BOX and @...TO draw boxes on the screen. They are only valid for the screen and are not available for the printer.
The most important command for formatted output is @...SAY which outputs the result of an expression. Output using @...SAY can occur on the screen or on the printer. Formatted output differs from unformatted output in that simultaneous output on the screen and printer is not possible. Selecting an output device is done using the command SET DEVICE (TO PRINTER or TO SCREEN). Output on the printer is at the current position of the print head which can be set using the functions PRow() and PCol(). The function SetPrc() resets the internal values for the row and column coordinates of the print head but does not reposition the print head.
The command @...SAY can be expanded to include data input using the GET option. Alternatively, an input field can be defined using the command @...GET. These commands are used to define one or more data entry fields prior to the actual data input which occurs within the READ command or the function ReadModal(). READ and ReadModal() both activate the default Get system of Xbase++ (see the later section on this).
Under an operating system with graphic user interface, the mouse (not the keyboard) is the most important input device for controlling the application. Because of this, running programs or individual modules is not controlled by program logic but by "events". These events are usually caused by actions of the user. Events, which come from outside the application, are temporarily stored by the operating system in an event queue and then sequentially processed by the application. Some examples of events would be: the mouse was moved, the right mouse button was clicked, the left mouse button was double clicked, etc. A keypress is also an event, which shows that events can come from various devices.
Each event is identified within the program by a numeric code. Each key has an associated unique numeric value and different mouse events have different numeric codes. In Xbase++, events are all handled by a group of event functions, regardless of their origin. There is also a group of functions and commands which assure language compatibility with Clipper. These compatibility functions and commands can only respond to the keyboard and they only consider the keyboard codes defined in Clipper. It should be noted that the numeric values associated with the various keys in Clipper do not necessarily match the event codes of the operating system or Xbase++ and the old keycodes are offered only for compatibility. A correlation is found between key codes and ASCII characters only in the range of 0 to 255. These compatibility functions and commands only consider the key codes defined in Clipper. The distinction between "event functions" and "keyboard functions and commands" is also shown in the following tables:
Function | Description |
---|---|
AppEvent() | Read event and remove it from the queue |
LastAppEvent() | Return the last event |
NextAppEvent() | Read next event without removing it from queue |
PostAppEvent() | Put event into the queue |
SetAppEvent() | Associate event with a code block |
SetMouse() | Toggle availability of mouse events on or off |
Function / Command | Description |
---|---|
Inkey() | Read key code |
KEYBOARD | Write characters into keyboard buffer |
LastKey() | Return last key code |
NextKey() | Read next key code |
SetKey() | Associate key code with a code block |
A detailed description of the compatibility functions can be found in the reference documentation.
AppEvent() reads events from the queue and also removes them from the queue. The return value of AppEvent() is the numeric code that uniquely identifies the event. The function SetMouse(.T.) must have been previously called for the AppEvent() function to register mouse events in VIO mode. The following program example is terminated when the right mouse button is pressed:
#include "Appevent.ch"
PROCEDURE Main
LOCAL nEvent := 0
CLEAR
@ 0,0 SAY "Press right mouse button to terminate"
SetMouse(.T.) // register mouse events
DO WHILE nEvent <> xbeM_RbDown // event: right mouse button
nEvent := AppEvent(,,,0) // wait until event occurs
IF nEvent < xbeB_Event // event: keypress
? "The event code for the key is:", nEvent
ELSE
? "The event code for the mouse is:" , nEvent
ENDIF
ENDDO
RETURN
The large number of possible events makes it impractical to directly program events using the numeric codes. Instead, the constants defined in the #include file APPEVENT.CH should be used. These constants all begin with the prefix xbe (which stands for xbase event) followed by an uppercase letter or an underscore. The uppercase letter identifies the category for the event and the underscore separates the prefix from the rest of the descriptive event name:
Category | Prefix | Example |
---|---|---|
No event | xbe_ | xbe_None |
Keyboard event | xbeK_ | xbeK_RETURN |
Base event | xbeB_ | xbeB_Event |
Mouse event | xbeM_ | xbeM_LbDown |
Xbase Part event | xbeP_ | xbeP_Activate |
In addition to the return value identifying the event, the function AppEvent() modifies two parameters that are passed by reference. These are called "message parameters" and contain additional information about the event. In an event driven system such as Xbase++, it is often insufficient to receive only a single event code. For example, it is generally necessary to know the position of the mouse pointer if the event is "mouse click". When the function AppEvent() is called, two parameters must be passed by reference to contain additional information about the event after AppEvent() returns. If the event is a mouse click, the coordinates of the mouse pointer are contained in the first message parameter as an array of two elements. The following example illustrates this:
#include "Appevent.ch"
PROCEDURE Main
LOCAL nEvent := 0, mp1, nRow, nCol
CLEAR
@ 0,0 SAY "Press right mouse button to cancel"
SetMouse(.T.) // register mouse event
DO WHILE nEvent <> xbeM_RbDown // event: right mouse button
nEvent := AppEvent(@mp1,,,0) // wait until event
// occurs
IF nEvent < xbeB_Event // event: keypress
? "Event code:", nEvent
ELSEIF nEvent <> xbeM_Motion // mouse events exept
// 'mouse moved'
nRow := mp1[1] // mouse coordinates
nCol := mp1[2]
@ nRow, nCol SAY "The event code is:" + Str(nEvent)
ENDIF
ENDDO
RETURN
The variable mp1 is passed by reference to the function AppEvent(). After each mouse event it contains the row and column coordinates of the mouse pointer in an array of two elements. In the example, the contents of the array are assigned to the two variables nRow and nCol. The event code is output at this position, unless it is a mouse movement. The xbeM_Motion event occurs very frequently during mouse movement and would clutter the screen if displayed.
In VIO mode, the mouse coordinates are the most important information contained in the message parameters. In many other cases the parameters contain the value NIL in this operating mode but contain additional information when Xbase Parts are used (of course, Xbase Parts are not available in VIO mode) or when user-defined events are created using the function PostAppEvent(). The following example illustrates the basic relationship between the functions AppEvent() and PostAppEvent() which (along with their message parameters) create the basis for event driven programming:
#include "Appevent.ch"
#define xbeU_DrawBox xbeP_User + 1 // xbeP_User is the base value
#define xbeU_Quit xbeP_User + 2 // for User events
PROCEDURE Main
LOCAL nEvent := 0, mp1, mp2
CLEAR
@ 0,0 SAY " Draw Box | QUIT" // mouse sensitive region
SetMouse(.T.) // register mouse
DO WHILE .T. // infinite event loop
nEvent := AppEvent( @mp1, @mp2 ,,0 )
DO CASE
CASE nEvent == xbeU_DrawBox // user event
DrawBox( mp1, mp2 ) // mp1 = Date(), mp2 = Time()
// from PostAppEvent()
CASE nEvent == xbeU_Quit // second user event
QUIT
CASE nEvent < xbeB_Event // key was pressed
@ MaxRow(), 0
?? "Key code is:", nEvent
CASE nEvent == xbeM_LbClick // left mouse button click
@ MaxRow(), 0
?? "Coordinates are:", mp1[1], mp1[2]
IF mp1[1]== 0 .AND. mp1[2] <= 21 // in mouse sensitive
// region
IF mp1[2] <= 15
PostAppEvent( xbeU_DrawBox, Date(), Time() )
ELSE
PostAppEvent( xbeU_Quit )
ENDIF
ELSE
@ mp1[1], mp1[2] SAY "No selection made"
ENDIF
ENDCASE
ENDDO
RETURN
********************************* // define position and display
PROCEDURE DrawBox( dDate, cTime ) // box using mouse clicks
LOCAL nEvent := 0, mp1, nTop, nLeft, nBottom, nRight
SAVE SCREEN
@ 0, 0 SAY "Click on upper left corner of box"
DO WHILE nEvent <> xbeM_LbClick // wait for left mouse click
nEvent := AppEvent( @mp1,,, 0 )
ENDDO
nTop := mp1[1]
nLeft := mp1[2]
nEvent := 0
@ nTop, nLeft SAY "Click on lower right corner of box"
DO WHILE nEvent <> xbeM_LbClick // wait for left mouse click
nEvent := AppEvent( @mp1,,, 0 )
IF nEvent == xbeM_LbClick .AND. ;
( mp1[1] <= nTop .OR. mp1[2] <= nLeft )
Tone(1000,1) // lower right corner
nEvent := 0 // is invalid
ENDIF
ENDDO
nBottom := mp1[1]
nRight := mp1[2]
RESTORE SCREEN
@ nTop, nLeft TO nBottom, nRight // output box
@ nTop+1,nLeft+1 SAY "Date:" // display values of
?? dDate // PostAppEvent()
@ nTop+2,nLeft+1 SAY " Time:"
?? cTime
RETURN
In the example, one of the two user-defined events is generated when the mouse is clicked in the hot spot region of the first screen row. The mouse coordinates (contained in the message parameter mp1) are used to distinguish whether the xbeU_DrawBox event or the xbeU_Quit event is placed in the queue by PostAppEvent(). The user-defined event xbeU_DrawBox receives the return values of Date() and Time() as message parameters. When this event is retrieved from the queue by AppEvent(), the message parameters are placed in the variables mp1 and mp2. These values are passed to the procedure DrawBox() and displayed on the screen by this function.
The program is a simple example showing the logic of event driven programming. Events are identified by a unique numeric value (using a #define constant) and additional information about the event is contained in the two message parameters. The values contained in the two parameters mp1 and mp2 vary depending on the event. The message parameter values can range from NIL in the simplest case to complex data structures such as arrays or objects. PostAppEvent() can be used to place any event in the queue that can then be retrieved from any place in the program using AppEvent().
Xbase++ provides a Get system for formatted data input in VIO mode. The source code of this system is contained in the GETSYS.PRG file. The open architecture of the Get system offers tremendous possibilities such as data validation before, during and after data entry. It also offers a specific place where a programmer can identify and process events that occur during data input without having to change the basic language elements for defining input fields.
An input field is most easily created using the command @...SAY...GET. The data entry itself is started using the command READ:
USE Address ALIAS Addr
@ 10,10 SAY "First Name:" GET Addr->FIRSTNAME // define data
@ 12,10 SAY " Last Name:" GET Addr->LASTNAME // entry fields
READ // read input
In these four lines a database file is opened, two data entry fields are defined and input is performed via the READ command. Input is terminated when the entry in the second field is finished using the Return key or if the Esc key is pressed during input.
The command syntax is the easiest way to program data entry fields. This syntax is translated by the preprocessor into code that creates Get objects and stores them in the GetList array. Get objects include methods that allow the formatted display of values and interactive data entry. The data entry values can be contained in memory variables or in field variables. Access to the contents of data entry variables does not occur directly, but through a code block. This code block is called the data code block. The data code block is used by the Get object to read the value of a variable into the Get object's edit buffer and then to write the modified value back into the variable.
When a Get object has input focus, the user can modify the contents of the edit buffer. A Get object uses various methods to control cursor navigation and transfer characters into the edit buffer. Get objects can also perform data validation before and after data input. Rules for data validation are specified in code blocks contained in instance variables of the Get object.
The command @...GET creates a Get object that already contain a code block to access the specified variable. When Get objects are created directly using the class method Get():new(), a data code block must be specified. Get objects are stored in an array that is passed to the function ReadModal(). If the @...GET command is used, the array referencing the Get objects is contained in the variable GetList. The READ command passes the GetList array to the function ReadModal(). The ReadModal() function is the default edit routine which accesses various edit and display methods of the Get objects. The default Xbase++ Get system includes the ReadModal() function along with the other globally visible utility routines listed in the following table:
Function | Description |
---|---|
ReadModal() | Activates data input for all Get objects |
ReadExit() | Defines termination keys for data entry fields |
ReadInsert() | Toggles between insert and overwrite mode |
ReadKill() | Terminates data input for all Get objects in the current GetList array |
Function | Description |
---|---|
GetEventReader() | Get Reader for AppEvent() |
GetHandleEvent() | Allows event processing by current Get object |
GetPrevalidate() | Prevalidates before a Get object receives focus |
GetPostvalidate() | Postvalidates before a Get object loses focus |
GetDoSetkey() | Executes code block which is linked to a key or an event |
GetActive() | Sets the Get object that has the focus |
GetKillActive() | Removes focus from the active Get object |
Getlist() | Determines current GetList array |
GetlistPos() | Returns position of current Get object in the GetList array |
GetEnableEvents() | Toggles between Inkey() and AppEvent() |
GetToMousePos() | Positions cursor in entry field at the mouse pointer |
Function | Description |
---|---|
GetReader() | Get reader for Inkey() |
GetApplykey() | Allows keys to be processed by the current Get object |
Two of the functions in the GETSYS.PRG file exist in order to ensure compatibility with the Clipper Get system. In these functions, the compatibility function Inkey() is used to retrieve keyboard entry. This is not compatible with the AppEvent() function which is used to read Xbase++ events. To make it easier to port existing Clipper applications to Xbase++, the compatibility functions are used by default in VIO mode. This means that the Get system reads only the keyboard using Inkey() and does not process any events. In this default mode, the Get system is completely compatible with Clipper but the mouse is not available.
The default setting is switched when SetMouse(.T.) is called before the first READ command or the first call to ReadModal(). Events in the Get system are then processed using the functions GetEventReader() and GetHandleEvent() instead of the functions GetReader() and GetApplykey(). In this setting the mouse is available. The function SetAppEvent() must be used instead of SetKey() to associate keystrokes with code blocks. The function GetEnableEvents( .T. | .F.) can also be used to switch between the compatibility mode and the event driven mode of the Xbase++ Get system.
The service functions of the Get system are rarely needed. The @...SAY...GET command is sufficient to allow complex data entry screens to be easily programmed. A detailed knowledge of the additional Get system functions is required when the default Get system is modified to meet unique requirements.
The source code of the Xbase++ Get system is contained in the file GETSYS.PRG and can be modified in just about any way. Changes should not be performed in the file itself but in a copy of the file. Before substantial changes to GETSYS.PRG are performed, other ways to modify the default behavior to meet individual requirements should be explored. For example, the instance variable oGet:reader contains a code block that calls a user-defined Get reader that can be used to provide special features. The Get reader code block must accept one parameter (the Get object) which must be passed as the first parameter to the function or procedure that implements the Get reader. The basic approach to implementing a special Get reader is shown in the following example. This code uses the compatibility function Inkey() to read keyboard input. The purpose of the example reader is to handle German language umlauts and "ß" entered as characters in the entry field. These characters are translated into their two character equivalents in the user-defined reader NoUmlauts() :
#include "Get.ch"
**************
PROCEDURE Main
LOCAL cName1 := Space(20), cName2 := Space(20)
@ 8,10 SAY "Input without umlauts and ß"
@ 10,10 SAY "First Name:" GET cName1 ; // define Get Reader
SEND reader := {|oGet| NoUmlauts(oGet) }
@ 12,10 SAY " Last Name:" GET cName2 ;
SEND reader := {|oGet| NoUmlauts(oGet) }
READ
RETURN
The definition of these input fields occurred using the command syntax. The code block which assigns the user-defined Get reader is specified using the SEND option. In it the reader code block is assigned to the instance variable oGet:reader. The Get reader itself is programmed in the following procedure:
***************************
PROCEDURE NoUmlauts( oGet )
LOCAL bBlock, cChar, nKey
IF GetPreValidate( oGet ) // perform prevalidation
oGet:setFocus() // set input focus to Get
bBlock := {|o,n1,n2| ; // translate within the code
GetApplykey(o,n1),; // block into two characters
IIf( o:typeOut, NIL, GetApplykey(o,n2) ) }
DO WHILE oGet:exitState == GE_NOEXIT
IF oGet:typeOut // no editable characters
oGet:exitState := GE_ENTER // right of the cursor
ENDIF
DO WHILE oGet:exitState == GE_NOEXIT
nKey := Inkey(0) // read keyboard
cChar:= Chr(nKey)
DO CASE // translate special characters
CASE cChar == "Ä"
Eval( bBlock, oGet, Asc("A"), Asc("e") )
CASE cChar == "Ö"
Eval( bBlock, oGet, Asc("O"), Asc("e") )
CASE cChar == "Ü"
Eval( bBlock, oGet, Asc("U"), Asc("e") )
CASE cChar == "ä"
Eval( bBlock, oGet, Asc("a"), Asc("e") )
CASE cChar == "ö"
Eval( bBlock, oGet, Asc("o"), Asc("e") )
CASE cChar == "ü"
Eval( bBlock, oGet, Asc("u"), Asc("e") )
CASE cChar == "ß"
Eval( bBlock, oGet, Asc("s"), Asc("s") )
OTHERWISE
GetApplykey( oGet, nKey )
ENDCASE
ENDDO
IF ! GetPostValidate( oGet )
oGet:exitState:= GE_NOEXIT // postvalidation
ENDIF // has failed
ENDDO
oGet:killFocus() // set back input focus
ENDIF
RETURN
Within the user-defined Get reader NoUmlauts(), keys are read using Inkey(). The keyboard input is then tested to see if it includes one of the German language characters Ä, Ö, Ü, ä, ö, ü and ß. If one of these characters is present, it is translated to the two equivalent characters Ae, Oe, Ue, ae, oe, ue, or ss. All other characters are passed along with the Get object to the function GetApplykey() which defines the default behavior for transferring characters into the edit buffer of Get objects.
In order to modify this Get reader to use the function AppEvent() for reading events (instead of just keystrokes), additional variables must be declared for the message parameters passed by reference to AppEvent(). Instead of GetApplykey(), the function GetHandleEvent() must be used for default handling of other events.
Along with formatted data input using @...SAY...GET with READ or using Get objects with ReadModal(), displaying an entire table is an important and frequently used interface feature. A table allows the user to interactively view large amounts of data. Data in a table is organized into rows and columns. This data can be from a database file open in a work area or from an array stored in memory. The TBrowse class and the TBColumn class are included in Xbase++ to allow tables to be displayed. These classes are used together but each performs a specific set of tasks when displaying tables. Table display is possible only through the combined efforts of objects of both classes. A TBrowse object performs the screen display and controls the current row and column of a table. A TBColumn object provides the data for a column of the table displayed by the Tbrowse object. TBColumn objects must be assigned to the TBrowse object and are used by the TBrowse object when a user views a table.
Class | Description |
---|---|
TBrowse() | Browsing of data that can be represented as a table of rows and columns |
TBColumn() | Display a single column of data in the browse window of the TBrowse object |
The TBrowse and TBColumn classes are associated with a set of functions that exist in Xbase++ only to provide compatibility with Clipper. The source code for the compatibility functions listed in the following table are contained in the files DBEDIT.PRG, BROWSYS.PRG and BROWUTIL.PRG.
Function | Description |
---|---|
Browse() | Browse database file, including DELETE and APPEND |
DbEdit() | Browse database file with UDF control |
TBrowseNew() | Create TBrowse object |
TBrowseDb() | Create TBrowse object for database file |
TBColumnNew() | Create TBColumn object |
TBrowse objects provide a versatile mechanism for displaying data in the form of a table. TBrowse objects display tabular data in a defined section of the screen (the browse window).
Tables are often too large to be entirely displayed on the screen. TBrowse objects provide methods that allow tables to be viewed interactively on the screen. The TBrowse class is designed so that an object of this class does not know anything about the data source it displays. To display data, a TBrowse object relies on one or more objects of the TBColumn class to provide the data for individual columns in the table. A TBrowse object displays the data provided by the TBColumn objects on the screen. Each TBColumn object deals with only one column of the associated table. The TBrowse object manages its own cell cursor and displays the data in the current row and column of a table (the current cell) in a highlighted color.
A user can position the cell cursor in the table using the cursor keys. A TBrowse object includes many methods which move the cell cursor. As soon as the user tries to move the cell cursor out of the Browse window, the TBrowse object automatically synchronizes the visible data on the screen with the data of the underlying table and scrolls the data in the browse window.
Since the data source of the table is not known to the TBrowse object, three functions must be specified to execute the three basic operations that can be performed on a table: jump to the start of the table, jump to the end of the table and change the current row within the table. These operations are comparable to the file commands GO TOP, GO BOTTOM and SKIP. These functions are provided to TBrowse objects as code blocks that are automatically executed within specific methods of the TBrowse object.
The browse window where the TBrowse object displays tabular data can be divided into three areas: headers or column titles, data lines containing the data, and footers at the bottom of each column. Each area, as well as each individual column, can be optionally delimited by a separating line.
In contrast to TBrowse objects, TBColumn objects are very simple objects that contain instance variables but do not have their own methods. TBColumn objects are needed to provide data for tables displayed using TBrowse objects and are useless without an associated TBrowse object. A TBColumn object contains in its instance variable all of the information required for displaying a single column of a table in the browse window of the TBrowse object.
The most important TBColumn instance variable (:block) contains a data code block that provides the data from the data source for a column of data. This code block might access a field variable in a work area, or a column in an array. TBColumn objects can also control the color of the data displayed based on its value.
If new values are assigned to the instance variables of a TBColumn object after the TBrowse object has already displayed data using the TBColumn object, the method oTBrowse:configure() must be executed to update the columns in the browse window (see TBrowse class).
If you see anything in the documentation that is not correct, does not match your experience with the particular feature or requires further clarification, please use this form to report a documentation issue.