Skip to content

TTMSFNCDataGrid

TTMSFNCDataGrid

Introduction

The TTMSFNCDataGrid is a versatile and feature-rich data grid component in Delphi, part of the TMS FNC UI Pack. This component is designed to handle large datasets efficiently while providing a customizable and user-friendly interface. It supports multiple platforms, including VCL, FMX, and Web, making it a cross-platform solution for displaying tabular data. Key features of the TTMSFNCDataGrid include sorting, filtering, editing, and grouping of data, as well as flexible cell formatting and the ability to embed various controls within cells. This makes it an essential tool for developers looking to create dynamic and interactive data-driven applications.

In addition to its core capabilities, the TTMSFNCDataGrid is well-suited for implementing master-detail relationships, a common pattern in database-driven applications. A master-detail setup allows developers to link two grids or data sources, where the selection in the master grid (e.g., a list of orders) determines the data displayed in the detail grid (e.g., the items within a selected order).

Reference

When developing applications with the TTMSFNCDataGrid, you'll encounter various classes, properties, methods, and events. Below is a list of the key classes that make up the data grid, which will help you navigate and utilize the most important ones effectively.

Getting Started

To start using the grid in your application, add it to a form:

  1. Open Your Project: Start by opening your Delphi or C++ Builder project.
  2. Locate the Component: In the Tool Palette, search for TTMSFNCDataGrid.
  3. Drag and Drop: Drag the TTMSFNCDataGrid component from the Tool Palette onto your form.

Alternatively, you can create and add the grid programmatically:

var
  Grid: TTMSFNCDataGrid;
begin
  Grid := TTMSFNCDataGrid.Create(Self);
  Grid.Parent := Self;
  Grid.Align := TAlignLayout.Client;
end;

Basic Configuration

Once the grid is added to your form, you can configure its basic properties:

  1. Columns: Define the columns for your grid.
  2. Rows: Set the initial number of rows.
  3. Headers: Customize the column headers.

The basic structure of the grid is handled by the TTMSFNCDataGridCore class.

Example: Basic Configuration

Grid.ColumnCount := 3;
Grid.RowCount := 5;
Grid.Columns[0].Header := 'ID';
Grid.Columns[1].Header := 'Name';
Grid.Columns[2].Header := 'Country';

Populating the Grid with Data

Populate the grid with data by accessing the cell data structure. Cell data is stored as TValue, which means that it can be any type of data you want. Data manipulation and storage is handled by the TTMSFNCDataGridData class.

Manual Data Entry

You can manually enter data into the grid by assigning values directly to specific cells.

Grid.Cells[0, 1] := 1;
Grid.Cells[1, 1] := 'John Doe';
Grid.Cells[2, 1] := 'USA';

Assigning Events

To interact with the grid or handle custom actions, you can assign events to the grid and handle them in your application.

Example: Cell Click event

procedure TForm1.GridCellClick(Sender: TObject; ACol, ARow: Integer);
begin
  ShowMessage('Cell clicked at Column: ' + IntToStr(ACol) + ', Row: ' + IntToStr(ARow));
end;

Grid.OnCellClick := DoCellClick;

Changing the Grid look & feel

The grid presents cells according to details like font, color, size, text alignment, and other attributes. These settings can be customized in multiple ways.

Using CellAppearance

The CellAppearance property defines the layout for various cell states, such as selected, fixed, focused, and more, as well as for specific cell types like group node headers or summary footers. To modify the appearance, you can write:

Grid.CellAppearance.FocusedLayout.Fill.Color := gcRed;

The code above will modify the fill color of the focused cell layout.

Using Columns

The CellAppearance property applies settings across the entire grid. However, at the column level, these settings can be overridden by specifying which particular settings need adjustment. This can be achieved using the following code:

Grid.Columns[1].Appearance.FocusedLayout.Fill.Color := gcRed;
Grid.Columns[1].AddSetting(gcsAppearance);

At design time, modifying properties in the columns editor will automatically apply the necessary overrides for the settings.

Using Cell Layouts

For more precise control over the appearance at the cell level, you can use Layouts. Similar to setting cell data, you can configure the Layouts property as follows:

Grid.Layouts[3, 3].Fill.Color := gcRed;

Note that changing the layout at the cell level affects all states simultaneously, rather than allowing you to modify the appearance for each state individually.

Adding, Formatting & Converting Data

In the Getting Started section, we provided a sample for adding data and demonstrated how the grid can handle various data types and display formats. In this chapter, we'll explore in more detail how to add, format and convert data.

Basic Cell Data

Grid.Cells[0, 1] := 1;
Grid.Cells[1, 1] := 'John Doe';
Grid.Cells[2, 1] := 'USA';

Cell data is stored using the TValue type, allowing it to hold any type of data. In the example above, we've inserted a number (1) and two strings. The grid will display the data exactly as it is in the corresponding cells. In the next sample, we've used a TDateTime & a floating point number.

Grid.Cells[0, 1] := 1.23456789;
Grid.Cells[1, 1] := Now;
Grid.Cells[2, 1] := 'USA';

You'll notice that the rendering is based on numerical values, as TDateTime is represented as a Double, with the default formatting displaying it as a decimal number. To change this formatting, you can either use the Formatting property at the column level or handle it through the OnGetCellFormatting event.

Grid.Columns[0].Formatting.&Type := gdftNumber;
Grid.Columns[0].Formatting.Format := '%.2f';
Grid.Columns[0].AddSetting(gcsFormatting);

Grid.Columns[1].Formatting.&Type := gdftDateTime;
Grid.Columns[1].AddSetting(gcsFormatting);

Setting Up OnGetCellFormatting Event

To customize the cell formatting, handle the OnGetCellFormatting event. This event is triggered for every cell that is rendered, and you can adjust the format based on the content.

Parameters

  • ACell: Identifies the cell that is currently being formatted.
  • AData: The actual data stored in the cell.
  • AFormatting: Defines the format in which the cell's data will be displayed.
  • AConvertSettings: A set of format settings (like date, number, currency formats) that can be adjusted to control the appearance of the data.
  • ACanFormat: Allows or denies formatting of a cell. Set this to True to enable formatting.

Example: Applying Formatting to Different Data Types

Let’s apply custom formatting to a grid that holds various types of data, including names, dates, statuses, years of experience, and salaries.

Here’s how to handle the formatting in the OnGetCellFormatting event for different data types:

Use Case

We have a grid with 5 columns:

  • Name: Display names.
  • Join Date: Display join dates in a specific format.
  • Status: Display boolean values as custom text ("Approved" or "Pending").
  • Years of Experience: Format numbers as integers.
  • Salary: Format salary as a currency with two decimal points.

Formatting Rules

  • Names: No special formatting applied.
  • Join Date: Dates should be displayed in the format yyyy/mm/dd, with / as the date separator.
  • Status: Booleans should be displayed as custom text—True as "Approved", and False as "Pending".
  • Years of Experience: Format integers.
  • Salary: Salaries should be displayed as currency, with two decimal places and a dollar sign ($).

Results in the Grid

  • Name: No changes.
  • Join Date: Dates like 2022/05/22 will display using the yyyy/mm/dd format.
  • Status: Status values will be displayed as "Approved" for True and "Pending" for False.
  • Years of Experience: Numbers like 5,5 will display as 6 without decimals.
  • Salary: Salaries like 75000.5 will appear as $75,000.50.
procedure TForm1.GridGetCellFormatting(Sender: TObject; ACell: TTMSFNCDataGridCellCoord;
  AData: TTMSFNCDataGridCellValue; var AFormatting: TTMSFNCDataGridDataFormatting;
  var AConvertSettings: TFormatSettings; var ACanFormat: Boolean);
begin
  // Skip formatting for the first row (header row)
  if ACell.Row = 0 then
    Exit;

  ACanFormat := True;

  // Column 2: Format as date
  if ACell.Column = 1 then
  begin
    AFormatting.Format := 'yyyy/mm/dd';  // Custom date format
    AFormatting.&Type := gdftDate;  // Set the formatting type to Date
    AConvertSettings.ShortDateFormat := 'yyyy/mm/dd';  // Ensure date is displayed as yyyy/mm/dd
    AConvertSettings.DateSeparator := '/';  // Use '/' as date separator
  end;

  // Column 3: Format as boolean with custom true/false text
  if ACell.Column = 2 then
  begin
    AFormatting.&Type := gdftBoolean;
    AFormatting.BooleanTrueText := 'Approved';
    AFormatting.BooleanFalseText := 'Pending';
  end;

  // Column 4: Format as integer with a specific format
  if ACell.Column = 3 then
  begin
    AFormatting.Format := '%g';  // Format numbers with commas for thousands
    AFormatting.&Type := gdftNumber;  // Set type to Number
  end;

  // Column 5: Format salary with two decimal points and currency symbol
  if ACell.Column = 4 then
  begin
    AFormatting.Format := '#,##0.00';  // Format salary with commas and decimals
    AFormatting.&Type := gdftFloat;  // Set type to Float
    AConvertSettings.CurrencyString := '$';  // Set currency symbol
    AConvertSettings.DecimalSeparator := '.';  // Use '.' as the decimal separator
    AConvertSettings.ThousandSeparator := ',';  // Use ',' as the thousand separator
  end;
end;

