YOUSEFUL / DELPHI / SAMPLE PROJECTS

Memory grid export

Console program Self-checking Product: Atlas Virtual Grid · Components: TAtlasMemoryData, TAtlasVirtualGrid

The quick start for Atlas Virtual Grid, and the project behind the tutorial. A console program that never shows a window:

Every step checks its own result and raises on a mismatch, so the exit code is 0 only when everything matched.

Build and run

Open MemoryGridExport.dpr in Delphi 13; the IDE creates the project file. Add the Atlas Virtual Grid src folder and the Atlas Plot src folder to the project's search path, then build and run for Win32 or Win64. No packages need to be installed. The program needs no input. It checks its own results and stops with an error if any of them differs from the output below.

Expected output

Example
1. Columns and rows
  ok: 6 rows, 6 columns
  ok: a row with a value that does not fit its column is rejected whole
2. Bind the grid and describe the columns
  ok: columns were created from the source
  ok: cells are formatted per column
  ok: totals: count 6, quantity sum 27, average price 229.21
3. Sort
  ok: ascending by customer; equal keys keep source order
  ok: descending by quantity; the null quantity comes last
4. Filter
  ok: 2 Fabrikam rows, still sorted
  ok: totals follow the filter
  ok: the filter reads the formatted date text: 3 rows shipped before 10 January
  ok: filter and sort removed
5. Edit through the data source
  ok: the grid follows the source: sum is now 32
  ok: text that is not a number is refused and the value kept
  ok: the read-only key column refuses writes
6. Selection text
  ok: tab-separated formatted rows, ready for the clipboard
7. Text output
Order,Customer,Shipped,Quantity,Unit price,Paid
1001,Contoso,2026-01-05,4,125.5,true
1002,Fabrikam,2026-01-06,12,40,false
1003,Northwind,2026-01-09,1,980,true
1004,Contoso,2026-01-12,5,125.5,false
1005,"Tailspin, Ltd",2026-01-15,7,64.25,true
1006,Fabrikam,,3,40,true

  ok: CSV carries raw values: invariant numbers, ISO dates, true/false
  ok: commas are quoted; nulls are empty
  ok: hidden columns are left out
  ok: HTML has the caption, bands and totals
8. Pictures and files
  ok: SVG of the visible part of the grid
  ok: orders.csv written to ...\MemoryGridExport
  ok: orders.html written to ...
  ok: orders.svg written to ...
  ok: orders.png written to ...
  ok: unknown extensions are refused
MemoryGridExport: all checks passed

orders.png shows two band headings (Shipment, Amount), right-aligned numbers and dates, check boxes for the Paid column and a footer with 6, 32 and 229.21.

Adapting it

Project files

Download all 34 projects

The ZIP contains every sample project with its README. See getting started for the search path to add.

Source

MemoryGridExport.dpr

pascal
program MemoryGridExport;

// Atlas Virtual Grid quick start: an in-memory data source, a grid bound to it,
// sorting, filtering, totals, programmatic edits and CSV/HTML/SVG/PNG output.
// No window is shown; the grid works without a parent. Every result is checked.

{$APPTYPE CONSOLE}

uses
  Winapi.Windows, System.SysUtils, System.Classes, System.IOUtils, Atlas.VGridData, Atlas.VGrid;

procedure Verify(Condition: Boolean; const What: string);
begin
  if not Condition then raise Exception.Create('Check failed: ' + What);
  Writeln('  ok: ', What);
end;

function Rejected(const Action: TProc): Boolean;
begin
  Result := False;
  try
    Action();
  except
    on EAtlasGridError do Result := True;
  end;
end;

procedure Run;
var
  Orders: TAtlasMemoryData;
  Grid: TAtlasVirtualGrid;
  Csv, Html, Svg: string;
  Line: string;
