TControl

Core Class Inherits TWindow source/classes/control.prg

TControl is the abstract base class for all UI controls in FiveWin. It inherits from TWindow and adds control-specific behavior: data binding via bSetGet, change notifications, drag-and-drop support, docking/anchoring within parent windows, design-mode resizing, and the REDEFINE pattern for resource-based dialogs. You never instantiate TControl directly; instead, you use its concrete subclasses like TButton, TGet, TXBrowse, etc.

Control Hierarchy

TControl is the common ancestor of every visual control. Here is the inheritance tree showing the major subclasses:

classDiagram class TControl { +oWnd +nId +bSetGet +bChange +lUpdate +lDrag +lTransparent +Refresh() +SetFocus() +AdjClient() +VarGet() +VarPut() } TControl <|-- TButton TControl <|-- TBtnBmp TControl <|-- TGet TControl <|-- TMGet TControl <|-- TSay TControl <|-- TXBrowse TControl <|-- TFolder TControl <|-- TComboBox TControl <|-- TCheckBox TControl <|-- TRadio TControl <|-- TScrollBar TControl <|-- TTreeView TControl <|-- TListBox TControl <|-- TProgress TControl <|-- TRichEdit TControl <|-- TSplitter

Key DATA Members

DATATypeDefaultDescription
oWndTWindow/TDialognilParent window or dialog that contains this control.
nIdNumericautoWin32 control ID. Auto-assigned from CLASSDATA nInitID (starts at 100) or explicitly set for resource dialogs.
bSetGetBlocknilData-binding block. When called with no args, returns the current value; with one arg, sets it. Created automatically by the GET / VAR command clauses.
bChangeBlocknilEvaluated whenever the control's value changes (typed text, selection change, etc.).
lUpdateLogical.F.If .T., the control participates in the parent's update cycle (oWnd:Update()). Used for data-bound controls that must refresh when the record pointer moves.
lDragLogical.F.Enable mouse drag-and-drop for this control. Used in design mode and for user-initiated dragging.
lTransparentLogical.F.Paint the control with a transparent background, letting the parent's brush/gradient show through.
lCapturedLogical.F.Whether the control currently has mouse capture.
lMouseDownLogical.F.Tracks whether the left mouse button is pressed.
nDlgCodeNumericnilValue returned by GetDlgCode(). Controls tab/arrow key navigation behavior.
oJumpTControlnilAfter VALID succeeds, focus jumps to this control instead of the next tab-order control.
nClientBevelNumericnilBevel/border offset for AdjClient() docking.
aSizeRectArraynilStores original size rectangle for proportional resizing.
ResizeTypeNumericnilResize behavior flag set by SetResizeType(). Controls how the control scales when its parent is resized.
uOriginalValueAnynilOriginal value at control creation. Used by lChanged access method.
LastValidValueAnynilStores the last value that passed validation.

Access / Assign Methods

AccessDescription
lChangedRead-only. Returns .T. if the current value (from VarGet()) differs from uOriginalValue.
nTop( nNewTop )SETGET — gets or sets the top coordinate, moving the Win32 window if it exists.
nLeft( nNewLeft )SETGET — gets or sets the left coordinate.

Key Methods

Refresh()

Inherited from TWindow. Forces the control to repaint. For data-bound controls (those with bSetGet), it also re-reads the value from the bound variable.

SetFocus()

Gives keyboard focus to this control. Internally sends FM_CHANGEFOCUS and calls the Win32 SetFocus() API.

VarGet() / VarPut()

METHOD VarGet() INLINE If( ValType( ::bSetGet ) == "B", Eval( ::bSetGet ), )
METHOD VarPut( uVal ) INLINE If( ValType( ::bSetGet ) == "B", Eval( ::bSetGet, uVal ), )

Read or write the control's bound variable through the bSetGet block. This is the standard way to programmatically get/set a control's value.

cToChar()

METHOD cToChar( cCtrlClass )

Generates source code that recreates this control. Used by the visual designer to serialize dialogs.

Move()

METHOD Move( nTop, nLeft, nWidth, nHeight, lRepaint )

