Tutorials

Tutorials

Building a Form: Controls, FillInFrom, and CallAfterFillIn

GPALForm lets you build a real Windows Form out of fluent GPAL control definitions, then drive it from a data file. This tutorial builds a small form with a label, buttons, radio buttons, and a checkbox, fills it from a GPALFile row by row with FillInFrom, pops up an approval dialog after each row via CallAfterFillIn, and shows the difference between Show() and ShowDialog().

Complete Program

Here's the whole workflow, start to finish. Each piece is broken down and explained below.

using GenerallyPositive;

using GenerallyPositive.GPALForm;

using static GenerallyPositive.GPALForm.GPALForm;

using static GenerallyPositive.Enums;

using System.Windows.Forms;

using Keys = System.Windows.Forms.Keys;

static GPALButton startButton = GPAL.Button

.WithName("button1")

.WithText("Start The Workflow")

.WithCallback<EventHandler>(StartButtonClickHandler)

.WithShortcutKey(Keys.Alt | Keys.S)

.ToGPALObject();

static GPALRadioButton chromeRadio = GPAL.RadioButton

.WithChecked(true)

.WithName("Radiobutton1")

.WithText("Chrome")

.ToGPALObject();

static GPALRadioButton edgeRadio = GPAL.RadioButton

.WithName("Radiobutton2")

.WithText("Edge")

.ToGPALObject();

static GPALCheckbox overwriteCheckbox = GPAL.Checkbox

.WithText("Overwrite")

.ToGPALObject();

static GPALLabel titleLabel = GPAL.Label

.WithText("Form Tester")

.ToGPALObject();

// approval popup shown after each row is filled in

static GPALLabel approvalLabel = GPAL.Label.WithText("Approve this entry?").ToGPALObject();

static GPALInput approvalReason = GPAL.Input.WithText("").ToGPALObject();

static GPALButton approveButton = GPAL.Button.WithName("ApproveButton").WithText("Approved").WithCallback<EventHandler>(ApproveButtonClickHandler).ToGPALObject();

static GPALButton notApproveButton = GPAL.Button.WithName("NotApproveButton").WithText("Not Approved").WithCallback<EventHandler>(NotApproveButtonClickHandler).ToGPALObject();

static bool approvalResult = false;

static GPALForm approvalForm = GPAL.Form

.WithTitle("Approval")

.WithLeft(1500).WithTop(400).WithWidth(300).WithHeight(180)

.WithFormControl(approvalLabel)

.WithFormControl(approvalReason)

.WithFormControl(approveButton)

.WithFormControl(notApproveButton)

.ToGPALObject();

static GPALFile inputFile = ((GPALFile)@"c:sdiebay.words.txt").WithDelimiter(',').ToGPALObject();

[STAThread]

static void Main(string[] args)

{

GPALForm myForm = GPAL.Form

.WithTitle("Simple Form Test")

.WithLeft(1500).WithTop(100).WithWidth(350).WithHeight(700)

.WithFormControl(titleLabel)

.WithFormControl(startButton)

.WithFormControl(chromeRadio)

.WithFormControl(edgeRadio)

.WithFormControl(overwriteCheckbox)

.CallAfterFillIn(CallAfterFillIn)

.ToGPALObject();

myForm

.Show()

.FillInFrom(inputFile)

.ShowDialog();

}

public static CallIfStatus CallAfterFillIn(GPALForm myForm, IGPALGrid<string> tokens, int tokenIdx)

{

((TextBox)approvalReason.WindowsControl).Text = "";

approvalForm

.WithLeft(myForm.Left + myForm.Width)

.WithTop(myForm.Top)

.ShowDialog();

return CallIfStatus.NotHandled;

}

private static void ApproveButtonClickHandler(object sender, EventArgs e)

{

approvalResult = true;

CloseParentForm(sender);

}

private static void NotApproveButtonClickHandler(object sender, EventArgs e)

{

approvalResult = false;

CloseParentForm(sender);

}

private static void CloseParentForm(object sender)

{

Control control = (Control)sender;

while (null != control.Parent)

control = control.Parent;

control.Hide();

}

private static void StartButtonClickHandler(object sender, EventArgs e)

{

// kick off the automation workflow using the values chosen on the form

}

Define Controls with One Pattern Each

Every control follows the same shape: start from GPAL.Button, GPAL.RadioButton, GPAL.Checkbox, GPAL.Label, and so on, chain With* calls to configure it, then finish with ToGPALObject(). WithCallback wires up an event handler, WithShortcutKey adds an Alt-key accelerator, and WithChecked/WithText set initial state. The same pattern applies whether you're building a button, a combo box, a chart, or a tree view.

static GPALButton startButton = GPAL.Button

.WithName("button1")

.WithText("Start The Workflow")

.WithCallback<EventHandler>(StartButtonClickHandler)

.WithShortcutKey(Keys.Alt | Keys.S)

.ToGPALObject();

static GPALRadioButton chromeRadio = GPAL.RadioButton

.WithChecked(true)

.WithName("Radiobutton1")

.WithText("Chrome")

.ToGPALObject();

static GPALCheckbox overwriteCheckbox = GPAL.Checkbox

.WithText("Overwrite")

.ToGPALObject();

TIP

Because every control is a real object (gPalButton1, gPalRadioButton1, etc), you can read and write its state (Enabled, Checked, Text) from anywhere in the program - including from event handlers on other controls - using simple write-through property setters.

Assemble the Form

GPAL.Form is itself a fluent builder. WithFormControl adds each control in the order you want it laid out, and ToGPALObject() produces the GPALForm you'll show. CallAfterFillIn registers a callback that fires once per row when the form is later driven from a data file.

GPALForm myForm = GPAL.Form

.WithTitle("Simple Form Test")

.WithLeft(1500).WithTop(100).WithWidth(350).WithHeight(700)

.WithFormControl(titleLabel)

.WithFormControl(startButton)

.WithFormControl(chromeRadio)

.WithFormControl(edgeRadio)

.WithFormControl(overwriteCheckbox)

.CallAfterFillIn(CallAfterFillIn)

.ToGPALObject();

Show, FillInFrom, and ShowDialog

Show() makes the form visible but non-blocking, which gives FillInFrom something to update on screen as it works. FillInFrom(inputFile) walks the GPALFile row by row, populating matching controls from each row's values and firing CallAfterFillIn after each row. ShowDialog() then blocks until the user closes the form, the same way a normal modal dialog would.

myForm

.Show()

.FillInFrom(inputFile)

.ShowDialog();

TIP

Show() before ShowDialog() is only useful when something between them - like FillInFrom here - needs the form visible but non-blocking first. If you don't need that, just call ShowDialog() directly.

CallAfterFillIn: React to Each Row

CallAfterFillIn receives the form, the full grid of tokens from the data file, and the index of the row just filled in. Here it pops up a small modal approval form next to the main form, positioned using the main form's Left/Top/Width so it doesn't overlap. Returning CallIfStatus.NotHandled lets GPAL continue its normal fill-in processing for the next row.

public static CallIfStatus CallAfterFillIn(GPALForm myForm, IGPALGrid<string> tokens, int tokenIdx)

{

((TextBox)approvalReason.WindowsControl).Text = "";

approvalForm

.WithLeft(myForm.Left + myForm.Width)

.WithTop(myForm.Top)

.ShowDialog();

return CallIfStatus.NotHandled;

}

WARNING

Calling ShowDialog() from inside CallAfterFillIn pauses the fill-in loop on that row until the approval form is closed. That's the point here - it forces a human decision per row - but be aware it turns an otherwise automatic loop into a step-through workflow.