Tutorial: Event Handling
Events are the backbone of interactive applications. Every time a user clicks a button, types a key, resizes a window, or closes a form, HarbourBuilder fires an event that your code can handle. This tutorial covers all the major event types and the different ways to respond to them.
Step 1: Understanding Events in HarbourBuilder
Every HarbourBuilder control exposes a set of event properties (e.g. OnClick, OnChange).
You assign a code block or a method reference to these properties. When the event
fires, HarbourBuilder executes your handler.
(click, key, resize)"] --> B["OS Message
WM_COMMAND etc."] B --> C["HarbourBuilder
Event Dispatcher"] C --> D["Your Handler
Code Block / Method"] style A fill:#58a6ff,stroke:#388bfd,color:#0d1117 style B fill:#d2a8ff,stroke:#bc8cff,color:#0d1117 style C fill:#3fb950,stroke:#2ea043,color:#0d1117 style D fill:#f0883e,stroke:#d18616,color:#0d1117
Step 2: Generating Event Handlers via the Object Inspector
- Select a control on the form (e.g. a Button).
- In the Object Inspector, switch to the Events tab.
- Double-click the event name (e.g.
OnClick). - The IDE automatically:
- Creates a
static functionstub in your source file. - Assigns the code block to call that function.
- Jumps the cursor to the new function body so you can start coding.
- Creates a
Double-clicking an event in the Object Inspector is the recommended workflow. It generates
properly named handlers (e.g. OnBtnSaveClick) and wires up the code block automatically.
Step 3: OnClick — Button and Control Clicks
The most common event. Fires when the user clicks a button, label, image, or any clickable control.
Inline code block (simple logic):
oBtn:OnClick := { || MsgInfo( "Button was clicked!" ) }
Calling a separate function (complex logic):
oBtn:OnClick := { || OnBtnSaveClick( oForm, oGet ) } static function OnBtnSaveClick( oForm, oGet ) local cValue := oGet:GetValue() if Empty( cValue ) MsgAlert( "Please enter a value." ) return nil endif MsgInfo( "Saved: " + cValue ) oForm:SetTitle( "Saved!" ) return nil
Step 4: OnChange — Value Changes
Fires whenever the value of an input control changes. Useful for TextBox (TGet), ComboBox,
CheckBox, and Spinner controls.
oGetSearch:OnChange := { || OnSearchChange( oGetSearch, oListBox ) } static function OnSearchChange( oGet, oList ) local cFilter := oGet:GetValue() oList:Filter( { |cItem| Upper( cFilter ) $ Upper( cItem ) } ) return nil
For TextBox controls, OnChange fires after each character typed. If you are doing expensive
operations (like database queries), consider using a TTimer to debounce the input.
Step 5: OnKeyDown — Keyboard Input
Fires when a key is pressed while the control has focus. The handler receives the key code and modifier flags as parameters.
oGet:OnKeyDown := { |nKey, nFlags| OnGetKeyDown( nKey, nFlags, oGet ) } static function OnGetKeyDown( nKey, nFlags, oGet ) do case case nKey == VK_RETURN MsgInfo( "Enter pressed! Value: " + oGet:GetValue() ) case nKey == VK_ESCAPE oGet:SetValue( "" ) case nKey == VK_F1 MsgInfo( "Help: type a value and press Enter." ) endcase return nil
Common key constants: VK_RETURN, VK_ESCAPE, VK_TAB, VK_DELETE,
VK_F1 through VK_F12, VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT.
Step 6: OnResize — Form and Control Resizing
Fires when the user resizes the form. Use this to reposition or resize controls dynamically for a responsive layout.
oForm:OnResize := { || OnFormResize( oForm, oMemo, oStatusBar ) } static function OnFormResize( oForm, oMemo, oStatusBar ) local nW := oForm:nWidth local nH := oForm:nHeight // Make the memo fill the form with a 10-pixel margin oMemo:SetSize( nW - 20, nH - 80 ) // Keep status bar at the bottom oStatusBar:SetPos( nH - 30, 0 ) oStatusBar:SetSize( nW, 30 ) return nil
Step 7: OnClose — Form Closing
Fires when the user tries to close the form (clicking the X button, pressing Alt+F4, or calling
Close()). Return .F. from the handler to prevent the form from closing — useful for
unsaved-changes prompts.
oForm:OnClose := { || OnFormClose( oForm ) } static function OnFormClose( oForm ) local nAnswer if lDataModified nAnswer := MsgYesNoCancel( "Save changes before closing?" ) do case case nAnswer == 1 // Yes SaveData() return .T. case nAnswer == 2 // No return .T. case nAnswer == 3 // Cancel return .F. // Prevent closing endcase endif return .T.
This is a powerful pattern for data-entry forms. Always give the user a way to force-close (e.g. the Cancel option) so they are never trapped in a form they cannot exit.
Step 8: OnCreate — Form Initialization
The OnCreate event fires after all child controls have been created and have their HWNDs.
This is useful for initialization that requires controls to be fully created, such as populating
listboxes or setting up data bindings.
// In class-based form format, the IDE automatically wires FormNCreate functions function Form1Create( oSelf ) // Populate listbox after controls are created oSelf:oList:SetItems( { "Item 1", "Item 2", "Item 3" } ) oSelf:oLblStatus:cText := "Ready" return nil
The IDE automatically detects functions named FormNCreate and wires them to the form's
::OnCreate event. This ensures initialization runs at the right time in the form lifecycle.
Event Parameters Summary
| Event | Parameters | Return | Controls |
|---|---|---|---|
OnClick |
(none) | ignored | Button, Label, Image, Panel |
OnChange |
(none) | ignored | TGet, ComboBox, CheckBox, Spinner |
OnKeyDown |
nKey, nFlags |
ignored | All focusable controls |
OnResize |
(none) | ignored | TForm |
OnClose |
(none) | .T. allow, .F. prevent |
TForm |
Code Block vs. Method Handler
HarbourBuilder supports two styles for event handlers:
Code block — best for short, inline logic:
oBtn:OnClick := { || oLabel:SetValue( Time() ) }
Method handler — best for complex logic that needs its own function:
oBtn:OnClick := { || OnBtnClick( oForm ) } static function OnBtnClick( oForm ) // Complex logic here: database calls, validation, etc. ... return nil
If the handler is more than one expression, use a separate function. It keeps your form definition clean and makes the logic easier to debug and test.
Now that you understand events, continue to the Database CRUD tutorial to build a data-driven application with SQLite.