procedure TForm1.PopulateGrid;
begin
  // Set the number of rows and columns
  Grid.RowCount := 5;  // 5 rows for more data
  Grid.ColumnCount := 5;  // 5 columns for different data types

  // Add column headers
  Grid.Cells[0, 0] := 'Name';
  Grid.Cells[1, 0] := 'Join Date';
  Grid.Cells[2, 0] := 'Status';
  Grid.Cells[3, 0] := 'Years of Experience';
  Grid.Cells[4, 0] := 'Salary';

  // Add sample data to the grid
  Grid.Cells[0, 1] := 'Jane Smith';
  Grid.Cells[1, 1] := EncodeDate(2022, 5, 22);  // Example join date
  Grid.Cells[2, 1] := False;  // Pending
  Grid.Ints[3, 1] := 5;  // Years of experience
  Grid.Floats[4, 1] := 75000.50;  // Salary with decimals

  Grid.Cells[0, 2] := 'John Doe';
  Grid.Cells[1, 2] := EncodeDate(2019, 7, 10);  // Example join date
  Grid.Cells[2, 2] := True;  // Approved
  Grid.Ints[3, 2] := 8;  // Years of experience
  Grid.Floats[4, 2] := 85000.75;  // Salary with decimals

  Grid.Cells[0, 3] := 'Alice Brown';
  Grid.Cells[1, 3] := EncodeDate(2021, 11, 3);  // Example join date
  Grid.Cells[2, 3] := False;  // Pending
  Grid.Ints[3, 3] := 3;  // Years of experience
  Grid.Floats[4, 3] := 62000.00;  // Salary with no decimals

  Grid.Cells[0, 4] := 'Bob Johnson';
  Grid.Cells[1, 4] := EncodeDate(2018, 9, 15);  // Example join date
  Grid.Cells[2, 4] := True;  // Approved
  Grid.Ints[3, 4] := 12;  // Years of experience
  Grid.Floats[4, 4] := 95000.00;  // Salary with no decimals

  Grid.AutoSizeGrid();
end;

TMSFNCDATAGRID_FORMATTING.png

Converting Data

Since TTMSFNCDataGrid stores its cell data using the versatile TValue type, it’s easy to convert between different data types when needed. You can use built-in functions to convert TValue to specific types, such as integers, floats, booleans, and strings:

  • Convert to Integer: TValue.ToInteger
  • Convert to String: TValue.ToString
  • Convert to Float: TValue.ToFloat
  • Convert to Boolean: TValue.ToBoolean

Example:

var  
  Value: TValue;  
  IntValue: Integer;  
  StrValue: String;  
begin  
  Value := Grid.Cells[1, 1];   // Fetch cell data as TValue  

  if Value.IsInteger then  
    IntValue := Value.AsInteger;  

  StrValue := Value.AsString;  // Convert to string  
end;  

This flexibility allows you to retrieve and manipulate data in the format you need for calculations, validations, or display purposes.

Converting String Data to Dates

Another frequent need is to convert string representations of dates into TDateTime values. This is common when loading data from external sources, where dates are often stored as strings in various formats.

Example: Converting String Data to TDateTime Using ConvertSettings

If your grid contains date values stored as strings (e.g., "31-12-2023"), you can use the ConvertSettings to transform these strings into TDateTime values that the grid will display properly:

procedure TForm1.GridGetCellFormatting(Sender: TObject; ACell: TTMSFNCDataGridCellCoord;  
  AData: TTMSFNCDataGridCellValue; var AFormatting: TTMSFNCDataGridDataFormatting;  
  var AConvertSettings: TFormatSettings; var ACanFormat: Boolean);  
begin  
  // Column 1 is expected to contain date data in string format  
  if ACell.Column = 1 then  
  begin  
    AConvertSettings.ShortDateFormat := 'dd-mm-yyyy';  // Define the input date format  
    AConvertSettings.DateSeparator := '-';             // Specify the date separator  

    AFormatting.Format := 'yyyy/mm/dd';  // Format the output as yyyy/mm/dd  
    AFormatting.&Type := gdftDate;       // Indicate the data should be treated as a date  

    ACanFormat := True;  // Allow the formatting to proceed  
  end;  
end;  

Here, the ConvertSettings define how to interpret the input string ("31-12-2023") and convert it into a TDateTime value. The grid will then display the date in the yyyy/mm/dd format, even though the input was in a different string format.

Editing

To enable editing in TTMSFNCDataGrid, you need to ensure that editing capabilities are properly configured. The grid provides various options for enabling and handling cell editing, both through user interaction and programmatically.

Here's a guide to enable and handle editing:

Enable Editing

Make sure the Options.Editing.Enabled property is set to True to allow cell editing.

procedure TForm1.FormCreate(Sender: TObject);
begin
  // Enable editing
  Grid.Options.Editing.Enabled := True;
end;

Enable Different Editor Types

The TTMSFNCDataGrid supports various types of editors, such as text editors, dropdown lists, date pickers, etc.

To specify the editor type for a particular column:

Grid.ColumnCount := 10;

// Example: Set a date picker editor for column 4
Grid.Columns[4].Editor := getDatePicker;
Grid.Columns[4].AddSetting(gcsEditor);

// Example: Set a color picker editor for column 5
Grid.Columns[5].Editor := getColorPicker;
Grid.Columns[5].AddSetting(gcsEditor);

// Example: Set a trackbar (slider) editor for column 6
Grid.Columns[6].Editor := getTrackBar;
Grid.Columns[6].AddSetting(gcsEditor);

TMSFNCDATAGRID_DATEPICKER.png TMSFNCDATAGRID_COLORPICKER.pngTMSFNCDATAGRID_TRACKBAR.png

List of Editor Types

Below is a list of editor types used in combination with the Editor property at column level, or via the OnGetInplaceEditorType event

  • getEdit :Default inplace editor, edit cell values based on strings
  • getNumericEdit : Edit cell values based on numbers
  • getSignedNumericEdit : Edit cell values specifically based on positive or negative numbers
  • getFloatEdit : Edit cell values based on floating point numbers
  • getSignedFloatEdit : Edit cell values specifically based on positive or negative floating point numbers
  • getUppercaseEdit : Edit cell values with Uppercase string values
  • getMixedCaseEdit : Edit cell values with Mixed case string values
  • getLowerCaseEdit : Edit cell values with Lower case string values
  • getMoneyEdit : Edit cell values formatted as Money numbers
  • getHexEdit : Edit cell values formatted as hex strings
  • getAlphaNumericEdit : Edit cell values containing only alpha or numeric characters
  • getValidCharsEdit : Edit cell values containing only a set of valid characters
  • getComboBox :Edit cell values with a combobox
  • getComboEdit :Edit cell values with a editable combobox
  • getSpinBox : Edit cell values with a spin box
  • getDatePicker : Edit cell values with a date picker
  • getColorPicker : Edit cell values with a color picker
  • getTrackBar : Edit cell values with a trackbar
  • getMemo : Edit cell values containing multi-line strings
  • getHTMLEditor : Edit cell values containing HTML in a separate editor
  • getCustom : Specify your own editor and set it up in the OnGetInplaceEditorClass event

Populate Dropdown Editor

If you're using a dropdown editor (combobox), you need to supply the items for the editor:

Grid.Columns[1].Editor := getComboBox;
Grid.Columns[1].AddSetting(gcsEditor);
Grid.Columns[1].EditorItems.Add('John Doe');
Grid.Columns[1].EditorItems.Add('Caroline Dover');
Grid.Columns[1].EditorItems.Add('Loena Vandamme');
Grid.Columns[1].AddSetting(gcsEditorItems);

TMSFNCDATAGRID_COMBOBOX.png

Custom Editor

The possibility exists to use a custom editor. To implement this, etCustom must be set to the var parameter in the OnGetCellEditorType event. For this sample we have used a TTreeView item in a cell that has a modified columnwidth and rowheight:

Grid.ColumnWidths[4] := 150;
Grid.RowHeights[4] := 100;

procedure TForm1.TMSFNCDataGrid1GetInplaceEditorType(Sender: TObject;
  ACell: TTMSFNCDataGridCellCoord;
  var AInplaceEditorType: TTMSFNCDataGridInplaceEditorType);
begin
  AInplaceEditorType := getCustom;
end;

To specify which editor the custom type is, the OnGetInplaceEditorClass must be implemented, returning the editor type of choice.

procedure TForm1.TMSFNCDataGrid1GetInplaceEditorClass(Sender: TObject;
  ACell: TTMSFNCDataGridCellCoord;
  var AInplaceEditorClass: TTMSFNCDataGridInplaceEditorClass);
begin
  AInplaceEditorClass := TTreeView;
end;

Additional properties, items, appearance can be added to the custom editor in the OnGetInplaceEditorProperties event.

procedure TForm1.TMSFNCDataGrid1GetInplaceEditorProperties(Sender: TObject;
  ACell: TTMSFNCDataGridCellCoord; AInplaceEditor: TTMSFNCDataGridInplaceEditor;
  AInplaceEditorType: TTMSFNCDataGridInplaceEditorType);
var
  tParent, tGroup, tItem: TTreeViewItem;
begin
  tParent := TTreeViewItem.Create(AInplaceEditor);
  tParent.Text := 'Fruits';
  AInplaceEditor.AddObject(tParent);

  tGroup := TTreeViewItem.Create(AInplaceEditor);
  tGroup.Text := 'Red Fruits';
  tParent.AddObject(tGroup);

  tItem := TTreeViewItem.Create(AInplaceEditor);
  tItem.Text := 'StrawBerry';
  tGroup.AddObject(tItem);

  tItem := TTreeViewItem.Create(AInplaceEditor);
  tItem.Text := 'Cherry';
  tGroup.AddObject(tItem);

  tGroup := TTreeViewItem.Create(AInplaceEditor);
  tGroup.Text := 'Green Fruits';
  tParent.AddObject(tGroup);

  tItem := TTreeViewItem.Create(AInplaceEditor);
  tItem.Text := 'Apple';
  tGroup.AddObject(tItem);

  tItem := TTreeViewItem.Create(AInplaceEditor);
  tItem.Text := 'Lime';
  tGroup.AddObject(tItem);
