Form Building

One Control, One Pattern

GPALControl: The Shared Base

Every control type -- button, input, checkbox, combo box, data grid, chart, and the rest -- derives from a shared base that provides the settings common to all of them: the visible label, an identifying name for logs, an enabled state, a tooltip, and an event handler. Each specific control type then adds only what is unique to it -- a checked state for checkboxes, a placeholder for text inputs, chart-specific options for charts. The shared settings are the same fluent calls no matter which control you are building.

// WithName, WithEnabled, WithCallback work the same on every control type

GPALButton saveButton = GPAL.ButtonFor("Save")

.WithName("SaveButton")

.WithEnabled(true)

.WithCallback<EventHandler>(SaveButton_Click)

.ForEvent(ControlEventType.Click)

.ToGPALObject();


var agreeCheckbox = GPAL.CheckboxFor("I agree to the terms");

NOTE

A callback that writes to its own control raises that control's event again, which calls the callback again, which writes again. It is the loop every form ends up guarding with a flag of its own, and GPAL watches for it so you do not have to: a callback entered while it is still running is reported as a WARNING naming the control. Nothing is blocked and nothing is refused, so a workflow that means to set a combo box from its own SelectedIndexChanged still can. On a handler attached with WithCallbackOffUIThread the same loop shows up as a queue that keeps growing rather than a callback inside itself, and GPAL names that instead.

Assembling a Form

GPAL.Form starts the form chain. WithTitle, WithWidth, WithHeight, WithLeft, and WithTop set up the window itself; leave out WithHeight and the form takes the height its content needs. Each control is passed to WithFormControl, which can be called repeatedly to add controls one at a time, and GPAL.SpacerFor(pixels) between two of them leaves a gap of that height. The form does not need to know what type each control is -- it arranges any GPALControl it receives.

var form = GPAL.Form

.WithTitle("Customer Entry")

.WithWidth(600)

.WithHeight(400)

.WithFormControl(GPAL.InputFor("Customer Name"))

.WithFormControl(GPAL.InputFor("Email Address"))

.WithFormControl(GPAL.ButtonFor("Submit"))

.ToGPALObject();

Picking the Right Control

Every control has a shorthand constructor on GPAL -- ButtonFor, InputFor, CheckboxFor, ComboBoxFor, and so on -- that returns the concrete type directly, no cast required. That typed variable is what you wire up: attach callbacks, set properties at runtime, and read values back after the form closes. GPAL covers every common need: single and multi-line text, yes/no and one-of-many choices, lists, grids, tree views, date and number pickers, file selectors, progress bars, status strips, and layout panels.

WARNING

Passing a control directly to WithFormControl without assigning it to a variable first creates a display-only control -- there is no reference to attach a callback to or read a value from after the form closes. Use the shorthand inline only for controls you never need to interact with in code.

💬 Ask GPAL