begin
  Orders := TAtlasMemoryData.Create(nil);
  Grid := TAtlasVirtualGrid.Create(nil);
  try
    Writeln('1. Columns and rows');
    Orders.AddColumn('Order', ckNumber, True);       // read-only key
    Orders.AddColumn('Customer');                    // ckText by default
    Orders.AddColumn('Shipped', ckDateTime);
    Orders.AddColumn('Quantity', ckNumber);
    Orders.AddColumn('Unit price', ckNumber);
    Orders.AddColumn('Paid', ckBoolean);
    Orders.BeginUpdate;                              // one change notification for the batch
    try
      // One invariant text per column: '.' decimals, ISO dates, yes/no. Empty text is null.
      Orders.AddRow(['1001', 'Contoso', '2026-01-05', '4', '125.50', 'yes']);
      Orders.AddRow(['1002', 'Fabrikam', '2026-01-06', '12', '40', 'no']);
      Orders.AddRow(['1003', 'Northwind', '2026-01-09', '1', '980', 'yes']);
      Orders.AddRow(['1004', 'Contoso', '2026-01-12', '', '125.50', 'no']);
      Orders.AddRow(['1005', 'Tailspin, Ltd', '2026-01-15', '7', '64.25', 'yes']);
      Orders.AddRow(['1006', 'Fabrikam', '', '3', '40', 'yes']);
    finally
      Orders.EndUpdate;
    end;
    Verify((Orders.RowCount = 6) and (Orders.ColumnCount = 6), '6 rows, 6 columns');
    Verify(Rejected(procedure begin Orders.AddRow(['1007', 'Adatum', '2026-02-01', 'twelve', '10', 'no']); end)
      and (Orders.RowCount = 6), 'a row with a value that does not fit its column is rejected whole');

    Writeln('2. Bind the grid and describe the columns');
    Grid.Data := Orders;                             // one grid column per data column
    Grid.Columns[0].Format := '0';
    Grid.Columns[0].Total := tkCount;
    Grid.Columns[1].Band := 'Shipment';
    Grid.Columns[2].Band := 'Shipment';
    Grid.Columns[2].Format := 'yyyy-mm-dd';
    Grid.Columns[3].Band := 'Amount';
    Grid.Columns[3].Total := tkSum;
    Grid.Columns[4].Band := 'Amount';
    Grid.Columns[4].Format := '#,##0.00';
    Grid.Columns[4].Total := tkAverage;
    Verify(Grid.Columns.Count = 6, 'columns were created from the source');
    Verify((Grid.CellText(1, 4) = 'Tailspin, Ltd') and (Grid.CellText(4, 0) = '125.50') and
      (Grid.CellText(5, 1) = 'No') and (Grid.CellText(3, 3) = ''), 'cells are formatted per column');
    Verify((Grid.TotalText(0) = '6') and (Grid.TotalText(3) = '27') and (Grid.TotalText(4) = '229.21'),
      'totals: count 6, quantity sum 27, average price 229.21');

    Writeln('3. Sort');
    Grid.Sort(1);                                    // by customer, ascending, ignoring case
    Verify((Grid.CellText(1, 0) = 'Contoso') and (Grid.DataRowOf(0) = 0) and (Grid.DataRowOf(1) = 3) and
      (Grid.CellText(1, 5) = 'Tailspin, Ltd'), 'ascending by customer; equal keys keep source order');
    Grid.Sort(3, True);                              // by quantity, descending
    Verify((Grid.CellText(3, 0) = '12') and (Grid.DataRowOf(5) = 3), 'descending by quantity; the null quantity comes last');

    Writeln('4. Filter');
    Grid.FilterText := 'fab';                        // any visible column, formatted text, ignoring case
    Verify((Grid.RowCount = 2) and (Grid.DataRowOf(0) = 1) and (Grid.DataRowOf(1) = 5), '2 Fabrikam rows, still sorted');
    Verify((Grid.TotalText(0) = '2') and (Grid.TotalText(3) = '15') and (Grid.TotalText(4) = '40.00'), 'totals follow the filter');
    Grid.FilterText := '2026-01-0';
    Verify(Grid.RowCount = 3, 'the filter reads the formatted date text: 3 rows shipped before 10 January');
    Grid.FilterText := '';
    Grid.Sort(-1);                                   // back to source order
    Verify((Grid.RowCount = 6) and (Grid.SortColumn = -1), 'filter and sort removed');

    Writeln('5. Edit through the data source');
    Orders.SetText(3, 3, '5', TFormatSettings.Invariant);
    Verify((Grid.CellText(3, 3) = '5') and (Grid.TotalText(3) = '32'), 'the grid follows the source: sum is now 32');
    Verify(Rejected(procedure begin Orders.SetText(3, 3, 'five', TFormatSettings.Invariant); end) and
      (Grid.CellText(3, 3) = '5'), 'text that is not a number is refused and the value kept');
    Verify(Rejected(procedure begin Orders.SetCell(0, 0, TAtlasCell.OfNumber(1)); end), 'the read-only key column refuses writes');

    Writeln('6. Selection text');
    Grid.SelectAll;
    Line := Grid.SelectionText;
    Verify((Grid.SelectedCount = 6) and Line.StartsWith('1001'#9'Contoso'#9'2026-01-05'#9'4'#9'125.50'#9'Yes'#13#10),
      'tab-separated formatted rows, ready for the clipboard');

    Writeln('7. Text output');
    Csv := Grid.ToCSV;
    Writeln(Csv);
    Verify(Csv.StartsWith('Order,Customer,Shipped,Quantity,Unit price,Paid'#13#10'1001,Contoso,2026-01-05,4,125.5,true'),
      'CSV carries raw values: invariant numbers, ISO dates, true/false');
    Verify((Pos('"Tailspin, Ltd"', Csv) > 0) and (Pos('1006,Fabrikam,,3,40,true', Csv) > 0), 'commas are quoted; nulls are empty');
    Grid.Columns[5].Visible := False;
    Verify(Grid.ToCSV.StartsWith('Order,Customer,Shipped,Quantity,Unit price'#13#10), 'hidden columns are left out');
    Grid.Columns[5].Visible := True;
    Html := Grid.ToHTML('Orders');
    Verify((Pos('<caption>Orders</caption>', Html) > 0) and (Pos('<th colspan="2">Amount</th>', Html) > 0) and
      (Pos('<tfoot>', Html) > 0) and (Pos('>229.21<', Html) > 0), 'HTML has the caption, bands and totals');

    Writeln('8. Pictures and files');
    Grid.ClearSelection;                             // pictures show the selection too
    Svg := Grid.ToSVG(800, 300);
    Verify((Pos('<svg', Svg) > 0) and (Pos('Northwind', Svg) > 0), 'SVG of the visible part of the grid');
    Grid.SaveToFile('orders.csv');                   // everything
    Grid.SaveToFile('orders.html');                  // everything, formatted, with totals
    Grid.SaveToFile('orders.svg', 800, 300);         // what is on screen
    Grid.SaveToFile('orders.png', 800, 300);
    for Line in ['orders.csv', 'orders.html', 'orders.svg', 'orders.png'] do
      Verify(TFile.Exists(Line) and (TFile.GetSize(Line) > 100), Line + ' written to ' + TDirectory.GetCurrentDirectory);
    Verify(Rejected(procedure begin Grid.SaveToFile('orders.xlsx'); end), 'unknown extensions are refused');
  finally
    Grid.Free;
    Orders.Free;
  end;
end;

begin
  // Formatting follows the global FormatSettings; fix them so the checks hold on every machine.
  FormatSettings := TFormatSettings.Invariant;
  try
    Run;
    Writeln('MemoryGridExport: all checks passed');
  except
    on E: Exception do
    begin
      Writeln('MemoryGridExport failed: ', E.ClassName, ': ', E.Message);
      ExitCode := 1;
    end;
  end;
end.