end;

To bind the treeview to the cell, we need the OnCellEditGetData & OnCellEditSetData

procedure TForm1.TMSFNCDataGrid1CellEditGetData(Sender: TObject;
  ACell: TTMSFNCDataGridCellCoord; AInplaceEditor: TTMSFNCDataGridInplaceEditor;
  var AValue: TTMSFNCDataGridCellValue);
begin
  (AInplaceEditor as TTreeView).Selected := (AInplaceEditor as TTreeView).ItemByText(AValue.AsString);
end;

procedure TForm1.TMSFNCDataGrid1CellEditSetData(Sender: TObject;
  ACell: TTMSFNCDataGridCellCoord; AInplaceEditor: TTMSFNCDataGridInplaceEditor;
  var AValue: TTMSFNCDataGridCellValue);
begin
  AValue := (AInplaceEditor as TTreeView).Selected.Text;
end;

When clicking in the cell to start the editor, the treeview is shown.

TTMSFNCDataGrid26

Validating Input

To validate user input before saving the changes, you can use the OnCellEditValidateData event:

procedure TForm1.GridCellEditValidateData(Sender: TObject;
  ACell: TTMSFNCDataGridCellCoord; AInplaceEditor: TTMSFNCDataGridInplaceEditor;
  var AValue: TTMSFNCDataGridCellValue; var AValid: Boolean);
var
  i: Integer;
begin
  // Example: Allow only numeric values in column 2
  if ACell.Column = 2 then
  begin
    if not TryStrToInt(Grid.ValueToString(AValue), i) then
    begin
      AValid := False;
      ShowMessage('Invalid input. Please enter a number.');
    end;
  end;
end;

Grid Cell Merging / Splitting

The grid supports merging and splitting cells programmatically as well as with the keyboard. To merge a range of cells simple call

Grid.MergeCells
Sample:

Grid.MergeCells(2, 3, 3, 2);

TMSFNCDATAGRID_MERGING.png

To split the merged cells, you can use the procedure grid.SplitCell. The parameters passed in the procedure need to be the base cell of the range of merged cells.

Sample:

Grid.SplitCell(MakeCell(2, 3));

When enabled via Grid.Options.Keyboard.CellMergeShortCut, the following shortcuts invoke a merge & split of the selected cells:

CTRL + M: merge a selection of cells.
CTRL + S: split a merged cell.

Selection

TTMSFNCDataGrid provides flexible selection options to enable users to select individual cells, multiple cells, rows, or columns. This guide walks you through enabling and handling selection behavior in TTMSFNCDataGrid.

Selection Modes

The TTMSFNCDataGrid provides several selection modes, each offering different ways to select cells:

  • gsmNone : Disables selection, but other interactions remain active.
  • gsmSingleCell : Selects a single cell at a time. The previously selected cell is deselected.
  • gsmSingleRow : Selects an entire row. Previous row selection is cleared when a new row is selected.
  • gsmSingleColumn : Selects an entire column. Previous column selection is cleared when a new column is selected.
  • gsmCellRange : Allows selecting multiple cells. Holding Shift while clicking selects a range of cells. Dragging the mouse across the grid also selects multiple cells.
  • gsmRowRange : Allows selecting multiple rows. Holding Shift selects a range of rows between the clicked row and the previously selected row.
  • gsmColumnRange : Allows selecting multiple columns. Holding Shift selects a range of columns.
  • gsmDisjunctRow : Similar to gsmRowRange, but allows selecting non-contiguous rows using Ctrl.
  • gsmDisjunctColumn : Similar to gsmColumnRange, but allows selecting non-contiguous columns using Ctrl.
  • gsmDisjunctCell : Allows selecting non-contiguous cells using Ctrl.
  • gsmDisjunctCellRange : Allows selecting non-contiguous cells using Ctrl and Shift in an explorer type selection mode.

Programmatically Setting Selection

You can set the selection programmatically using the Selection property. For example, in gsmCellRange mode, to select a range of cells from (3,3) to (4,4), use:

Grid.Selection := MakeCellRange(3, 3, 4, 4);

TMSFNCDATAGRID_SELECTION.png

In gsmSingleCell mode, to select a single cell (e.g., (3,3)):

grid.FocusedCell := MakeCell(Col,Row);

Grid.Selection := MakeCellRange(StartCol,StartRow,EndCol,EndRow);

Working with Row Selection in gsmDisjunctRow

The SelectedRows[RowIndex]: Boolean property allows you to check or set the selection state of a row.

var
  i: Integer;
begin
  for i := 0 to Grid.RowCount - 1 do
  begin
    if Grid.SelectedRows[i] then
    begin
      // Process the selected row
      // For example paint the row in a color
    end;
  end;
end;

Column and Cell Selection

Similar to rows, the gsmDisjunctColumn and gsmDisjunctCell modes allow disjoint selection of columns and cells. You can use the following properties:

  • Column Selection: Use SelectedColumns[columnindex]: Boolean, SelectedColumnCount, to work with column selections.
  • Cell Selection: Use SelectedCells[col,row: Integer]: Boolean, SelectedCellCount, to work with individual cell selections.

Example for looping through all selected cells:

Fixed Cell Selection

In combination with the Options.Selection.Mode, the grid supports selection when clicking or dragging on fixed cells. This behavior can be enabled using the Options.Mouse.FixedCellSelection property.

  • gfcsAll: Clicking the top-left fixed cell selects all cells in the grid (with gsmCellRange).
  • gfcsRow: Clicking and dragging on the fixed columns selects rows (with gsmSingleRow).
  • gfcsColumn: Clicking and dragging on the fixed rows selects columns (with gsmSingleColumn).
  • gfcsRowRange: Clicking and dragging on the fixed columns selects multiple rows (with gsmRowRange).
  • gfcsColumnRange: Clicking and dragging on the fixed rows selects multiple columns (with gsmColumnRange).

Note: If column dragging, row dragging, or sorting is enabled, fixed cell selection mode is automatically disabled.

Calculations

The grid has built-in methods to perform calculations on all rows or a selected range of rows within a column. These functions return a result when called. Additionally, the grid provides automatic column calculations that are displayed in a footer row and updated when cell values change through editing.

Built-in Row Calculation Functions

Grid.RowAverage(ARow: Integer; FromCol: Integer = -1; ToCol: Integer = -1): Double

  • Purpose: Calculates the average of values in a specified row.
  • Parameters:
  • ARow: The index of the row.
  • FromCol: The starting column for the calculation (optional, defaults to all columns if set to -1).
  • ToCol: The ending column for the calculation (optional, defaults to all columns if set to -1).
  • Return Value: The average of the row values for the specified range of columns.

Grid.RowCustomCalculation(ARow: Integer; FromCol: Integer = -1; ToCol: Integer = -1): Double

  • Purpose: Performs a custom calculation on the values in a row.
  • Trigger: Triggers the event OnRowCalc, where the custom calculation logic can be implemented.

Grid.RowDistinct(ARow: Integer; FromCol: Integer = -1; ToCol: Integer = -1): Double

  • Purpose: Counts the number of distinct (unique) values in a specified row.

Grid.RowMax(ARow: Integer; FromCol: Integer = -1; ToCol: Integer = -1): Double

  • Purpose: Finds the maximum value in a specified row.

Grid.RowMin(ARow: Integer; FromCol: Integer = -1; ToCol: Integer = -1): Double

  • Purpose: Finds the minimum value in a specified row.

Grid.RowStandardDeviation(ARow: Integer; FromCol: Integer = -1; ToCol: Integer = -1): Double

  • Purpose: Calculates the standard deviation of values in a specified row.

Grid.RowSum(ARow: Integer; FromCol: Integer = -1; ToCol: Integer = -1): Double

  • Purpose: Calculates the sum of values in a specified row.

Built-in Column Calculation Functions


Grid.ColumnSum(ACol: Integer; FromRow: Integer = -1; ToRow: Integer = -1): Double

- Purpose: Calculates the sum of values in a column. - Parameters: - ACol: The index of the column. - FromRow: The starting row for the calculation (optional, defaults to all rows if set to -1). - ToRow: The ending row for the calculation (optional, defaults to all rows if set to -1). - Return Value: The sum of the column values for the specified range of rows.


Grid.ColumnAverage(ACol: Integer; FromRow: Integer = -1; ToRow: Integer = -1): Double

  • Purpose: Calculates the average of cell values in a column.

Grid.ColumnMin(ACol: Integer; FromRow: Integer = -1; ToRow: Integer = -1): Double

  • Purpose: Returns the minimum value in a specified column.

Grid.ColumnMax(ACol: Integer; FromRow: Integer = -1; ToRow: Integer = -1): Double

  • Purpose: Returns the maximum value in a specified column.

Grid.ColumnDistinct(ACol: Integer; FromRow: Integer = -1; ToRow: Integer = -1): Double

  • Purpose: Counts the number of distinct (unique) cell values in a column.

Grid.ColumnStandardDeviation(ACol: Integer; FromRow: Integer = -1; ToRow: Integer = -1): Double

  • Purpose: Calculates the standard deviation of cell values in a column.

Grid.ColumnCustomCalculation(ACol: Integer; FromRow: Integer = -1; ToRow: Integer = -1): Double

  • Purpose: Performs a custom calculation on the values in a column.

