YOUSEFUL / DELPHI / COMPONENTS

TAtlasDBGrid

A data-aware grid with validated typed edits that never post on their own, CSV export of visible or selected rows, and a row height that follows the monitor DPI.

Visual control Palette: Atlas Data Unit Atlas.DBControls · Inherits TDBGrid · Product Atlas Info

Description

TAtlasDBGrid is a TDBGrid descendant. Bind it to a TDataSource as usual; Columns, Options, ReadOnly, SelectedRows and every other standard grid member work unchanged and are not repeated here.

What it adds:

The constructor adds dgMultiSelect and dgAlwaysShowSelection to Options, so rows can be selected with Ctrl and Shift clicks, and sets DefaultDrawing to False. Cells are filled with the window and highlight colours of the active VCL style and then drawn with DefaultDrawColumnCell; an OnDrawColumnCell handler runs after that. The column's font name, size and style are used, but column colours and font colours are replaced by the style colours.

Atlas Info adds no column classes, events, sorting, grouping, footers or printing to the grid: columns are the standard TColumn objects.

The grid's own checks raise EAtlasDatabaseError. Invalid numbers, Booleans and dates are reported by the Atlas VCL unit Atlas.Data with EAtlasDataError, and an unknown field name by Data.DB with EDatabaseError.

Members

MemberKindWhat it does
CreateconstructorCreates the grid with LogicalRowHeight 28, records the control's current PPI, sets DefaultRowHeight to ScaledRowHeight, adds dgMultiSelect and dgAlwaysShowSelection to Options and sets DefaultDrawing to False.
EditValuemethodWrites Text to the field FieldName of the dataset's current record, converted to the field's type.
ExportCSVmethodReturns the dataset's records as CSV text.
ScaledRowHeightmethodReturns LogicalRowHeight scaled to the PPI the grid was created at, or the PPI of the last DPI change: MulDiv(LogicalRowHeight, PPI, 96).
LogicalRowHeightpropertyRow height in pixels at 96 PPI.

Constructors

Create constructor

constructor Create(AOwner: TComponent); override;

Creates the grid with LogicalRowHeight 28, records the control's current PPI, sets DefaultRowHeight to ScaledRowHeight, adds dgMultiSelect and dgAlwaysShowSelection to Options and sets DefaultDrawing to False.

pascal
Grid := TAtlasDBGrid.Create(Form);
Grid.Parent := Form;
Grid.DataSource := CustomerSource;

Properties

LogicalRowHeight property

property LogicalRowHeight: Integer read FLogicalRowHeight write SetLogicalRowHeight default 28;

Row height in pixels at 96 PPI. Default 28. Values outside 18..120 raise EAtlasDatabaseError (Logical row height must be 18..120). Setting it updates DefaultRowHeight to ScaledRowHeight and repaints the grid.

Set this property rather than DefaultRowHeight: the grid overwrites DefaultRowHeight on the next layout or DPI change.

pascal
Grid.LogicalRowHeight := 22;  // denser rows

Methods

EditValue method

procedure EditValue(const FieldName, Text: string);

Writes Text to the field FieldName of the dataset's current record, converted to the field's type. The record is never posted.

These checks run before the record is touched, so a refused value leaves the dataset state unchanged:

  1. DataSource has an active dataset (An active data source is required).
  2. The field exists (EDatabaseError from FieldByName).
  3. The grid is not ReadOnly, the dataset CanModify, and the field is a data field (not calculated or lookup) that is not ReadOnly (Field is read-only: <name>).
  4. The text converts to the field type:
    • empty text sets the field to Null, unless the field is Required (A value is required: <name>);
    • integer fields (ftSmallint, ftInteger, ftWord, ftAutoInc, ftLargeint, ftShortint, ftByte, ftLongWord) take integer text and are range-checked for their type (Invalid integer, Integer out of range, Word out of range and so on);
    • ftFloat, ftCurrency, ftBCD, ftFMTBcd, ftExtended and ftSingle take invariant number text with a . decimal separator, such as 1250.75 or 1e3; text such as 1,5 or abc, NaN and infinity raise EAtlasDataError (Invalid finite number: ...);
    • ftBoolean takes true, false, 1 or 0, in any letter case;
    • ftDate takes YYYY-MM-DD;
    • string fields take text up to the field's Size (Text exceeds field size); ftMemo and ftWideMemo take any text;
    • every other type, including ftDateTime, ftTime, ftTimeStamp and blobs, raises Use a typed dataset field API for this field type.

If the dataset was browsing, EditValue calls Edit, and cancels that edit again if assigning the value fails. If the dataset was already in edit or insert state it stays there, so several fields can be set before one Post.

