program GridQuickStart; // Atlas VCL quick start: a typed TAtlasTable shown in a TAtlasGrid, with a // filter box, header sorting, in-place editing, validated entry through // TAtlasValueEdit, row deletion and CSV export. The form is built in code; no // .dfm is needed. // // Run with a file name argument to save a PNG of the window instead of // showing it: GridQuickStart.exe window.png uses Winapi.Windows, System.SysUtils, System.Classes, System.IOUtils, Vcl.Forms, Vcl.Controls, Vcl.StdCtrls, Vcl.ExtCtrls, Atlas.Data, Atlas.Grid, Atlas.Editors, Atlas.TestVcl; type TMainForm = class(TForm) private FTable: TAtlasTable; FGrid: TAtlasGrid; FFilter: TEdit; FProduct: TEdit; FQuantity: TAtlasValueEdit; FPrice: TAtlasValueEdit; FRestock: TAtlasValueEdit; FAvailable: TCheckBox; FStatus: TLabel; procedure AddLabel(Parent: TWinControl; Left, Top: Integer; const Caption: string); procedure AddButton(Parent: TWinControl; Left, Top: Integer; const Caption: string; OnClick: TNotifyEvent); function AddValueEdit(Parent: TWinControl; Left, Width: Integer; Kind: TAtlasColumnKind; Required: Boolean): TAtlasValueEdit; function Invalid(Edit: TAtlasValueEdit; const Name: string): Boolean; procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); procedure FilterChanged(Sender: TObject); procedure AddClicked(Sender: TObject); procedure DeleteClicked(Sender: TObject); procedure ExportClicked(Sender: TObject); procedure ClearSortClicked(Sender: TObject); procedure TableChanged(Sender: TObject); public constructor Create(AOwner: TComponent); override; end; constructor TMainForm.Create(AOwner: TComponent); var Toolbar, Footer: TPanel; begin inherited CreateNew(AOwner); Caption := 'Atlas VCL: grid quick start'; Width := 900; Height := 560; Position := poScreenCenter; KeyPreview := True; // FormKeyDown sees Enter and Escape before the grid editor OnKeyDown := FormKeyDown; Toolbar := TPanel.Create(Self); Toolbar.Parent := Self; Toolbar.Align := alTop; Toolbar.Height := 108; Toolbar.BevelOuter := bvNone; AddLabel(Toolbar, 16, 17, 'Filter'); FFilter := TEdit.Create(Self); FFilter.Parent := Toolbar; FFilter.SetBounds(56, 14, 260, 24); FFilter.OnChange := FilterChanged; AddButton(Toolbar, 332, 11, 'Clear sort', ClearSortClicked); AddButton(Toolbar, 448, 11, 'Delete row', DeleteClicked); AddButton(Toolbar, 564, 11, 'Export CSV', ExportClicked); AddLabel(Toolbar, 16, 50, 'Product'); AddLabel(Toolbar, 232, 50, 'Quantity'); AddLabel(Toolbar, 338, 50, 'Unit price'); AddLabel(Toolbar, 444, 50, 'Restock (YYYY-MM-DD)'); FProduct := TEdit.Create(Self); FProduct.Parent := Toolbar; FProduct.SetBounds(16, 68, 200, 24); FQuantity := AddValueEdit(Toolbar, 232, 90, ackNumber, True); FPrice := AddValueEdit(Toolbar, 338, 90, ackNumber, True); FRestock := AddValueEdit(Toolbar, 444, 110, ackDate, False); FAvailable := TCheckBox.Create(Self); FAvailable.Parent := Toolbar; FAvailable.SetBounds(570, 70, 90, 20); FAvailable.Caption := 'Available'; FAvailable.Checked := True; AddButton(Toolbar, 676, 65, 'Add row', AddClicked); Footer := TPanel.Create(Self); Footer.Parent := Self; Footer.Align := alBottom; Footer.Height := 32; Footer.BevelOuter := bvNone; FStatus := TLabel.Create(Self); FStatus.Parent := Footer; FStatus.SetBounds(16, 8, 860, 20); // The schema is fixed before the first row is added. FTable := TAtlasTable.Create(Self); FTable.AddColumn('Product', ackText, 260); FTable.AddColumn('Quantity', ackNumber, 110); FTable.AddColumn('Unit price', ackNumber, 110); FTable.AddColumn('Available', ackBoolean, 100); FTable.AddColumn('Restock', ackDate, 120); FTable.OnChange := TableChanged; FGrid := TAtlasGrid.Create(Self); FGrid.Parent := Self; FGrid.Align := alClient; FGrid.AlignWithMargins := True; FGrid.Margins.SetBounds(16, 8, 16, 8); FGrid.Table := FTable; FTable.ImportCSV( 'Product,Quantity,Unit price,Available,Restock' + sLineBreak + 'Field notebook,120,4.75,true,2026-10-03' + sLineBreak + 'Drafting pencil,300,1.2,true,' + sLineBreak + 'Steel ruler,45,6.5,false,2026-11-15' + sLineBreak + '"Ink, archival",80,12,true,2026-10-20' + sLineBreak); end; procedure TMainForm.AddLabel(Parent: TWinControl; Left, Top: Integer; const Caption: string); var Text: TLabel; begin Text := TLabel.Create(Self); Text.Parent := Parent; Text.SetBounds(Left, Top, 180, 16); Text.Caption := Caption; end; procedure TMainForm.AddButton(Parent: TWinControl; Left, Top: Integer; const Caption: string; OnClick: TNotifyEvent); var Button: TButton; begin Button := TButton.Create(Self); Button.Parent := Parent; Button.SetBounds(Left, Top, 100, 30); Button.Caption := Caption; Button.OnClick := OnClick; end; function TMainForm.AddValueEdit(Parent: TWinControl; Left, Width: Integer; Kind: TAtlasColumnKind; Required: Boolean): TAtlasValueEdit; begin Result := TAtlasValueEdit.Create(Self); Result.Parent := Parent; Result.SetBounds(Left, 68, Width, 24); Result.ValueKind := Kind; Result.Required := Required; end; procedure TMainForm.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin // In this baseline the grid's first Enter only closes the in-place editor, // and Escape closes it without discarding the text. Make both keys act at once. if (ActiveControl <> FGrid) or not FGrid.EditorMode then Exit; if Key = VK_RETURN then begin FGrid.EditorMode := False; // hand the editor text to the grid FGrid.CommitEdit; // validate and store it, or raise EAtlasDataError end else if Key = VK_ESCAPE then begin FGrid.CancelEdit; // closing the editor keeps its text pending... FGrid.CancelEdit; // ...so a second call discards it Key := 0; end; end; procedure TMainForm.FilterChanged(Sender: TObject); begin FGrid.CommitEdit; // a pending edit must land before the view changes FTable.SetFilter(FFilter.Text); end; procedure TMainForm.ClearSortClicked(Sender: TObject); begin FGrid.CommitEdit; FTable.Sort(-1); end; function TMainForm.Invalid(Edit: TAtlasValueEdit; const Name: string): Boolean; begin Result := not Edit.Validate; if Result then FStatus.Caption := Name + ': ' + Edit.ValidationMessage; end; procedure TMainForm.AddClicked(Sender: TObject); const Flags: array[Boolean] of string = ('false', 'true'); begin if Trim(FProduct.Text) = '' then begin FStatus.Caption := 'Product: a name is required'; Exit; end; if Invalid(FQuantity, 'Quantity') or Invalid(FPrice, 'Unit price') or Invalid(FRestock, 'Restock') then Exit; FGrid.CommitEdit; FTable.AddRow([Trim(FProduct.Text), FQuantity.NormalizedValue, FPrice.NormalizedValue, Flags[FAvailable.Checked], FRestock.NormalizedValue]); FProduct.Clear; FQuantity.Clear; FPrice.Clear; FRestock.Clear; end; procedure TMainForm.DeleteClicked(Sender: TObject); begin // Grid row 0 is the header; table rows are zero-based view indices. if (FGrid.Row >= 1) and (FGrid.Row <= FTable.RowCount) then begin FGrid.CancelEdit; FTable.DeleteRow(FGrid.Row - 1); end; end; procedure TMainForm.ExportClicked(Sender: TObject); var FileName: string; begin FGrid.CommitEdit; FileName := TPath.Combine(TPath.GetDirectoryName(ParamStr(0)), 'inventory.csv'); TFile.WriteAllText(FileName, FTable.ExportCSV, TEncoding.UTF8); FStatus.Caption := 'Wrote the visible rows to ' + FileName; end; procedure TMainForm.TableChanged(Sender: TObject); var SortText: string; begin if FTable.SortColumn < 0 then SortText := 'unsorted' else if FTable.Ascending then SortText := 'sorted by ' + FTable.Columns[FTable.SortColumn].Name + ' ascending' else SortText := 'sorted by ' + FTable.Columns[FTable.SortColumn].Name + ' descending'; FStatus.Caption := Format('%d of %d rows shown, %s. Click a header to sort; ' + 'Enter stores an edit, Escape discards it.', [FTable.RowCount, FTable.SourceRowCount, SortText]); end; var Form: TMainForm; begin Application.Initialize; Application.MainFormOnTaskbar := True; Application.CreateForm(TMainForm, Form); if ParamCount > 0 then SaveFormImage(Form, ParamStr(1)) else Application.Run; end.