Using the ColumnCalculations[Col, Name] property, you can specify the type of calculation that should be displayed in a footer row for each column. The available options are:

  • gcmNone: No result is displayed in the footer cell.
  • gcmSum: Displays the sum of the column.
  • gcmAvg: Displays the average of the column.
  • gcmCount: Displays the row count of the column.
  • gcmMin: Displays the minimum value of the column.
  • gcmMax: Displays the maximum value of the column.
  • gcmCustom: Displays a custom calculation result for the column.
  • gcmDistinct: Displays the count of distinct values in the column.
  • gcmStdDev: Displays the standard deviation of the column.

Programmatically Updating Calculations

When cell values are updated programmatically, the calculations can be updated using:

  • Grid.UpdateCalculations
    Updates all column calculations.

  • Grid.UpdateColumnCalculation(ColumnIndex: Integer)
    Updates the calculation for the specified column.

  • Grid.UpdateRowCalculation(RowIndex: Integer)
    Updates the calculation for the specified row.

Example

Here is an example of how to initialize the grid and set up column calculations:

Grid.RowCount := 20;
Grid.FixedBottomRowCount := 1;
Grid.RandomFill(False, 100);
Grid.ColumnCalculations[0, 'SUM'] := [CreateNormalColumnCalculation(gcmSum)];
Grid.ColumnCalculations[1, 'AVERAGE'] := [CreateNormalColumnCalculation(gcmAverage)];
Grid.ColumnCalculations[2, 'MIN'] := [CreateNormalColumnCalculation(gcmMin)];
Grid.ColumnCalculations[3, 'MAX'] := [CreateNormalColumnCalculation(gcmMax)];
Grid.UpdateCalculations;

Upon running the application, the footer row will display the specified calculations for each column. Automatic Row Calculations can be applied in a similar way but in combination with FixedRightColumnCount. When grouping, Column or Row Calculations can be created based on individual groups, use the CreateGroupColumnCalculation or CreateGroupRowCalculation instead.

Cell Controls

Built-in Cell Control Methods

The following methods are available to add or manage controls in grid cells:

  • Grid.AddButton(AColumn, ARow: Integer; AText: string = '')
    Adds a button control to the specified cell.

  • Grid.AddCheckBox(AColumn, ARow: Integer; AValue: Boolean = False)
    Adds a checkbox control to the specified cell.

  • Grid.AddRadioButton(AColumn, ARow: Integer; AValue: Boolean = False; AGroupIndex: Integer = 0)
    Adds a radio button control to the specified cell, allowing selection from a group of radio buttons.

  • Grid.AddProgressBar(AColumn, ARow: Integer; AValue: Integer = 0)
    Adds a progress bar control to the specified cell.

  • Grid.AddDataCheckBox(AColumn, ARow: Integer)
    Adds a data-bound checkbox control to the specified cell, allowing it to bind to grid data.

  • Grid.AddDataRadioButton(AColumn, ARow: Integer; AValue: Boolean = False; AGroupIndex: Integer = 0)
    Adds a data-bound radio button control to the specified cell.

  • Grid.AddDataProgressBar(AColumn, ARow: Integer)
    Adds a data-bound progress bar control to the specified cell, enabling it to update dynamically based on grid data.

  • Grid.RemoveButton(AColumn, ARow: Integer)
    Removes the button control from the specified cell.

  • Grid.RemoveCheckBox(AColumn, ARow: Integer)
    Removes the checkbox control from the specified cell.

  • Grid.RemoveProgressBar(AColumn, ARow: Integer)
    Removes the progress bar control from the specified cell.

This is an example from a demo with multiple things from the list above:

TMSFNCDATAGRID_CELLCONTROLS.png

These are other examples of Cell Controls:

TMSFNCDATAGRID_CELLCONTROLS2.png

Control Properties

Each control has properties that allow customization:

  • ControlWidths[AColumn, ARow]: Integer
    Defines the width of the control inside the cell.

  • ControlHeights[AColumn, ARow]: Integer
    Defines the height of the control inside the cell.

  • ControlAligns[AColumn, ARow]: TTMSFNCDataGridCellControlAlign
    Sets the alignment of the control within the cell.

  • ControlTexts[AColumn, ARow]: string
    Defines the text displayed on the control (applies to buttons, checkboxes, etc.).

  • ControlPositions[AColumn, ARow]: TTMSFNCDataGridCellControlPosition
    Sets the position of the control inside the cell.

Example of Adding Controls

Here is a code snippet demonstrating how to add various controls to a grid:

// Add a button to cell (1, 1)
Grid.AddButton(1, 1, 'Click Me');

// Add a checkbox to cell (2, 2) with a default value of False
Grid.AddCheckBox(2, 2, False);

// Add a radio button to cell (3, 3), part of group 1
Grid.AddRadioButton(3, 3, True, 1);

// Add a progress bar to cell (4, 4) with a default value of 50
Grid.AddProgressBar(4, 4, 50);

// Customize control properties
Grid.ControlWidths[1, 1] := 100;
Grid.ControlAligns[1, 1] := gcaTop;

Updating and Removing Controls

To update or modify existing controls programmatically:

  • Grid.SetButton(AColumn, ARow: Integer; AText: string = '')
    Updates or replaces the button at the specified cell.

  • Grid.SetCheckBox(AColumn, ARow: Integer; AValue: Boolean = False)
    Updates or replaces the checkbox at the specified cell.

To remove a control from a cell:

// Remove the button from cell (1, 1)
Grid.RemoveButton(1, 1);

// Remove the checkbox from cell (2, 2)
Grid.RemoveCheckBox(2, 2);

Control Events

The grid provides events that can be triggered when controls are interacted with:

  • OnCellButtonClick
    Triggered when a button in a cell is clicked.

  • OnCellCheckBoxChange
    Triggered when the value of a checkbox in a cell changes.

  • OnCellRadioButtonChange
    Triggered when a radio button in a cell changes state.

Example of Handling Control Events

procedure TForm1.GridCellButtonClick(Sender: TObject; AColumn, ARow: Integer);
begin
  ShowMessage('Button in cell (' + IntToStr(AColumn) + ', ' + IntToStr(ARow) + ') clicked.');
end;

procedure TForm1.GridCellCheckBoxChange(Sender: TObject; AColumn, ARow: Integer);
begin
  if Grid.Booleans[AColumn, ARow] then
    ShowMessage('Checkbox in cell (' + IntToStr(AColumn) + ', ' + IntToStr(ARow) + ') is checked.')
  else
    ShowMessage('Checkbox in cell (' + IntToStr(AColumn) + ', ' + IntToStr(ARow) + ') is unchecked.');
end;

Custom Cell Drawing

Each cell supports custom drawing via an event. With the events OnBeforeDrawCell and OnAfterDrawCell you can custom draw on a cell, or complete column / row of choice that can be retrieved by using the ACell.Row and ACell.Column parameters. Below is a sample that draws a diagonal line as a background which replaces the default background of a cell on location 3, 3.

procedure TForm1.GridAfterDrawCell(Sender: TObject; AGraphics: TTMSFNCGraphics;
  ACell: TTMSFNCDataGridCell);
begin
  if (ACell.Column = 3) and (ACell.Row = 3) then
  begin
    AGraphics.SetStrokeColor(gcRed);
    AGraphics.DrawLine(PointF(ACell.Rect.Left, ACell.Rect.Top),
      PointF(ACell.Rect.Right, ACell.Rect.Bottom));
  end;
end;

TTMSFNCGrid14

Customize Cell Elements

Each cell supports a set of elements that are determining what is being drawn. For example, if it's required to only export the text & stroke during PDF generation, it's possible to customize this via the DrawElements property. To implement this, use the OnBeforeDrawCell event with the following code.

procedure TForm1.GridBeforeDrawCell(Sender: TObject;
  AGraphics: TTMSFNCGraphics; ACell: TTMSFNCDataGridCell;
  var ACanDraw: Boolean);
begin
  if ACell.IsExporting then
    ACell.DrawElements := [gcdText, gcdStroke];
end;

Custom Cell Classes

TTMSFNCDataGrid provides a variety of cell classes to handle different types of content and behavior within the grid. These classes inherit from the base TTMSFNCDataGridCell class and allow for customizations such as node management, checkboxes, buttons, and progress bars. By default, the Grid automatically handles the required cell class but in some cases, it might be required to customize this. This chapter covers the available cell classes and how to implement them using the OnGetCellClass event.

TTMSFNCDataGridCell (Base Class)

TTMSFNCDataGridCell is the fundamental class that all other cell types in TTMSFNCDataGrid are based on. It manages the basic functionality, such as rendering the cell's content and handling user interactions.

Key Properties:

  • Rect: TRectF: Defines the boundaries of the cell.
  • Column and Row: Integer: Specify the location of the cell within the grid.
  • Text: string: Holds the cell's content.
  • Control: TTMSFNCDataGridCellControl: Specifies any controls embedded within the cell (for example, buttons or checkboxes).

You can use the base cell class when you don’t need any special controls or custom behavior in a grid cell.

TTMSFNCDataGridNodeCell

This cell class is used to represent a node in a hierarchical grid. Nodes can be collapsed or expanded, and they can have child rows, creating a tree-like structure within the grid.

Key Features:

  • State: TTMSFNCDataGridRowNodeState: Manages whether the node is expanded or collapsed.
  • IconPosition: TTMSFNCDataGridNodeCellIconPosition: Controls the placement of the expand/collapse icon.

