TXBrowse
The most powerful and versatile data grid control in FiveWin
Source: source/classes/xbrowse.prg | Parent: TControl | Header: XBrowse.ch
Overview
TXBrowse is the flagship data grid control of FiveWin. It renders tabular data from virtually any source -- DBF tables, in-memory arrays, ADO RecordSets, ODBC cursors, or custom data -- through a unified block-based navigation architecture. It virtualizes rendering so that only visible rows are processed, enabling it to handle millions of records without excessive memory consumption.
Key capabilities include:
- Data source abstraction -- one control, many back-ends (DBF/RDD, Array, ADO, ODBC, custom)
- Rich column objects -- each column (
TXBrwCol) controls its own header, data formatting, colors, editing, and width - In-cell editing -- GET, ComboBox, CheckBox, DatePicker, and Spinner editors per column
- Incremental seek / filter -- built-in
lIncrFilterseek bar with per-column search - Multi-select, freeze columns, record selector, marquee styles
- Export to Excel, CSV, clipboard, and printer
Architecture
TXBrowse decouples the visual grid from the underlying data through a set of navigation code blocks. Helper methods like SetRDD(), SetArray(), and SetAdo() simply populate these blocks with the appropriate logic for each data source type.
(SetRDD)"] ARR["Array
(SetArray)"] ADO["ADO RecordSet
(SetAdo)"] ODBC["ODBC Cursor
(SetODBC)"] CUSTOM["Custom
(manual blocks)"] end subgraph NavBlocks["Navigation Blocks"] bGoTop bGoBottom bSkip bBof bEof bKeyNo bKeyCount bBookMark bSeek end subgraph TXBrowseObj["TXBrowse Object"] GRID["Grid Engine
paint, scroll, keys"] COLS["aCols<br/>TXBrwCol array"] GRID --- COLS end DBF --> bGoTop ARR --> bGoTop ADO --> bGoTop ODBC --> bGoTop CUSTOM --> bGoTop bGoTop --> GRID bGoBottom --> GRID bSkip --> GRID bKeyCount --> GRID bBookMark --> GRID style TXBrowseObj fill:#1c2129,stroke:#58a6ff,stroke-width:2px,color:#e6edf3 style NavBlocks fill:#1c2129,stroke:#3fb950,stroke-width:1px,color:#e6edf3 style DataSources fill:#1c2129,stroke:#d29922,stroke-width:1px,color:#e6edf3
Column Object: TXBrwCol
Each column in the browse is a TXBrwCol object stored in the aCols array. Columns define what data to display and how to display it.
Key Properties (DATA)
| Property | Type | Description |
|---|---|---|
aCols | Array | Array of TXBrwCol column objects defining every column in the grid |
cAlias | String | Work area alias when using SetRDD() |
nFreeze | Numeric | Number of left-hand columns that remain frozen during horizontal scroll |
lRecordSelector | Logical | When .T., displays a row-indicator column on the left edge |
nMarqueeStyle | Numeric | Visual style of the selection highlight (see XBrowse.ch constants: MARQSTYLE_HIGHLROW, MARQSTYLE_HIGHLCELL, MARQSTYLE_DOTEDCELL, etc.) |
lFastEdit | Logical | When .T., typing a character immediately opens the cell editor -- no Enter key required. Caution: this disables single-key navigation shortcuts. |
lIncrFilter | Logical | When .T., a seek/filter bar appears below the headers for incremental column searching |
nDataType | Numeric | Internal flag indicating the data source type (RDD, Array, ADO, etc.) |
lMultiSelect | Logical | Enables multi-row selection with Ctrl+Click and Shift+Click |
nArrayAt | Numeric | Current row index when browsing an array data source |
nRowHeight | Numeric | Pixel height of each data row |
nHeaderHeight | Numeric | Pixel height of the column header row |
Navigation Blocks
These code blocks form the data abstraction layer. They are set automatically by SetRDD(), SetArray(), SetAdo(), etc., but you can supply custom blocks for specialized data sources.
| Block | Signature | Description |
|---|---|---|
bGoTop | {|| ... } | Position data source at first record |
bGoBottom | {|| ... } | Position data source at last record |
bSkip | {|n| ... } | Move n records (positive=forward, negative=backward). Returns actual records moved. |
bBof | {|| ... } | Returns .T. if before first record |
bEof | {|| ... } | Returns .T. if past last record |
bKeyNo | {|n| ... } | Get/set the current logical position (for scrollbar) |
bKeyCount | {|| ... } | Returns total number of records (for scrollbar) |
bBookMark | {|u| ... } | If u is NIL, returns a bookmark for the current row. Otherwise, repositions to bookmark u. |
bSeek | {|c| ... } | Incremental seek -- find the first record matching the string c |
Key Methods
| Method | Description |
|---|---|
New( oWnd ) | Constructor. Creates a TXBrowse instance associated with parent window/dialog oWnd. |
SetRDD( [cAlias] ) | Configure navigation blocks for a DBF/RDD work area. |
SetArray( aData, [lAutoCol] ) | Configure navigation blocks for an in-memory array. |
SetAdo( oRs ) | Configure navigation blocks for an ADO RecordSet object. |
SetODBC( oQry ) | Configure navigation blocks for an ODBC result set. |
AddColumn( cHead, bData, [cPict], ... ) | Add a column with header, data block, optional picture, and more. |
CreateFromCode() | Finalize control creation after configuring data source and columns. |
Adjust() | Recalculate header, row, and footer dimensions. Sometimes needed after font changes. |
Refresh() | Repaint all visible rows. |
SelectedCol() | Returns the currently selected TXBrwCol object. |
Report( [cTitle] ) | Quick-print the browse contents via the FWH print engine. |
ToExcel() | Export visible data to Microsoft Excel. |
ToClip() | Copy visible data to the clipboard. |
Commands (XBrowse.ch)
FiveWin provides two command styles for creating TXBrowse controls:
Positioned Command
@ nRow, nCol XBROWSE oBrw ;
[ OF oWnd ] ;
[ SIZE nWidth, nHeight ] ;
[ COLUMNS cCol1, cCol2, ... ] ;
[ HEADERS cHead1, cHead2, ... ] ;
[ ALIAS cAlias ] ;
[ ON DBLCLICK bAction ] ;
[ FONT oFont ] ;
[ NOBORDER ]
Quick Browser Command
XBROWSER <cAlias> // browse a DBF alias
XBROWSER <aArray> // browse an array
XBROWSER <oRs> // browse an ADO RecordSet
Code Examples
Example 1: Browsing a DBF Table
#include "FiveWin.ch"
#include "XBrowse.ch"
FUNCTION Main()
LOCAL oWnd, oBrw
USE Customer VIA "DBFCDX" NEW
DEFINE WINDOW oWnd TITLE "Customers" FROM 1, 1 TO 24, 80
oBrw := TXBrowse():New( oWnd )
oBrw:nTop := 0
oBrw:nLeft := 0
oBrw:nRight := -1 // stretch to right edge
oBrw:nBottom := -1 // stretch to bottom edge
oBrw:SetRDD() // bind to current work area
// Configure individual columns
WITH OBJECT oBrw:AddColumn( "ID", FieldBlock( "CUST_ID" ), "99999" )
:nWidth := 80
:nDataStrAlign := AL_RIGHT
END
WITH OBJECT oBrw:AddColumn( "First Name", FieldBlock( "FIRSTNAME" ), "@!" )
:nWidth := 140
:lEdit := .T. // allow in-cell editing
END
oBrw:AddColumn( "Last Name", FieldBlock( "LASTNAME" ), "@!" )
oBrw:AddColumn( "City", FieldBlock( "CITY" ) )
oBrw:AddColumn( "Balance", FieldBlock( "BALANCE" ), "@E 999,999.99" )
oBrw:nFreeze := 1 // freeze ID column
oBrw:lRecordSelector := .T. // show row indicator
oBrw:nMarqueeStyle := MARQSTYLE_HIGHLROW
oBrw:CreateFromCode()
oWnd:oClient := oBrw // fill the window client area
ACTIVATE WINDOW oWnd
CLOSE Customer
RETURN NIL
Example 2: Browsing an Array
#include "FiveWin.ch"
#include "XBrowse.ch"
FUNCTION Main()
LOCAL oWnd, oBrw
LOCAL aData := { ;
{ "Alice", "Smith", 28, .T. }, ;
{ "Bob", "Johnson", 34, .F. }, ;
{ "Charlie", "Williams", 45, .T. }, ;
{ "Diana", "Brown", 31, .T. }, ;
{ "Edward", "Jones", 52, .F. } ;
}
DEFINE WINDOW oWnd TITLE "Array Browse" FROM 2, 2 TO 20, 70
oBrw := TXBrowse():New( oWnd )
oBrw:SetArray( aData, .F. ) // .F. = do not auto-create columns
oBrw:aCols := {}
oBrw:AddColumn( "First", {|| aData[ oBrw:nArrayAt ][ 1 ] } )
oBrw:AddColumn( "Last", {|| aData[ oBrw:nArrayAt ][ 2 ] } )
oBrw:AddColumn( "Age", {|| aData[ oBrw:nArrayAt ][ 3 ] }, "999" )
oBrw:AddColumn( "Active", {|| aData[ oBrw:nArrayAt ][ 4 ] } )
// Make "Active" column render as a checkbox
oBrw:aCols[ 4 ]:SetCheck( "CHECK1", "UNCHECK1" )
oBrw:nMarqueeStyle := MARQSTYLE_HIGHLCELL
oBrw:CreateFromCode()
oWnd:oClient := oBrw
ACTIVATE WINDOW oWnd
RETURN NIL
Example 3: Editable Browse with Column Configuration
#include "FiveWin.ch"
#include "XBrowse.ch"
FUNCTION Main()
LOCAL oWnd, oBrw, oCol
USE Invoices VIA "DBFCDX" NEW
DEFINE WINDOW oWnd TITLE "Editable Invoices"
@ 0, 0 XBROWSE oBrw OF oWnd ;
COLUMNS "INVNO", "CUSTNAME", "AMOUNT", "INVDATE", "PAID" ;
HEADERS "Invoice #", "Customer", "Amount", "Date", "Paid?"
oBrw:SetRDD()
// Configure the Amount column
oCol := oBrw:aCols[ 3 ]
oCol:cPicture := "@E 999,999.99"
oCol:lEdit := .T.
oCol:nEditType := EDIT_GET // edit with a GET control
oCol:bClrStd := {|| { CLR_BLACK, CLR_WHITE } }
// Configure the Date column with a date picker editor
oCol := oBrw:aCols[ 4 ]
oCol:lEdit := .T.
oCol:nEditType := EDIT_DATE
// Configure the Paid column as a checkbox
oCol := oBrw:aCols[ 5 ]
oCol:SetCheck( "CHECK1", "UNCHECK1" )
oCol:lEdit := .T.
oBrw:lFastEdit := .T. // typing goes straight to edit mode
oBrw:CreateFromCode()
oWnd:oClient := oBrw
ACTIVATE WINDOW oWnd
CLOSE Invoices
RETURN NIL
Example 4: Incremental Seek / Filter Bar
#include "FiveWin.ch"
#include "XBrowse.ch"
FUNCTION Main()
LOCAL oWnd, oBrw
USE Customer INDEX Customer VIA "DBFCDX" NEW
DEFINE WINDOW oWnd TITLE "Customer Search"
oBrw := TXBrowse():New( oWnd )
oBrw:SetRDD()
oBrw:AddColumn( "Name", FieldBlock( "NAME" ) )
oBrw:AddColumn( "City", FieldBlock( "CITY" ) )
oBrw:AddColumn( "Phone", FieldBlock( "PHONE" ) )
oBrw:AddColumn( "Balance", FieldBlock( "BALANCE" ), "@E 999,999.99" )
// Enable the incremental filter bar
oBrw:lIncrFilter := .T.
// Custom seek block -- performs an indexed seek on the NAME field
oBrw:bSeek := {|cSeek| ( oBrw:cAlias )->( DbSeek( cSeek, .T. ) ) }
oBrw:nMarqueeStyle := MARQSTYLE_HIGHLROW
oBrw:lRecordSelector := .T.
oBrw:CreateFromCode()
oWnd:oClient := oBrw
ACTIVATE WINDOW oWnd
CLOSE Customer
RETURN NIL
Tips and Best Practices
- Always call
CreateFromCode()after configuring data source, columns, and visual properties. - Use
oWnd:oClient := oBrwto make the browse auto-resize with the window. - Avoid
lFastEditwith navigation-heavy UIs -- it intercepts character keys that would otherwise trigger seeks or keyboard shortcuts. - For incremental seek on a DBF, ensure the table has an active index on the column you are searching.
- Column
bClrEditcontrols the colors of the cell while it is being edited (foreground/background block). This is separate frombClrStdwhich controls display-mode colors. - Double-buffer painting: TXBrowse paints to an off-screen bitmap, then blits to screen. This eliminates flicker but means you should call
Refresh()-- notInvalidate()-- to force a redraw. - For very large arrays, consider using
SetRDD()with a temporary DBF instead, as array navigation does not virtualize seeks.
See Also
- TControl -- parent class
- TDatabase -- high-level DBF wrapper often used with TXBrowse
- TRecordSet (ADO) -- ADO data source for
SetAdo() - TGet -- used internally for cell editing