Table of Contents

Renderer source

Render a single grid from an external, prepared "layer" — a complete renderer with its own data, appearance, and scroll state — and swap between layers with one property assignment. Use it to keep several ready-made views alive at once and flip the visible one instantly, without rebuilding or reloading the grid.

Overview

A TTMSFNCDataGrid normally owns and draws its own renderer layer: the cells you fill, the theme you set, and the scroll position all belong to that one grid. A renderer source lets you move that layer outside the grid into a reusable component, then link it back by reference.

TTMSFNCDataGridRendererSource wraps a standalone TTMSFNCDataGridRenderer instance — a full layer with its own data and appearance. Assign one to the grid's RendererSource property and the grid renders that instance: its data, its theme, its scroll behaviour. The grid keeps no copy, so edits to the active layer show up live. Set RendererSource back to nil and the grid reverts to its own built-in renderer and data, untouched.

Reach for a renderer source when you want several prepared views and one grid to show them: a dashboard that flips between "Sales" and "Inventory" tables, a wizard that swaps datasets between steps, or an A/B comparison where each layer keeps its own scroll position and selection while it sits in the background. When you only ever show one dataset, you do not need this — fill the grid directly. When you need data operations with no grid at all, use the headless data layer instead.

How a layer fits the three-layer design

The grid is built from a component facade, a core, a data layer, and a renderer (see Architecture). The renderer is the layer that holds drawing state, appearance, and the cell data it paints. A renderer source externalises exactly that layer:

  • TTMSFNCDataGridRendererSource.Renderer is the owned TTMSFNCDataGridRenderer. Configure cells, fixed rows, CellAppearance, and Options on it exactly as you would on a grid.
  • The source implements ITMSFNCDataGridRendererSource, whose single GetRendererInstance method hands the grid the instance to draw. The grid links to whatever component implements that contract, not to a fixed class.
  • While a source is linked, the grid facade (Cells[], RowCount, Sort, selection, …) operates on the active layer — so all your usual grid code keeps working, it just targets the linked layer.

Quick start

Create a source, configure its Renderer like a grid, and link it:

procedure TForm1.BuildSalesLayer;
var
  src: TTMSFNCDataGridRendererSource;
  r: TTMSFNCDataGridRenderer;
  i: Integer;
begin
  // A renderer source owns a complete, standalone "layer": its own renderer
  // instance, its own data and its own appearance. It renders nothing on its
  // own - it only becomes visible once linked to a grid's RendererSource.
  src := TTMSFNCDataGridRendererSource.Create(Self);
  src.Name := 'SalesLayer';
  FSalesLayer := src;

  // Configure the layer through its owned Renderer. Everything you can set on a
  // grid - cells, fixed rows, appearance, column stretching - is set here.
  r := src.Renderer;
  r.BeginUpdate;
  try
    r.CellAppearance.FixedLayout.Fill.Color := gcSteelblue;
    r.CellAppearance.FixedLayout.Font.Color := gcWhite;
    r.CellAppearance.NormalLayout.Fill.Color := gcAliceblue;
    r.Options.Column.Stretching.Enabled := True;

    r.FixedRowCount := 1;
    r.ColumnCount := 3;
    r.RowCount := 4;

    r.Cells[0, 0] := 'Region';
    r.Cells[1, 0] := 'Q1';
    r.Cells[2, 0] := 'Q2';
    for i := 1 to 3 do
    begin
      r.Cells[0, i] := Format('Region %d', [i]);
      r.Cells[1, i] := (400 + i * 10).ToString;
      r.Cells[2, i] := (450 + i * 10).ToString;
    end;
  finally
    r.EndUpdate;
  end;
end;

Once the layer is built, one assignment shows it in the grid. The screenshot below shows the grid rendering the linked "Sales by Region" layer with the layer's own blue theme — the grid component itself was never themed or filled; its data, appearance, and scroll state all come from the linked layer, by reference:

DataGrid rendering an external Sales by Region renderer-source layer with the layer's own blue theme DataGrid rendering an external Sales by Region renderer-source layer styled for dark mode through the layer's own appearance

Switching layers at runtime

Prepare each layer once, then flip the visible one with a single property write. Editing through the grid facade targets whichever layer is active, and each layer remembers its own state while another is shown:

procedure TForm1.ShowSalesLayer(Sender: TObject);
begin
  // One assignment swaps the entire layer by reference: data, appearance and
  // scroll position all come from the source. The grid keeps no copy.
  Grid.RendererSource := FSalesLayer;
end;

procedure TForm1.ShowOwnLayer(Sender: TObject);
begin
  // nil reverts the grid to its own built-in renderer and its own data, exactly
  // as it was before any source was linked.
  Grid.RendererSource := nil;
end;

procedure TForm1.AppendRowToActiveLayer(Sender: TObject);
var
  newRow: Integer;
begin
  // The grid facade always targets the ACTIVE layer. While a source is linked,
  // edits flow into that layer and are kept by it - switch away and back and the
  // change is still there, because the grid renders the source by reference.
  Grid.BeginUpdate;
  try
    newRow := Grid.RowCount;
    Grid.RowCount := newRow + 1;
    Grid.Cells[0, newRow] := Format('Added row %d', [newRow]);
  finally
    Grid.EndUpdate;
  end;
end;

This is the combination that makes renderer sources useful: switching and editing the active layer together. Build two sources up front, link one, append a row through Grid.Cells[] — that row belongs to the linked layer. Switch to the second layer, edit it, switch back, and the first layer's added row is still there because the grid renders the source by reference rather than copying it.

Supplying a custom renderer class

The source instantiates its renderer from GetRendererInstanceClass. Subclass the source and override that protected method to inject a custom TTMSFNCDataGridRenderer descendant — for example, one with a custom cell class or overridden drawing:

type
  TMyRendererSource = class(TTMSFNCDataGridRendererSource)
  protected
    function GetRendererInstanceClass: TTMSFNCDataGridRendererClass; override;
  end;

function TMyRendererSource.GetRendererInstanceClass: TTMSFNCDataGridRendererClass;
begin
  Result := TMyCustomRenderer;   // your TTMSFNCDataGridRenderer descendant
end;

The grid still links through the same RendererSource property and ITMSFNCDataGridRendererSource contract — only the renderer class it draws changes.

Pitfalls

  • Lifetime and free-notification. The grid holds the source by reference and registers free-notification, so freeing a linked source automatically resets RendererSource to nil and reverts the grid — but the source is a real component you own. Give it an AOwner (or free it yourself) so the layer is not leaked when the form closes.
  • nil reverts, it does not clear. Setting RendererSource := nil restores the grid's own layer and data exactly as before; it does not empty the grid. The external layer keeps its data for the next time you link it.
  • Edits go to the active layer, not the grid's own layer. While a source is linked, Grid.Cells[...] := … changes the linked layer. If you meant to edit the grid's built-in data, set RendererSource := nil first.
  • Configure the layer through Source.Renderer. Theme and fill the source's own Renderer before (or after) linking — styling the grid directly has no effect while an external layer is active, because the grid is drawing the source's instance.

See also