Use this class when implementing hierarchical data that needs collapsible rows or tree structures.

TTMSFNCDataGridCheckBoxCell

This cell type includes a checkbox that can be toggled by the user. It is ideal for displaying boolean values or selecting rows.

Key Features:

  • Checked: Boolean: Determines the state of the checkbox (checked or unchecked).
  • OnChange: TNotifyEvent: Fires when the checkbox state changes.

Use this class when you need checkboxes in your grid cells, such as for selection or boolean options.

TTMSFNCDataGridButtonCell

This cell type includes a clickable button. When clicked, the button triggers an event.

Key Features:

  • OnClick: TNotifyEvent: Event that is triggered when the button is clicked.
  • Text: string: The label of the button.

Use this class for cells that require user interaction via buttons, such as "Edit" or "Delete" actions.

TTMSFNCDataGridProgressBarCell

This cell class displays a progress bar, allowing you to visualize the progress of tasks or processes.

Key Features:

  • Value: Integer: Represents the progress percentage (0-100).
  • Max: Integer: The maximum value for the progress bar.

Use this class when you need to show progress within a cell, such as task completion or loading indicators.

TTMSFNCDataGridBitmapCell

This cell type allows you to display images inside the grid. It supports loading and displaying bitmaps.

Key Features:

  • Bitmap: TTMSFNCBitmap: The image to be displayed in the cell.
  • BitmapAlign: TTMSFNCDataGridCellControlAlign: Controls the alignment of the bitmap within the cell.

Use this class to display images or icons in grid cells, such as product images, profile pictures, or status icons.

Implementing Cell Classes via OnGetCellClass

To dynamically assign a specific cell class to a grid cell, the OnGetCellClass event is used. This event allows you to specify the class type of the cell based on the row or column, enabling custom cell behavior per cell or row.

Event Handler Implementation

In the OnGetCellClass event, you can switch between different cell types based on the column or row index. This is particularly useful for grids with heterogeneous data types.

Example of Setting Different Cell Classes

The following example demonstrates how to assign different cell classes dynamically:

procedure TForm1.GridGetCellClass(Sender: TObject; ACol, ARow: Integer; var CellClassType: TTMSFNCDataGridCellClass);
begin
  if ACol = 1 then
    CellClassType := TTMSFNCDataGridCheckBoxCell
  else if ACol = 2 then
    CellClassType := TTMSFNCDataGridButtonCell
  else if ACol = 3 then
    CellClassType := TTMSFNCDataGridProgressBarCell
  else
    CellClassType := TTMSFNCDataGridCell; // Default
end;

Import & Export

Files

procedure SaveToFileData(AFileName: String; AEncoding: TEncoding);
procedure LoadFromFileData(AFileName: String; AEncoding: TEncoding);
SaveToFileData saves cell data and column widths to a proprietary file format, while LoadFromFileData loads cell data and column widths from the same format. Both methods can optionally be specified with a particular encoding.

CSV files

procedure SaveToCSVData(AFileName: String; AEncoding: TEncoding);
procedure LoadFromCSVData(AFileName: String; AEncoding: TEncoding);

SaveToCSVData saves cell data to a CSV file, while LoadFromCSVData loads cell data from a CSV file.

Several properties impact the CSV methods:

Grid.Options.IO.Delimiter: Char;
This specifies the delimiter to use when saving to and loading from CSV files. By default, the delimiter is set to #0, which allows for automatic delimiter detection when loading data from a CSV file. When saving to a CSV file, the ; character is used as the separator if the delimiter is set to #0. Setting the delimiter to a value other than #0 forces the CSV functions to use the specified delimiter exclusively.

Grid.Options.IO.QuoteEmptyCells: Boolean;
When set to true, an empty cell in the CSV file is saved as "". If false, no characters are written for empty cells.
Grid.Options.IO.AlwaysQuotes: Boolean;
When set to true, every cell value is saved with both prefix and suffix quotes. If false, quotes are only used when the cell data contains the delimiter character. Additionally, if the cell data includes quotes, they are written as doubled quotes in the file.

By default, data is loaded into the grid starting from the first normal cell, which is typically cell 1,1 (considering there is one fixed row and one fixed column). To override this default behavior and specify a different starting cell for loading data, you can use the public properties:

Grid.Options.IO.StartColumn := 0;
Grid.Options.IO.StartRow := 0;

HTML files

procedure SaveToHTMLData(AFileName: String);
SaveToHTMLData saves cell data to an HTML file, utilizing Grid.Options.IO.HTML for various export settings. The cell data is formatted and saved within an HTML table.

PDF Files

Drop a TTMSFNCDataGridPDFIO component on the form and connect the TTMSFNCDataGrid to this non-visual component’s DataGrid property.

TMSFNCDataGridPDFIO.Save(FileName);

Alternatively, the TMSFNCDataGridPDFIO component can save to a stream by calling the Save method with a TStream instance. Additionally, the component allows for configuring margins, headers, footers, and PDF metadata such as creator, author, title, and keywords. These settings can be adjusted under the Options and Information properties.

PDF Export example

TMSFNCDATAGRID_PDFEXPORT1.png

XLS files

With the TTMSFNCDataGridExcelIO component, you can easily read from and write to Excel .XLS files without needing Excel installed on your machine.

To use TTMSFNCDataGridExcelIO for importing or exporting XLS files, follow these steps:

  • Drop both the TTMSFNCDataGrid and TTMSFNCDataGridExcelIO components onto your form.
  • Assign the TTMSFNCDataGrid instance to the DataGrid property of the TTMSFNCDataGridExcelIO component.
  • Configure the TTMSFNCDataGridExcelIO properties to control the Excel file read/write behavior, although the default settings are usually sufficient.
  • To import an Excel file, use:

TMSFNCDataGridExcelIO.XLSImport(FileName);
or
TMSFNCDataGridExcelIO.XLSImport(FileName,SheetName);

To export the contents of TTMSFNCDataGrid to an XLS file use:

TMSFNCDataGridExcelIO.XLSExport(Filename);
or
TMSFNCDataGridExcelIO.XLSExport(FileName,SheetName); 

Properties of TTMSFNCDataGridExcelIO

Many properties are available in TTMSFNCDataGridExcelIO to customize importing & exporting of Excel files in the grid.

Property name Description
AutoResizeGrid: Boolean; When true, the dimensions of the grid (ColCount, RowCount) will adapt to the number of imported cells.
DateFormat: string; Sets the format of dates to use for imported dates from the Excel file. When empty, the default system date formatting is applied.
GridStartCol, GridStartRow: integer; Specifies from which top/left column/row the import/export happens
Options.ExportCellFormats: Boolean; When true, cell format (string, integer, date, float) is exported, otherwise all cells are exported as strings.
Options.ExportCellMargings: Boolean; When true, the margins of the cell are exported
Options.ExportCellProperties: Boolean; When true, cell properties such as color, font, alignment are exported
Options.ExportCellSizes: Boolean; When true, the size of the cells is exported
Options.ExportFormulas: Boolean; When true, the formula is exported, otherwise the formula result is exported
Options.ExportHardBorders: Boolean; When true, cell borders are exported as hard borders for the Excel sheet
Options.ExportHiddenColumns: Boolean; When true, hidden columns are also exported
Options.ExportHTMLTags: Boolean; When true, HTML tags are also exported, otherwise all HTML tags are stripped during export
Options.ExportImages: Boolean; When true, images in the grid are also exported
Options.ExportOverwrite: Boolean; Controls if existing files should be overwritten or not during export
Options.ExportOverwriteMessage: Boolean; Sets the message to show warning to overwrite existing files during export
Options.ExportPrintOptions: Boolean; When true, the print options are exported to the XLS file
Options.ExportShowGridLines: Boolean; When true, grid line setting as set in TAdvStringGrid is exported to the XLS sheet
Options.ExportShowInExcel: Boolean; When true, the exported file is automatically shown in the default installed spreadsheet after export.
Options.ExportSummaryRowBelowDetail: Boolean; When true, summary rows are shown below detail rows in the exported XLS sheet
Options.ExportWordWrapped: Boolean; When true, cells are exported as wordwrapped cells
Options.ImportCellFormats: Boolean; When true, cells are imported with formatting as applied in the XLS sheet
Options.ImportCellProperties: Boolean; When true, cell properties such as color, font, alignment are imported
Options.ImportCellSizes: Boolean; When true, the size of cells is imported
Options.ImportClearCells: Boolean; When true, it will clear all existing cells in the grid before the import is done
Options.ImportFormulas: Boolean; When true, the formula is imported, otherwise only a formula result is imported
Options.ImportImages: Boolean; When true, images from the XLS sheet are imported
Options.ImportLockedCellsAsReadOnly: Boolean; When true, cells that are locked in the XLS sheet will be imported as read-only cells
Options.ImportPrintOptions: Boolean; When true, print settings as defined in the XLS sheet will be imported as grid.PrintSettings
Options.UseExcelStandardColorPalette: Boolean; When true, colors will be mapped using the standard Excel color palette, otherwise a custom palette will be included in the XLS sheet.
TimeFormat: string; Sets the format of cells with a time. When no format is specified, the default system time format is applied.
XlsStartCol, XlsStartRow: integer; Sets the top/left cell from where the import/export should start

Advanced Topics on Importing & Exporting

In TTMSFNCDataGrid, you can use the OnLoadCellData and OnSaveCellData events to apply transformations to cell data. Here’s how you can modify the approach to use these events instead of inheriting from TTMSFNCDataGrid:

  1. Assign Event Handlers: Define event handler methods for OnLoadCellData and OnSaveCellData to apply your transformations (e.g., encryption and decryption).

  2. Attach Event Handlers: Assign these methods to the corresponding events of the TTMSFNCDataGrid component.