pascal
Grid.EditValue('Balance', '1250.75');  // Customers.State is now dsEdit
Customers.Post;                        // or Customers.Cancel

ExportCSV method

function ExportCSV(SelectedOnly: Boolean = False): string;

Returns the dataset's records as CSV text. Nothing is written to disk; save the string yourself.

  • Columns: every Columns item that is Visible and bound to a field, in column order. The header row uses each field's DisplayLabel. With the default columns, setting a column's Title.Caption changes that DisplayLabel; a caption set on a customized column is not used.
  • Rows: with SelectedOnly False, every record from First to Eof, so an active dataset filter, including TAtlasDataFilter, limits the export. With SelectedOnly True, the records in SelectedRows. That list is sorted by bookmark, so rows come out in dataset order, not in the order they were selected. An empty selection gives the header only.
  • Values: Null is an empty field. Floating-point and BCD values use invariant FloatToStr (1200.5), Booleans are true or false, ftDate is YYYY-MM-DD, and ftDateTime and ftTimeStamp are YYYY-MM-DDThh:nn:ss.zzz. Everything else is the field's AsString. A value containing a comma, quote, CR or LF is enclosed in quotes, with inner quotes doubled.
  • Every line, including the last, ends with CR LF.

The dataset must be active and browsing (Post or cancel before exporting) and bidirectional (Export requires a bidirectional dataset). At least one visible column must be bound to a field (No visible fields to export). A selected bookmark that is no longer valid raises A selected bookmark has expired. Controls are disabled during the export, and the current record is restored afterwards.

pascal
TFile.WriteAllText('customers.csv', Grid.ExportCSV, TEncoding.UTF8);

ScaledRowHeight method

function ScaledRowHeight: Integer;

Returns LogicalRowHeight scaled to the PPI the grid was created at, or the PPI of the last DPI change: MulDiv(LogicalRowHeight, PPI, 96). The grid sets DefaultRowHeight to this value after every layout change and DPI change.

pascal
Grid.LogicalRowHeight := 22;
// ScaledRowHeight is 22 at 96 PPI and 33 at 144 PPI (150 % scaling).

Examples

Bind a grid and post an edit explicitly

Creates a grid for a customer table with denser rows, then sets a balance from text and posts it. EditValue validates the text before the record changes; the Post call is what makes the change permanent.

pascal
uses Data.DB, Vcl.Controls, Atlas.DBControls;

function AddCustomerGrid(Parent: TWinControl; Source: TDataSource): TAtlasDBGrid;
begin
  Result := TAtlasDBGrid.Create(Parent);
  Result.Parent := Parent;
  Result.Align := alClient;
  Result.DataSource := Source;
  Result.LogicalRowHeight := 22;  // at 96 PPI; 33 pixels at 144 PPI
end;

procedure SetBalance(Grid: TAtlasDBGrid; const Text: string);
begin
  Grid.EditValue('Balance', Text);  // validates, then leaves the record in edit state
  Grid.DataSource.DataSet.Post;     // EditValue never posts; your code does
end;

Refuse bad input and cancel

Tries an edit and cancels it on any error. abc is refused with Invalid finite number: abc before the record is edited; 1e3 is stored as 1000.

pascal
uses System.SysUtils, Data.DB, Atlas.DBControls;

function TrySetBalance(Grid: TAtlasDBGrid; const Text: string): Boolean;
var
  DataSet: TDataSet;
begin
  DataSet := Grid.DataSource.DataSet;
  try
    Grid.EditValue('Balance', Text);
    DataSet.Post;
    Result := True;
  except
    on E: Exception do
    begin
      if DataSet.State in dsEditModes then
        DataSet.Cancel;
      Writeln('Rejected: ', E.Message);
      Result := False;
    end;
  end;
end;

Export visible or selected rows

Saves every record the dataset shows to a UTF-8 file, then returns the current record alone as CSV. Hide a column first (Grid.Columns[2].Visible := False) to leave it out of both.

pascal
uses System.SysUtils, System.IOUtils, Atlas.DBControls;

procedure SaveVisibleRows(Grid: TAtlasDBGrid; const FileName: string);
begin
  // Visible columns only, every record the dataset's filter lets through.
  TFile.WriteAllText(FileName, Grid.ExportCSV, TEncoding.UTF8);
end;

function SelectedAsCSV(Grid: TAtlasDBGrid): string;
begin
  Grid.SelectedRows.CurrentRowSelected := True;  // add the current record to the selection
  Result := Grid.ExportCSV(True);
end;

TAtlasDBLookupCombo · TAtlasDataFilter · TAtlasDBRichEdit · EAtlasDatabaseError

Also in Atlas Info

TAtlasDBLookupCombo · TAtlasDBRichEdit · TAtlasDataFilter · TAtlasFilterEdit