TFolder
Tab folder control for organizing complex dialogs into pages
Fuente: source/classes/folder.prg | Parent: TControl
Overview
TFolder implements a Windows tab control. Each tab page is an independent TDialog object, so you can place any controls on each page using standard dialog syntax. The folder manages which page is visible when the user clicks a tab.
Key features:
- Each page is a full
TDialog-- any control can be placed on it - Tab icons via
oImageList - Programmatic page switching via
nOption - Tabs at top or bottom
- Dynamic page adding/removing at runtime
Page Composition
graph TD
PARENT["TDialog / TWindow
(Parent)"] --> FOLDER["TFolder"] FOLDER --> TAB1["Tab 1: General"] FOLDER --> TAB2["Tab 2: Details"] FOLDER --> TAB3["Tab 3: Notes"] TAB1 --> DLG1["aDialogs[1]
TDialog"] TAB2 --> DLG2["aDialogs[2]
TDialog"] TAB3 --> DLG3["aDialogs[3]
TDialog"] DLG1 --> C1["TGet, TComboBox,
TCheckBox, ..."] DLG2 --> C2["TXBrowse, TButton, ..."] DLG3 --> C3["TMGet (memo), ..."] style FOLDER fill:#1c2129,stroke:#58a6ff,stroke-width:2px,color:#e6edf3 style DLG1 fill:#1c2129,stroke:#3fb950,stroke-width:1px,color:#e6edf3 style DLG2 fill:#1c2129,stroke:#3fb950,stroke-width:1px,color:#e6edf3 style DLG3 fill:#1c2129,stroke:#3fb950,stroke-width:1px,color:#e6edf3
(Parent)"] --> FOLDER["TFolder"] FOLDER --> TAB1["Tab 1: General"] FOLDER --> TAB2["Tab 2: Details"] FOLDER --> TAB3["Tab 3: Notes"] TAB1 --> DLG1["aDialogs[1]
TDialog"] TAB2 --> DLG2["aDialogs[2]
TDialog"] TAB3 --> DLG3["aDialogs[3]
TDialog"] DLG1 --> C1["TGet, TComboBox,
TCheckBox, ..."] DLG2 --> C2["TXBrowse, TButton, ..."] DLG3 --> C3["TMGet (memo), ..."] style FOLDER fill:#1c2129,stroke:#58a6ff,stroke-width:2px,color:#e6edf3 style DLG1 fill:#1c2129,stroke:#3fb950,stroke-width:1px,color:#e6edf3 style DLG2 fill:#1c2129,stroke:#3fb950,stroke-width:1px,color:#e6edf3 style DLG3 fill:#1c2129,stroke:#3fb950,stroke-width:1px,color:#e6edf3
Key Properties
| Property | Type | Description |
|---|---|---|
aPrompts | Array | Array of strings for each tab label (e.g. {"General", "Details", "Notes"}) |
aDialogs | Array | Array of TDialog objects, one per tab page |
nOption | Numeric | Index of the currently selected tab (1-based). Set this to switch pages programmatically. |
oImageList | Object | Optional TImageList providing icons for each tab |
bChange | Block | Code block executed when the user switches tabs |
lBottom | Logical | When .T., tabs appear at the bottom instead of the top |
nTabHeight | Numeric | Height of the tab strip in pixels |
Key Methods
| Method | Description |
|---|---|
New( nRow, nCol, oWnd, aPrompts, ... ) | Constructor. Creates the folder with the given tab labels. |
AddPage( cPrompt, oDlg ) | Add a new page at runtime. |
DelPage( nPage ) | Remove a page at runtime. |
SetOption( n ) | Switch to page n programmatically. |
GetPage( n ) | Returns the TDialog for page n. |
Command Syntax
@ nRow, nCol FOLDER oFld ;
OF oDlg ;
PROMPTS "Tab1", "Tab2", "Tab3" ;
DIALOGS oDlg1, oDlg2, oDlg3 ;
[ SIZE nWidth, nHeight ] ;
[ ON CHANGE bChange ] ;
[ BOTTOM ]
Each dialog variable (oDlg1, oDlg2, oDlg3) is automatically created as a child TDialog. You then place controls on each page dialog in the ACTIVATE DIALOG block.
Tab Navigation Flow
stateDiagram-v2
[*] --> Page1 : Folder created
Page1 --> Page2 : Click tab 2
Page2 --> Page3 : Click tab 3
Page3 --> Page1 : Click tab 1
Page1 --> PageN : SetOption(N)
PageN --> Page1 : SetOption(1)
state Page1 {
[*] --> DLG1_Visible
DLG1_Visible : aDialogs[1] shown
DLG1_Visible : Other pages hidden
}
state Page2 {
[*] --> DLG2_Visible
DLG2_Visible : aDialogs[2] shown
DLG2_Visible : Other pages hidden
}
state Page3 {
[*] --> DLG3_Visible
DLG3_Visible : aDialogs[3] shown
DLG3_Visible : Other pages hidden
}
Code Examples
Example 1: Customer Edit Dialog with Tabs
#include "FiveWin.ch"
FUNCTION CustomerEdit()
LOCAL oDlg, oFld, oFont
LOCAL oDlg1, oDlg2, oDlg3
LOCAL cName := "John Smith"
LOCAL cPhone := "(555)123-4567"
LOCAL cEmail := "john@example.com"
LOCAL cAddr := "123 Main St"
LOCAL cCity := "New York"
LOCAL cState := "NY"
LOCAL cZip := "10001"
LOCAL nCredit := 5000.00
LOCAL cNotes := "VIP customer since 2015"
LOCAL lActive := .T.
DEFINE FONT oFont NAME "Segoe UI" SIZE 0, -14
DEFINE DIALOG oDlg TITLE "Edit Customer" ;
SIZE 520, 420 FONT oFont
// Create the folder with 3 tabs
@ 0.5, 0.5 FOLDER oFld ;
OF oDlg ;
PROMPTS "General", "Address", "Notes" ;
DIALOGS oDlg1, oDlg2, oDlg3 ;
SIZE 490, 310
// --- Page 1: General ---
@ 1.0, 1 SAY "Name:" OF oDlg1
@ 1.0, 5 GET cName OF oDlg1 SIZE 200, 22 PICTURE "@!"
@ 2.5, 1 SAY "Phone:" OF oDlg1
@ 2.5, 5 GET cPhone OF oDlg1 SIZE 140, 22 PICTURE "(999)999-9999"
@ 4.0, 1 SAY "Email:" OF oDlg1
@ 4.0, 5 GET cEmail OF oDlg1 SIZE 220, 22
@ 5.5, 1 SAY "Credit Limit:" OF oDlg1
@ 5.5, 5 GET nCredit OF oDlg1 SIZE 120, 22 PICTURE "@E 99,999.99"
@ 7.0, 1 CHECKBOX lActive PROMPT "Active Customer" OF oDlg1
// --- Page 2: Address ---
@ 1.0, 1 SAY "Street:" OF oDlg2
@ 1.0, 5 GET cAddr OF oDlg2 SIZE 280, 22
@ 2.5, 1 SAY "City:" OF oDlg2
@ 2.5, 5 GET cCity OF oDlg2 SIZE 180, 22
@ 4.0, 1 SAY "State:" OF oDlg2
@ 4.0, 5 GET cState OF oDlg2 SIZE 40, 22 PICTURE "!!"
@ 4.0, 9 SAY "ZIP:" OF oDlg2
@ 4.0, 11 GET cZip OF oDlg2 SIZE 80, 22 PICTURE "99999"
// --- Page 3: Notes ---
@ 0.5, 1 GET cNotes MEMO OF oDlg3 SIZE 440, 200
// Buttons below the folder
@ 24, 12 BUTTON "&Save" OF oDlg SIZE 80, 25 ACTION oDlg:End() DEFAULT
@ 24, 22 BUTTON "&Cancel" OF oDlg SIZE 80, 25 ACTION oDlg:End() CANCEL
ACTIVATE DIALOG oDlg CENTERED
RELEASE FONT oFont
RETURN NIL
Example 2: Folder with Browse and Dynamic Page Switching
#include "FiveWin.ch"
#include "XBrowse.ch"
FUNCTION DataViewer()
LOCAL oDlg, oFld, oBrw
LOCAL oDlg1, oDlg2
LOCAL aLog := { ;
{ "2026-03-24 09:00", "Login", "admin" }, ;
{ "2026-03-24 09:15", "Edit", "admin" }, ;
{ "2026-03-24 10:00", "Export", "user1" } ;
}
DEFINE DIALOG oDlg TITLE "Data Viewer" SIZE 600, 450
@ 0.2, 0.2 FOLDER oFld ;
OF oDlg ;
PROMPTS "Browse Data", "Activity Log" ;
DIALOGS oDlg1, oDlg2 ;
SIZE 570, 340
// Notify when tab changes
oFld:bChange := {|| MsgBeep() }
// --- Page 1: XBrowse of a DBF ---
USE Customer VIA "DBFCDX" NEW
oBrw := TXBrowse():New( oDlg1 )
oBrw:SetRDD()
oBrw:CreateFromCode()
oDlg1:oClient := oBrw
// --- Page 2: XBrowse of an array ---
LOCAL oBrw2 := TXBrowse():New( oDlg2 )
oBrw2:SetArray( aLog )
oBrw2:aCols := {}
oBrw2:AddColumn( "Timestamp", {|| aLog[ oBrw2:nArrayAt ][ 1 ] } )
oBrw2:AddColumn( "Action", {|| aLog[ oBrw2:nArrayAt ][ 2 ] } )
oBrw2:AddColumn( "User", {|| aLog[ oBrw2:nArrayAt ][ 3 ] } )
oBrw2:CreateFromCode()
oDlg2:oClient := oBrw2
// Buttons
@ 27, 18 BUTTON "&Close" OF oDlg SIZE 80, 25 ACTION oDlg:End() CANCEL
// Jump to Activity Log tab programmatically
@ 27, 2 BUTTON "Show &Log" OF oDlg SIZE 100, 25 ;
ACTION oFld:SetOption( 2 )
ACTIVATE DIALOG oDlg CENTERED
CLOSE Customer
RETURN NIL
Tips
- Place controls on each page dialog (
oDlg1,oDlg2, ...) -- not on the parent dialogoDlg. - Buttons that should remain visible on all tabs (Save, Cancel) go on the parent dialog, positioned below the folder.
- Use
oFld:SetOption( n )to switch tabs from code, for example in response to a button click. - Use
bChangeto refresh data when the user switches tabs (e.g. refresh a browse on a tab). - To add icons to tabs, create a
TImageListand assign it tooFld:oImageListbefore activation. - Each page dialog inherits the font from the parent dialog, so set the font on the parent for consistency.