Here is a code example demonstrating how to use these events:

procedure TForm1.FormCreate(Sender: TObject);
begin
  // Assign event handlers to the grid
  Grid.OnSaveCellData := SaveCellDataHandler;
  Grid.OnLoadCellData := LoadCellDataHandler;
end;

procedure TForm1.SaveCellDataHandler(Sender: TObject; AColumn, ARow: Integer; var AValue: TTMSFNCDataGridCellValue);
begin
  // Apply encryption or any other transformation before saving
  AValue := Encrypt(AValue);
end;

procedure TForm1.LoadCellDataHandler(Sender: TObject; AColumn, ARow: Integer; var AValue: TTMSFNCDataGridCellValue);
begin
  // Apply decryption or reverse the transformation after loading
  AValue := Decrypt(AValue);
end;

// Replace Encrypt and Decrypt with your actual transformation methods
function TForm1.Encrypt(const AValue: TTMSFNCDataGridCellValue): TTMSFNCDataGridCellValue;
begin
  // Example encryption logic
  Result := 'Encrypted_' + Grid.ValueToString(AValue);
end;

function TForm1.Decrypt(const AValue: TTMSFNCDataGridCellValue): TTMSFNCDataGridCellValue;
begin
  // Example decryption logic
  Result := StringReplace(Grid.ValueToString(AValue), 'Encrypted_', '', [rfReplaceAll]);
end;

In this example: - SaveCellDataHandler is called when saving cell data, applying an encryption transformation. - LoadCellDataHandler is called when loading cell data, applying a decryption transformation. - Encrypt and Decrypt are placeholder methods for your actual transformation logic.

Make sure to replace the Encrypt and Decrypt functions with your real encryption and decryption implementations.

Sorting

To perform sorting in TTMSFNCDataGrid, you typically use the Sort method, which sorts based on column index and direction (ascending or descending). Sorting can be triggered programmatically or through user interaction with the grid, such as clicking on a column header.

Example of Sorting by Column

Here's a basic example of sorting a grid based on a column's data:

Grid.Sort(0, gsdDescending);  // Sort the first column in desc order

In the above code, the Sort method is called with two parameters: - The first parameter is the column index (0 in this case, meaning the first column). - The second parameter is the sorting direction, which can be either gsdAscending, gsdDescending or gsdNone.

In this picture you find an example of sorting:

TMSFNCDATAGRID_SORTING.png

Example: Dynamic Sorting Based on Column Click

Grid.Options.Sorting.Enabled := True;

In this example: - You enable the Sorting function by setting it on true - The Grid checks the current sort direction of the clicked column. - If the column is already sorted in descending order, it sorts the column in ascending order. Otherwise, it sorts the column in descending order.

Advanced Sorting with Multiple Columns

You can also perform multi-column sorting, where sorting occurs based on multiple columns sequentially.

var
  Columns: TArray<Integer>;
  Directions: TArray<TTMSFNCDataGridSortDirection>;
begin
  // Define columns to sort by
  Columns := TArray<Integer>.Create(0, 1); // Sort by first and second columns

  // Define corresponding sort directions
  Directions := TArray<TTMSFNCDataGridSortDirection>.Create(sdDescending, sdAscending);

  // Perform multi-column sorting
  Grid.Sort(Columns, Directions);
end;

In this example: - You specify the columns (0 and 1, representing the first and second columns). - You define sort directions for each column (Descending for the first column and ascending for the second column). - Then, you use the Sort method to sort based on both columns.

Using Custom Sorting Logic

For more complex sorting requirements, you can implement custom sorting by utilizing the OnCustomCompare event, which allows you to define your own logic for comparing values in two cells.

procedure TForm1.GridCustomCompare(Sender: TObject; ACol, ARow1, ARow2: Integer; var AResult: Integer);
begin
  // Custom comparison logic for sorting
  if Grid.Floats[ACol, ARow1] > Grid.Floats[ACol, ARow2] then
    AResult := 1
  else if Grid.Floats[ACol, ARow1] < Grid.Floats[ACol, ARow2] then
    AResult := -1
  else
    AResult := 0;
end;

In this example: - The OnCustomCompare event is assigned to the grid. - You implement custom logic for comparing two rows based on the value of a specific column.

This is how sorting can be implemented using the style provided in your documentation. You can adjust the sorting logic based on your specific needs, such as custom comparison or sorting multiple columns.

Grouping

Grouping allows you to organize data within the TTMSFNCDataGrid based on one or more columns. This feature makes it easier to analyze datasets by visually categorizing and displaying related data together. The grid also supports various aggregation methods, such as calculating the sum, average, or count for grouped data, making it a powerful tool for data analysis.

Enabling Grouping

To enable grouping, use the Group method to specify the columns that should be grouped. For instance, to group by a specific column, you can call:

  Grid.Group(0);  // Group by the first column

You can also group by multiple columns, allowing for hierarchical grouping:

  Grid.Group([0, 1]);  // Group by the first and second columns

Multi-Column Grouping with Custom Logic

For more advanced use cases, you can apply custom grouping logic when grouping by multiple columns. Here's an example of multi-column grouping with a custom grouping function:

Key Concepts in Multi-Column Grouping

Grouping in TTMSFNCDataGrid works by categorizing rows based on the values of specific columns. When multi-column grouping is enabled, the grid groups data by the first column, then subgroups by the second column, and so on. You can also implement custom logic to define how grouping works.

Key Methods and Events:

  • Group(AColumns: TArray<Integer>): This method enables grouping by an array of columns.
  • OnGetCustomGroup: This event allows you to define custom grouping logic for each group level.

Example Scenario: Grouping Employee Data

In this example, we have an employee dataset with the following columns:

  • Department
  • Location
  • Position
  • Salary

We will group the data first by Location, then by Salary. Additionally, we'll implement custom logic to group Salaries into salary ranges instead of exact values.

Step 1: Setting Up the Grid with Employee Data

The first step is to initialize the grid, define the columns, and populate it with employee data. You will have columns like Department, Location, Position, and Salary, with corresponding rows representing different employees.

procedure TForm1.SetupGrid;
begin
  // Define the columns
  Grid.ColumnCount := 4;
  Grid.Columns[0].Header := 'Department';
  Grid.Columns[1].Header := 'Location';
  Grid.Columns[2].Header := 'Position';
  Grid.Columns[3].Header := 'Salary';

  // Add some employee data
  Grid.RowCount := 8;
  Grid.Cells[0, 1] := 'HR';       Grid.Cells[1, 1] := 'New York';  Grid.Cells[2, 1] := 'Assistant';   Grid.Cells[3, 1] := '60000';
  Grid.Cells[0, 2] := 'HR';       Grid.Cells[1, 2] := 'San Francisco'; Grid.Cells[2, 2] := 'Manager'; Grid.Cells[3, 2] := '95000';
  Grid.Cells[0, 3] := 'IT';       Grid.Cells[1, 3] := 'New York';  Grid.Cells[2, 3] := 'Developer';   Grid.Cells[3, 3] := '85000';
  Grid.Cells[0, 4] := 'IT';       Grid.Cells[1, 4] := 'Chicago';   Grid.Cells[2, 4] := 'Developer';   Grid.Cells[3, 4] := '80000';
  Grid.Cells[0, 5] := 'Finance';  Grid.Cells[1, 5] := 'New York';  Grid.Cells[2, 5] := 'Analyst';     Grid.Cells[3, 5] := '70000';
  Grid.Cells[0, 6] := 'Finance';  Grid.Cells[1, 6] := 'Chicago';   Grid.Cells[2, 6] := 'Analyst';     Grid.Cells[3, 6] := '72000';
  Grid.Cells[0, 7] := 'Finance';  Grid.Cells[1, 7] := 'San Francisco'; Grid.Cells[2, 7] := 'Manager'; Grid.Cells[3, 7] := '95000';
end;

Step 2: Group by Multiple Columns

To group the data by Location, and Salary, you need to call the Group method and pass an array of column indices (e.g., [1, 3] for the 2 columns). This method will group the data hierarchically, starting with Location, and then Salary.

procedure TForm1.GroupData;
begin
  // Group by Location (col 1), and Salary (col 3)
  Grid.Group([1, 3]);
end;

Step 3: Implement Custom Grouping Logic

Grouping the Salary column can be done with a custom logic to categorize salaries into ranges like "$60,000-$70,000" instead of exact numbers. This can be achieved using the OnGetCustomGroup event, where you define how each salary value is transformed into a salary range.

procedure TForm1.GridGetCustomGroup(Sender: TObject; ACell: TTMSFNCDataGridCellCoord; AData: TTMSFNCDataGridCellValue; ALevel: Integer; var AGroup: string);
var
  Salary: Double;
begin
  if ACell.Column = 3 then  // If it's the salary column
  begin
    Salary := AData.ToFloat;
    if Salary < 70000 then
      AGroup := '$60,000 - $70,000'
    else if Salary < 80000 then
      AGroup := '$70,000 - $80,000'
    else if Salary < 90000 then
      AGroup := '$80,000 - $90,000'
    else
      AGroup := 'Above $90,000';
  end;
end;

Step 4: Applying Grouping and Custom Logic

Once your custom logic is in place, you can apply it when performing the grouping. For instance, after grouping by the primary columns (Location, Salary), the grid will categorize the data further by salary ranges according to your custom rules.

