TComboBox
Drop-down selection control with multiple styles and data binding
Source: source/classes/combobox.prg | Parent: TControl
Overview
TComboBox wraps the Windows ComboBox control, providing a drop-down list from which the user selects one item. It supports three styles:
- CBS_DROPDOWN (default) -- editable text field with a drop-down list
- CBS_DROPDOWNLIST -- non-editable; the user can only select from the list
- CBS_SIMPLE -- editable text field with an always-visible list
The combo can bind to either a string variable (the selected item text) or a numeric variable (the selected item index). It supports incremental search, custom drawing, and data-driven item lists.
ComboBox Styles
graph LR
subgraph "CBS_DROPDOWN"
DD_TEXT["Editable text field"]
DD_BTN["Drop-down button"]
DD_LIST["List appears on click"]
end
subgraph "CBS_DROPDOWNLIST"
DDL_TEXT["Read-only display"]
DDL_BTN["Drop-down button"]
DDL_LIST["Selection from list only"]
end
subgraph "CBS_SIMPLE"
S_TEXT["Editable text field"]
S_LIST["List always visible"]
end
style DD_TEXT fill:#1c2129,stroke:#58a6ff,color:#e6edf3
style DDL_TEXT fill:#1c2129,stroke:#3fb950,color:#e6edf3
style S_TEXT fill:#1c2129,stroke:#d29922,color:#e6edf3
Key Properties
| Property | Type | Description |
|---|---|---|
aItems | Array | Array of strings displayed in the drop-down list |
bSetGet | Block | Data-binding block. For string combos: reads/writes the selected text. For numeric combos: reads/writes the selected index. |
bChange | Block | Code block executed when the selection changes |
nAt | Numeric | Index of the currently selected item (1-based) |
lIncSearch | Logical | Enable incremental search as the user types |
lOwnerDraw | Logical | Enable custom item drawing via bDrawItem |
bDrawItem | Block | Custom drawing block for owner-draw mode |
cCueText | String | Placeholder text when nothing is selected |
nDropWidth | Numeric | Width of the drop-down list (can be wider than the control itself) |
Key Methods
| Method | Description |
|---|---|
New( nRow, nCol, bSetGet, aItems, ... ) | Constructor. Creates a new combo at the given position. |
ReDefine( nId, bSetGet, aItems, oWnd, ... ) | Redefine from a dialog resource. |
Set( cText ) | Set the selected item by its text. |
Select( n ) | Set the selected item by its index. |
Add( cItem ) | Add an item to the list at runtime. |
Del( n ) | Remove item at index n. |
Reset() | Clear all items from the list. |
Refresh() | Repopulate the list from aItems and restore the selection. |
Command Syntax
String-bound ComboBox
@ nRow, nCol COMBOBOX oCombo VAR cVar ;
ITEMS aItems ;
[ OF oWnd ] ;
[ SIZE nWidth, nHeight ] ;
[ ON CHANGE bChange ] ;
[ VALID bValid ] ;
[ STYLE CBS_DROPDOWNLIST ] ;
[ FONT oFont ]
Numeric-bound (index) ComboBox
@ nRow, nCol COMBOBOX oCombo VAR nIndex ;
ITEMS aItems ;
[ OF oWnd ] ;
[ SIZE nWidth, nHeight ] ;
[ ON CHANGE bChange ]
Data Binding Diagram
sequenceDiagram
participant User
participant Combo as TComboBox
participant Var as Bound Variable
User->>Combo: Select "Option B"
Combo->>Combo: nAt = 2
alt String binding
Combo->>Var: Eval(bSetGet, "Option B")
else Numeric binding
Combo->>Var: Eval(bSetGet, 2)
end
Combo->>Combo: Eval(bChange)
Note over Combo,Var: On dialog activation, Combo reads
Eval(bSetGet) to set initial selection
Eval(bSetGet) to set initial selection
Code Examples
Example 1: Simple ComboBox Selections
#include "FiveWin.ch"
FUNCTION ComboDemo()
LOCAL oDlg, oFont
LOCAL cCountry := "USA"
LOCAL nPriority := 2
LOCAL aCountries := { "USA", "Canada", "Mexico", "Brazil", "UK", "France", "Germany", "Spain" }
LOCAL aPriority := { "Low", "Medium", "High", "Critical" }
DEFINE FONT oFont NAME "Segoe UI" SIZE 0, -14
DEFINE DIALOG oDlg TITLE "ComboBox Demo" SIZE 400, 300 FONT oFont
// String-bound combo -- cCountry holds the selected text
@ 1.0, 1 SAY "Country:" OF oDlg
@ 1.0, 6 COMBOBOX cCountry ;
ITEMS aCountries ;
OF oDlg SIZE 180, 200 ;
STYLE CBS_DROPDOWNLIST
// Numeric-bound combo -- nPriority holds the selected index
@ 3.0, 1 SAY "Priority:" OF oDlg
@ 3.0, 6 COMBOBOX nPriority ;
ITEMS aPriority ;
OF oDlg SIZE 140, 200 ;
ON CHANGE ( MsgInfo( "Priority changed to: " + aPriority[ nPriority ] ) )
@ 5.5, 6 BUTTON "&OK" OF oDlg SIZE 80, 25 ;
ACTION ( MsgInfo( "Country: " + cCountry + CRLF + ;
"Priority: " + aPriority[ nPriority ] ), ;
oDlg:End() ) DEFAULT
@ 5.5, 16 BUTTON "&Cancel" OF oDlg SIZE 80, 25 ACTION oDlg:End() CANCEL
ACTIVATE DIALOG oDlg CENTERED
RELEASE FONT oFont
RETURN NIL
Example 2: Database-Driven ComboBox
#include "FiveWin.ch"
FUNCTION DBCombo()
LOCAL oDlg, oCmb
LOCAL cCategory := ""
LOCAL aCategories := {}
// Build the items array from a database table
USE Categories VIA "DBFCDX" NEW
Categories->( DbGoTop() )
DO WHILE !Categories->( Eof() )
AAdd( aCategories, AllTrim( Categories->CATNAME ) )
Categories->( DbSkip() )
ENDDO
DEFINE DIALOG oDlg TITLE "Select Category" SIZE 400, 200
@ 1.5, 1 SAY "Category:" OF oDlg
@ 1.5, 6 COMBOBOX oCmb VAR cCategory ;
ITEMS aCategories ;
OF oDlg SIZE 200, 200 ;
STYLE CBS_DROPDOWN ;
VALID ( !Empty( cCategory ) ) ;
CUETEXT "Type or select a category"
@ 4.0, 6 BUTTON "&Select" OF oDlg SIZE 90, 25 ;
ACTION ( MsgInfo( "Selected: " + cCategory ), oDlg:End() ) DEFAULT
@ 4.0, 16 BUTTON "&Cancel" OF oDlg SIZE 80, 25 ACTION oDlg:End() CANCEL
ACTIVATE DIALOG oDlg CENTERED
CLOSE Categories
RETURN NIL
Example 3: Dependent ComboBoxes (Country/City)
#include "FiveWin.ch"
FUNCTION DependentCombos()
LOCAL oDlg
LOCAL cCountry := "USA"
LOCAL cCity := ""
LOCAL oCmbCountry, oCmbCity
LOCAL aCountries := { "USA", "Canada", "UK" }
LOCAL aCities := {}
DEFINE DIALOG oDlg TITLE "Dependent Combos" SIZE 400, 250
@ 1, 1 SAY "Country:" OF oDlg
@ 1, 6 COMBOBOX oCmbCountry VAR cCountry ;
ITEMS aCountries OF oDlg SIZE 160, 200 ;
STYLE CBS_DROPDOWNLIST ;
ON CHANGE ( UpdateCities( cCountry, @aCities, oCmbCity ) )
@ 3, 1 SAY "City:" OF oDlg
@ 3, 6 COMBOBOX oCmbCity VAR cCity ;
ITEMS aCities OF oDlg SIZE 160, 200 ;
STYLE CBS_DROPDOWNLIST
// Initialize cities for the default country
UpdateCities( cCountry, @aCities, oCmbCity )
@ 5.5, 6 BUTTON "&OK" OF oDlg SIZE 80, 25 ACTION oDlg:End() DEFAULT
@ 5.5, 16 BUTTON "&Cancel" OF oDlg SIZE 80, 25 ACTION oDlg:End() CANCEL
ACTIVATE DIALOG oDlg CENTERED
RETURN NIL
STATIC FUNCTION UpdateCities( cCountry, aCities, oCmbCity )
DO CASE
CASE cCountry == "USA"
aCities := { "New York", "Los Angeles", "Chicago", "Houston" }
CASE cCountry == "Canada"
aCities := { "Toronto", "Montreal", "Vancouver", "Calgary" }
CASE cCountry == "UK"
aCities := { "London", "Manchester", "Birmingham", "Glasgow" }
OTHERWISE
aCities := {}
ENDCASE
IF oCmbCity != NIL
oCmbCity:aItems := aCities
oCmbCity:Reset()
oCmbCity:Refresh()
IF Len( aCities ) > 0
oCmbCity:Select( 1 )
ENDIF
ENDIF
RETURN NIL
Tips
- Use
CBS_DROPDOWNLISTwhen the user must choose from the list only. UseCBS_DROPDOWNwhen free-text entry is also acceptable. - The SIZE height parameter controls the drop-down list height, not the control itself. Make it large enough to show several items.
- For large item lists, enable
lIncSearchso users can type to filter. - Use
bChangefor cascading combos (changing one combo updates another). - To refresh a combo's items at runtime, update
aItems, callReset(), thenRefresh(). - For combos bound to a DBF field, use
FieldWBlock()as thebSetGet.
See Also
- TControl -- parent class
- TGet -- text input alternative
- TCheckBox / TRadio -- other selection controls
- TXBrowse -- combos are used as cell editors in browse columns