Repositions and/or resizes the control within its parent.

End()

Destroys the control's Win32 window and removes it from the parent's aControls array.

Initiate()

METHOD Initiate( hWnd )

Called automatically by the parent after the control's Win32 window is created. It subclasses the window procedure to intercept control messages and completes the control's initialization. Not typically called directly.

CheckDots() / ShowDots() / HideDots()

METHOD CheckDots( hWnd, nId )
METHOD ShowDots()
METHOD HideDots()

Design-mode helper methods that manage the eight resize handles (dots) around the control border. ShowDots() displays the handles; HideDots() removes them; CheckDots() is called during MResize() to adjust handle positions. These are used by the visual dialog designer.

Docking and Anchoring

TControl provides docking methods that automatically position a control to fill specific regions of the parent window. These are essential for building resizable layouts:

flowchart TD subgraph Parent["Parent Window (TWindow / TDialog)"] T["oTop — AdjTop()
Docked to top edge"] L["oLeft — AdjLeft()
Docked to left edge"] C["oClient — AdjClient()
Fills remaining space"] R["oRight — AdjRight()
Docked to right edge"] B["oBottom — AdjBottom()
Docked to bottom edge"] end T --- C L --- C C --- R C --- B

AdjClient()

METHOD AdjClient()

Resizes the control to fill the entire client area of the parent, minus any space taken by docked controls (oTop, oBottom, oLeft, oRight) and the message bar. This is the most commonly used docking method.

Internally, AdjClient() inspects the parent's oTop, oBar, oBottom, oMsgBar, oLeft, and oRight properties to calculate the remaining rectangle, then calls WndAdjClient() to position the control.

AdjTop() / AdjBottom()

Dock the control to the top or bottom edge of the parent. The control stretches the full width, and its height remains as defined.

AdjLeft() / AdjRight()

Dock the control to the left or right edge. The control stretches the full height (between any top/bottom docked controls), and its width remains as defined.

Docking in Practice

#include "FiveWin.ch"

function Main()

   local oWnd, oTree, oBrw, oBottom

   DEFINE WINDOW oWnd TITLE "Docking Layout"

   // Toolbar docks automatically to top via oWnd:oBar
   DEFINE BUTTONBAR oBar OF oWnd SIZE 48, 48 2007
   DEFINE BUTTON OF oBar RESOURCE "OPEN" ACTION nil

   // Status bar docks automatically to bottom via oWnd:oMsgBar
   SET MESSAGE OF oWnd TO "Ready" CENTERED

   // Left panel: tree view
   oTree := TTreeView():New( 0, 0, oWnd, , 220, 400 )
   oWnd:oLeft := oTree
   oTree:AdjLeft()

   // Client area: browse control fills the rest
   oBrw := TXBrowse():New( oWnd )
   oBrw:SetArray( GetCustomerData() )
   oBrw:CreateFromCode()
   oWnd:oClient := oBrw
   oBrw:AdjClient()

   ACTIVATE WINDOW oWnd MAXIMIZED ;
      ON RESIZE ( oTree:AdjLeft(), oBrw:AdjClient() )

return nil

The key pattern: assign the control to the parent's docking slot (oWnd:oLeft, oWnd:oClient), then call the corresponding Adj*() method. In the ON RESIZE handler, call them again so the layout adapts.

Data Binding with bSetGet

The bSetGet code block is the heart of FiveWin's data binding. It follows the xBase getter/setter convention:

// When creating a GET, FiveWin generates this block automatically:
local cName := Space( 30 )

@ 10, 10 GET oGet VAR cName OF oDlg   // Internally creates:
// oGet:bSetGet := { |u| If( u == nil, cName, cName := u ) }

// Reading the value:
? oGet:VarGet()       // returns cName

// Writing a value:
oGet:VarPut( "John" ) // sets cName := "John"
oGet:Refresh()        // updates the visual display

For database-bound controls, bSetGet references a field:

@ 10, 10 GET oGet VAR Customers->Name OF oDlg
// bSetGet := { |u| If( u == nil, Customers->Name, Customers->Name := u ) }

Example: Creating a Custom Control

You can subclass TControl to create custom controls with specialized painting and behavior:

#include "FiveWin.ch"

// A simple LED indicator control

CLASS TLed FROM TControl

   DATA lOn       INIT .F.     // LED state
   DATA nClrOn    INIT CLR_GREEN
   DATA nClrOff   INIT CLR_RED
   DATA nRadius   INIT 12

   METHOD New( nTop, nLeft, oWnd, nWidth, nHeight, lOn )
   METHOD Paint()
   METHOD Toggle() INLINE ::lOn := !::lOn, ::Refresh()

ENDCLASS

METHOD New( nTop, nLeft, oWnd, nWidth, nHeight, lOn ) CLASS TLed

   DEFAULT nWidth := 30, nHeight := 30, lOn := .F.

   ::nTop    := nTop
   ::nLeft   := nLeft
   ::nBottom := nTop + nHeight
   ::nRight  := nLeft + nWidth
   ::oWnd    := oWnd
   ::lOn     := lOn

   ::nStyle := nOR( WS_CHILD, WS_VISIBLE )

   if oWnd != nil
      ::Create( "TLed" )
      oWnd:AddControl( Self )
   endif

return Self

METHOD Paint() CLASS TLed

   local hDC  := ::BeginPaint()
   local aRect := ::GetCliRect()
   local nCx  := ( aRect[4] - aRect[2] ) / 2
   local nCy  := ( aRect[3] - aRect[1] ) / 2
   local nClr := If( ::lOn, ::nClrOn, ::nClrOff )

   // Fill background
   FillSolidRect( hDC, aRect, GetSysColor( COLOR_BTNFACE ) )

   // Draw the LED circle
   FW_FillCircle( hDC, nCy, nCx, ::nRadius, nClr )

   // Draw border
   FW_Circle( hDC, nCy, nCx, ::nRadius + 1, CLR_BLACK )

   ::EndPaint()

return 0

Usage:

function TestLed()

   local oDlg, oLed1, oLed2

   DEFINE DIALOG oDlg TITLE "LED Demo" SIZE 300, 200 TRUEPIXEL

   oLed1 := TLed():New( 30, 40, oDlg, 36, 36, .T. )   // ON
   oLed2 := TLed():New( 30, 90, oDlg, 36, 36, .F. )   // OFF

   @ 30, 140 SAY "Server"  OF oDlg SIZE 80, 22 TRUEPIXEL
   @ 30, 190 SAY "Offline" OF oDlg SIZE 80, 22 TRUEPIXEL

   @ 100, 40 BUTTON "Toggle" OF oDlg SIZE 100, 30 TRUEPIXEL ;
      ACTION ( oLed1:Toggle(), oLed2:Toggle() )

   ACTIVATE DIALOG oDlg CENTER

return nil

Design Mode

TControl includes built-in support for visual design mode (used by FiveWin's dialog editor). When lDrag is set to .T.:

The REDEFINE Pattern

For resource-based dialogs, controls are created by the dialog resource template. FiveWin's REDEFINE commands bind FiveWin objects to those existing Win32 controls by their numeric ID:

METHOD Redefine( nId, oWnd ) CONSTRUCTOR
DEFINE DIALOG oDlg RESOURCE "MYDIALOG"

   REDEFINE GET oGet VAR cName ID 101 OF oDlg
   REDEFINE BUTTON oBtn ID 102 OF oDlg ACTION SaveData()
   REDEFINE COMBOBOX oCbx VAR cCity ID 103 OF oDlg ;
      ITEMS { "NYC", "London", "Tokyo" }

ACTIVATE DIALOG oDlg CENTER

Control Lifecycle

flowchart LR A["New() or Redefine()
Object created"] --> B["Parent Activate()
Win32 controls created"] B --> C["Initiate()
Subclass window proc"] C --> D["Default()
Set initial value"] D --> E["User interaction
bChange, bValid, bWhen"] E --> F{"Dialog closes
or End()"} F --> G["Destroy()
Win32 window destroyed"]

Usage Tips

Veja Também