This will result in a hierarchical data structure, where employees are first grouped by their department, then their location, and finally by their position and salary range.

TMSFNCDATAGRID_CUSTOMGROUPING.png

Group Calculations

The TTMSFNCDataGrid provides built-in methods to perform calculations on grouped data, such as sum, average, and distinct counts. You can also merge and add a summary from the group. For example, to calculate the sum of a grouped column:

Grid.Group(1); // Groups the data based on the second column (index 1).
Grid.GroupSum(6); // Calculates and displays the sum of the values in the seventh column (index 6) for each group.
Grid.GroupAverage(5); // Calculates and displays the average of the values in the sixth column (index 5) for each group.

Other calculation options include:

  • GroupAverage: Calculate the average value within a group
  • GroupMax: Find the maximum value in a group
  • GroupMin: Find the minimum value in a group
  • GroupDistinct: Count distinct values in a group

The calculation is a one-time operation. If you want to store calculations, check the Calculations chapter.

Group Sorting

You can also sort grouped data using the GroupSort method. This method lets you specify the direction of sorting for each column:

DataGrid.GroupSort(0, gsdAscending);  // Sort the first column in ascending order within the group

To sort by multiple columns:

DataGrid.GroupSort([0, 1], [gsdAscending, gsdDescending]);  // Sort first column ascending and second column descending order within the group

Ungrouping

To ungroup the data and return the grid to its original state, call the Ungroup method this can also be done by adding a button:

DataGrid.Ungroup();

Extra grouping features

Some extra capabilities for more visually appealing grouping can be set through the property grid.Options.Grouping. Through this property it can be enabled that group headers are automatically set in a different color and that cells from a group header are automatically merged. In addition, a group can also have a summary line. A summary line is an extra row below items that belong to the same group. This summary line can be used to put calculated group values in. The color for this summary line can also be automatically set as well as cell merging performed on this. See the grid.Options.Grouping description for all details.

Example

In this example of Grouping there are also Group Calculations, GroupAverage in column 'Cyl' and the sum in column 'Kw'

TMSFNCDATAGRID_GROUPING.png

Filtering

The TTMSFNCDataGrid allows you to filter data, enabling users to display only the rows that match specific criteria. This is particularly useful when working with large datasets, where you want to focus on a subset of the data.

Enabling Filtering

You can enable filtering on the grid by using the ApplyFilter method. To define a filter condition, use the Filter property and set conditions based on specific columns:

var
  fltr: TTMSFNCDataGridDataFilterData;
begin
  // Clear any existing filters
  Grid.Filter.Clear;

  // Add the filter condition (Column 1, value starts with 'A')
  fltr := Grid.Filter.Add;
  fltr.Column := 1; // First column (0-based index)
  fltr.Condition := 'A*'; // Condition: values starting with 'A'

  // Apply the filters
  Grid.ApplyFilter;
end;

You can apply multiple filters:

var
  fltr: TTMSFNCDataGridDataFilterData;
begin
  Grid.Filter.Clear;

  // Add the first filter condition (Column 1, value starts with 'A')
  fltr := Grid.Filter.Add;
  fltr.Column := 1; // First column (0-based index)
  fltr.Condition := 'A*'; // Condition: values starting with 'A'

  // Add the second filter condition (Column 3, value = 40)
  fltr := Grid.Filter.Add;
  fltr.Column := 3; // Fourth column (0-based index)
  fltr.Condition := '=40'; // Condition: values equal to 40

  // Apply the filters
  Grid.ApplyFilter;
end;

Custom Filter Options

The grid allows you to customize filter options using different operators, such as:

  • gftStartsWith: Checks if the cell starts with a particular substring.
  • gftEndsWith: Checks if the value ends with a particular substring.
  • gftContains: Checks if the cell contains a particular substring.
  • gftNotContains: Checks if the cell does not contain a particular substring.
  • gftEqual: Checks if the value equals the specified condition.
  • gftEmpty: Checks if the value is empty.
  • gftNotEmpty: Checks if the value is not empty.
  • gftNotEqual: Checks if the value does not equal the specified condition.
  • gftLargerThan: Filters values greater than the specified value.
  • gftSmallerThan: Filters values less than the specified value.
  • gftLargerOrEqualThan: Filters values greater or equal than the specified value.
  • gftSmallerOrEqualThan: Filters values smaller or equal than the specified value.

To apply this, set the public FilterType property while setting up the filter;

var
  fltr: TTMSFNCDataGridDataFilterData;
begin
  fltr := Grid.Filter.Add;
  fltr.FilterType := gftLargerThan;
  fltr.Column := 0;
  fltr.Condition := '40';

  Grid.ApplyFilter;
  //Results in a condition = "> 40"
end

Clearing Filters

To clear any applied filters and display all rows again, use the ClearFilter method:

Grid.ClearFilter();

TMSFNCDATAGRID_FILTERING.png

Filter Row

The filter row is a feature that can be used to apply filtering in the grid. If you already have data in the grid, you might need to start off by inserting a new row, making sure the filter editors are not covering potential data that needs to be taken into account when filtering. Typically, the filter row is part of the fixed rows. If you have a fixed row containing headers, it might be useful to add another fixed row.

Grid.FixedRowCount := 2;  
Grid.InsertRow(1);  

Enabling the filter row on top of the filter drop-down (available by default) is easy. We enable filtering first, then specify which kinds of filter options we want for runtime filtering and then specify which row needs to be allocated to show the filter options.

Grid.Options.Filtering.Enabled := True;  
Grid.Options.Filtering.Controls := [gfcButton, gfcEditor];  
Grid.Options.Filtering.Row := 1; 

TMSFNCDATAGRID_FILTERROW1.png

By default, enabling a filter row automatically detects which kind of editor is required. This can be customized at column level by using the following code.

Grid.ColumnByHeader('Joined').AddSetting(gcsFilterEditorType);  
Grid.ColumnByHeader('Joined').FilterEditorType := gfetDateTime;  

The following types are available

  • gfetAutomatic: Automatically detects the value type and chooses one of the other types.
  • gfetString: A standard edit for string based values
  • gfetBoolean: A checkbox for boolean based values
  • gfetDateTime: A date/time picker for TDateTime based values
  • gfetFloat: A spinbox for float values
  • gfetNumber: A spinbox for number values
  • gfetCustom: Specify your own editor

Additionally, when the column width is sufficiently large, a filter type selector and a clear button will appear.

Custom Filter Editor

When you are in need for a custom filter editor, you can select the filter type gfetCustom, which enables you to create your own editor via the OnCreateCustomFilterEditor event. Below is a sample to create a combobox which filters the progress column via a selection of ranges.

Grid.BeginUpdate;  
Grid.RemoveFilterEditor(MakeCell(Grid.ColumnIndexByHeader('Progress'), 1));  
Grid.Columns[Grid.ColumnIndexByHeader('Progress')].FilterEditorType := gfetCustom;  
Grid.Columns[Grid.ColumnIndexByHeader('Progress')].AddSetting(gcsFilterEditorType);  
Grid.EndUpdate;  

When we implement the OnCreateCustomFilterEditor you can create an instance of the control of your choice, set content and bind events for filtering purposes

function TForm1.GridCreateCustomFilterEditor(Sender: TObject;  
  ACell: TTMSFNCDataGridCellCoord): TTMSFNCDataGridCellControl;  
var  
  cbo: TComboBox;  
begin  
  Result := TComboBox.Create(nil);  
  cbo := TComboBox(Result);  
  cbo.Items.Add('');  
  cbo.Items.Add('<= 25');  
  cbo.Items.Add('> 25 & <= 50');  
  cbo.Items.Add('> 50 & <= 75');  
  cbo.Items.Add('> 75');  
  cbo.OnChange := DoComboChange;  
end;  

procedure TForm1.DoComboChange(Sender: TObject);  
var  
  cbo: TComboBox;  
  f: TTMSFNCDataGridDataFilterData;  
begin  
  cbo := TComboBox(Sender);  

  Grid.RemoveFilter;  
  f := Grid.Filter.ColumnFilter[Grid.ColumnIndexByHeader('Progress')];  
  f.Condition := cbo.Text;  
  Grid.ApplyFilter;  
end;  

TMSFNCDATAGRID_FILTERROW2.png

Chaining Filters

With the filter row as an option on top of the already built-in filtering features there are plenty of ways to browse through the data. If visual filtering is not applicable to your application, programmatic filtering can be an option and in this area we provide a way to chain filter operations together. Let's take the following example, where we find if the Name contains 'k', the Status equals 'Abroad' and the Progress is smaller than 50.

Grid.ClearFilter;  
Grid.Filter.Add(Grid.ColumnIndexByHeader('Name'), gftContains, 'k')  
           .&And(Grid.ColumnIndexByHeader('Status'), gftEqual, 'Abroad')  
           .&And(Grid.ColumnIndexByHeader('Progress'), gftSmallerThan, '50');  
Grid.ApplyFilter;  

TMSFNCDATAGRID_FILTERROW3.png

HTML formatted text, cell anchors, highlighting and marking in cells

The grid supports HTML formatted strings in cells. This is based on a small & fast HTML rendering engine. This engine implements a subset of the HTML standard to display formatted text. It supports following tags :

B : Bold tag

<B> : start bold text
</B> : end bold text

Example : This is a test

U : Underline tag

<U> : start underlined text
</U> : end underlined text
Example : This is a test

I : Italic tag

<I> : start italic text
</I> : end italic text
Example : This is a test

S : Strikeout tag

<S> : start strike-through text
</S> : end strike-through text
Example : This is a test

A : anchor tag

<A href="value"> : text after tag is an anchor. The 'value' after the href identifier is the anchor. This can be an URL (with ftp,http,mailto,file identifier) or any text.
If the value is an URL, the shellexecute function is called, otherwise, the anchor value can be found in the OnAnchorClick event </A> : end of anchor

Examples : This is a test This is a test This is a test

FONT : font specifier tag

<FONT face='facevalue' size='sizevalue' color='colorvalue' bgcolor='colorvalue'> : specifies font of text after tag.with
- face : name of the font - size : HTML style size if smaller than 5, otherwise pointsize of the font - color : font color with either hexidecimal color specification or color constant name, ie gcRed,gcYellow,gcWhite ... etc - bgcolor : background color with either hexidecimal color specification or color constant name : ends font setting

Examples : This is a test This is a test

HR : horizontal line

<HR> : inserts linebreak with horizontal line

BR : linebreak

<BR> : inserts a linebreak

BODY : body color / background specifier

<BODY bgcolor="colorvalue" [bgcolorto="colorvalue"] [dir="v|h"] background="imagefile
specifier"> : sets the background color of the HTML text or the background bitmap file

Example :

<BODY bgcolor="gcYellow"> : sets background color to yellow
<BODY background="file://c:\test.bmp"> : sets tiled background to file test.bmp
<BODY bgcolor="gcYellow" bgcolorto="gcWhite" dir="v"> : sets a vertical gradient from yellow to white 

IND : indent tag This is not part of the standard HTML tags but can be used to easily create multicolumn text

<IND x="indent"> : indents with "indent" pixels
Example : This will be indented 75 pixels.

IMG : image tag

<IMG src="specifier:name" [align="specifier"] [width="width"] [height="height"]
[alt="specifier:name"] > : inserts an image at the location

specifier can be: name of image in a BitmapContainer

Optionally, an alignment tag can be included. If no alignment is included, the text alignment with respect to the image is bottom. Other possibilities are: align="top" and align="middle"

The width & height to render the image can be specified as well. If the image is embedded in anchor tags, a different image can be displayed when the mouse is in the image area through the Alt attribute.

Examples : This is an image <IMG src="name">

SUB : subscript tag

<SUB> : start subscript text
</SUB> : end subscript text
Example : This is 9/16 looks like 9/16

SUP : superscript tag

<SUP> : start superscript text
</SUP> : end superscript text

UL : list tag

<UL> : start unordered list tag
</UL> : end unordered list
Example :
    <LI>List item 1
    <LI>List item 2
    <UL> 
    <LI> Sub list item A
    <LI> Sub list item B
    </UL>
    <LI>List item 3
    </UL>
    

    LI : list item

    <LI [type="specifier"] [color="color"] [name="imagename"]>: new list item specifier can be "square", "circle" or "image" bullet. Color sets the color of the square or circle bullet. Imagename sets the PictureContainer image name for image to use as bullet

    SHAD : text with shadow

    <SHAD> : start text with shadow
    </SHAD> : end text with shadow
    
    Z : hidden text
    <Z> : start hidden text
    </Z> : end hidden text
    

    Special characters Following standard HTML special characters are supported : < : less than : < > : greater than : > & : & " : "   : non breaking space ™ : trademark symbol € : euro symbol § : section symbol © : copyright symbol ¶ : paragraph symbol

    When hyperlinks are specified in grid cells, these hyperlinks are displayed underlined and in blue color. When the hyperlink is clicked, the OnCellAnchorClick event is triggered. Via HTML formatting, the grid also offers highlighting or marking of text in grid cells. This can be used to indicate text that matches a search or to show errors.

    Data Binding

    The TTMSFNCDataGrid supports data binding, allowing you to easily link the grid to various data sources, such as databases, arrays, or custom collections. This feature simplifies the process of displaying, editing, and managing large datasets within the grid.

    Setting Up Data Binding

    To bind the grid to a dataset, you need to set the DataAdapter property. This property connects the TTMSFNCDataGrid to a specific data source. For example, to bind to a dataset, you can use:

    DataGrid.Adapter := MyDatasetAdapter;
    

    The TTMSFNCDataGrid uses adapters to communicate with different data sources. These adapters handle the interaction between the grid and your data, automatically populating the grid with records and reflecting changes made in the dataset.

    Using Database Adapters

    When working with databases, use the TTMSFNCDataGridDatabaseAdapter to bind the grid to a database source. For example, to bind the grid to a TDataSet, you would use:

    var
      Adapter: TTMSFNCDataGridDatabaseAdapter;
    begin
      Adapter := TTMSFNCDataGridDatabaseAdapter.Create(Self);
      Adapter.DataSource := MyDataSource;  // Link to the dataset
      DataGrid.Adapter := Adapter;   // Bind the adapter to the grid
    end;
    

    Memo Fields, Boolean Fields & Image Blobs

    The TTMSFNCDataGridDatabaseAdapter can handle various data types and display them in a grid, such as memo fields, Boolean fields, and image blobs.

    Memo Fields

    • By default, memo fields are displayed as (MEMO) in the grid, similar to how it works in the default TGrid or TStringGrid in FMX and VCL.
    • To show the actual content of memo fields, set the global property ShowMemoFields = true.
    • To automatically adjust the row sizes based on the text in memo fields, call the following method after activating the dataset:
    Grid.AutoSizeRows(false, 4);
    

    Boolean Fields

    • A database field of type ftBoolean is displayed as true or false by default.
    • To display Boolean fields as checkboxes, set ShowBooleanFields = true.
    • Additionally, the adapter can show checkboxes for fields that are not of type ftBoolean, but contain two values (e.g., 1 for true and 0 for false). This is useful for databases that don't natively support Boolean fields.
    • To display checkboxes for such fields, configure the following properties:
    Adapter.Columns[columnindex].CheckBoxField = true;
    Grid.Columns[columnindex].Binding.BooleanTrueValue = <true_value>;
    Grid.Columns[columnindex].Binding.BooleanFalseValue = <false_value>;
    

    Image Blobs

    • By default, image blobs are displayed as (GRAPHIC).
    • To display actual images (BMP, GIF, JPEG) stored in BLOB fields, set ShowPictureFields = true.
    • If the field type is ftBlob (not ftGraphic), and the BLOB contains image data, you can manually set the column to display images by configuring:
    Columns[columnindex].PictureField = true;
    

    HTML Templates

    HTML templates allow you to customize how multiple fields are displayed in a single cell, including optional HTML formatting.

    • To set an HTML template, use the property Adapter.Columns[Index].HTMLTemplate.
    • The template is a string where DB field references like <#DBFIELDNAME> will be replaced by the actual field values for each record.

    Suppose a table has a field called Length (cm). You can format a cell with blue text and bold formatting using the following HTML template:

    Columns[2].HTMLTemplate := '<FONT color="gcBlue">Length is </FONT> <B><#Length (cm)> ft</B>';
    

    This would result in a grid cell that looks like this:

    TMSFNCDATAGRID_HTMLTEMPLATES.png

    Editing

    The adapter supports various inplace editors as well as the capability to use external controls as inplace editor for the grid. The “Editing” chapter in this document has an overview of all possible inplace editors that are included and how to use external editors as well. To enable editing in the grid, it is required that

    • The database table is editable
    • The database field is editable

    When a selected cell is clicked, the inplace editing starts. It is important to know that editing of a cell or row stops when the inplace editor loses focus. Only when it loses focus, the edited value is internally stored to post when the row changes.

    Master-Detail

    Master-Detail relationships using TTMSFNCDataGrid and TTMSFNCDataGridDatabaseAdapter allow developers to create intuitive and synchronized data representations, where a master grid shows parent records, and a detail grid reflects the corresponding child records based on the selected master entry. By linking the master and detail grids through the adapter, the detail grid can dynamically refresh and display the appropriate records from the child table. This is achieved by setting up datasets for both tables and using TDataSource components for database linking.

    1. Setting up Master-Detail Tables

    The master-detail relationship consists of:

    • Master Table: Contains parent records (e.g., `Orders`).
    • Detail Table: Contains child records linked to the master table (e.g., `OrderDetails` with a foreign key `OrderID`).

    2. Configuring Database Components

    1. Create Datasets
      • Create datasets for the master (`Orders`) and detail (`OrderDetails`) tables.
      • Use `TDataSource` components to link the datasets (`dsMaster` and `dsDetail`).
    2. Configure Database Adapters
      • Place two `TTMSFNCDataGridDatabaseAdapter` components on the form.
      • Set the `DataSource` of `MasterAdapter` to `dsMaster` and `DetailAdapter` to `dsDetail`.

    3. Linking Master and Detail Grids

    1. Master Grid Setup
      • Drop a `TTMSFNCDataGrid` component on the form and set it to display fields from the master dataset (`Orders`).
      • Bind this grid to `MasterAdapter`.
    2. Detail Grid Setup
      • Add another `TTMSFNCDataGrid` for the detail dataset (`OrderDetails`).
      • Bind this grid to `DetailAdapter`.
    3. Define Master-Detail Relationship
      • Set the `MasterSource` property of the detail dataset (`dsDetail`) to the master dataset’s `TDataSource`.
      • Define the `MasterFields` to specify the linking key (e.g., `OrderID`).
    4. Display Detail Data:
      • Set the DetailControl property of the `MasterAdapter` to `OrderDetails` Grid.

    The DetailControl property of the Database adapter is capable of hosting any type of control in a Master-Detail relationship.

    TMSFNCDATAGRID_MASTER_DETAIL.png