Skip to main content
Table of Contents

Stroke Kind Guide

The stroke kind components let users choose a line style (TTMSFNCGraphicsStrokeKind) for use with graphics strokes, pens, or drawing tools. Two controls serve this purpose:

Component Presentation
TTMSFNCStrokeKindSelector An always-visible grid showing all stroke pattern samples
TTMSFNCStrokeKindPicker A compact control that opens the selector in a dropdown

Available stroke kinds include: gskSolid, gskDash, gskDot, gskDashDot, gskDashDotDot, and gskNone.

Using the selector

The selector displays all items in a grid. Read SelectedStrokeKind to get the current choice, or handle OnStrokeKindSelected to react immediately.

Using the selectorPascalC++
// TTMSFNCStrokeKindSelector shows all available stroke patterns in a grid.
// Read or set the selected kind via SelectedStrokeKind.
TMSFNCStrokeKindSelector1.SelectedStrokeKind := gskSolid;

procedure TForm1.TMSFNCStrokeKindSelector1StrokeKindSelected(Sender: TObject;
  AStrokeKind: TTMSFNCGraphicsStrokeKind);
begin
  // Apply the chosen stroke kind to a drawing stroke
  ShapeStroke.Kind := AStrokeKind;
  Panel1.Invalidate;
end;
// TTMSFNCStrokeKindSelector shows all available stroke patterns in a grid.
// Read or set the selected kind via SelectedStrokeKind.
void __fastcall TForm1::ConfigureStrokeKindSelector()
{
  TMSFNCStrokeKindSelector1->SelectedStrokeKind = gskSolid;
}

void __fastcall TForm1::TMSFNCStrokeKindSelector1StrokeKindSelected(TObject *Sender,
  TTMSFNCGraphicsStrokeKind AStrokeKind)
{
  // Apply the chosen stroke kind to a drawing stroke
  ShapeStroke->Kind = AStrokeKind;
  Panel1->Repaint();
}

Using the picker

The picker is suited to toolbars where space is limited. Setting CloseOnSelection := True collapses the dropdown automatically after a choice is made.

Using the pickerPascalC++
// TTMSFNCStrokeKindPicker opens the selector in a dropdown.
// Connect it to a graphics stroke via OnStrokeKindSelected.
TMSFNCStrokeKindPicker1.SelectedStrokeKind := gskDash;
TMSFNCStrokeKindPicker1.CloseOnSelection   := True;

procedure TForm1.TMSFNCStrokeKindPicker1StrokeKindSelected(Sender: TObject;
  AStrokeKind: TTMSFNCGraphicsStrokeKind);
begin
  TMSFNCRichEditor1.Pen.Kind := AStrokeKind;
end;
// TTMSFNCStrokeKindPicker opens the selector in a dropdown.
// Connect it to a graphics stroke via OnStrokeKindSelected.
void __fastcall TForm1::ConfigureStrokeKindPicker()
{
  TMSFNCStrokeKindPicker1->SelectedStrokeKind = gskDash;
  TMSFNCStrokeKindPicker1->CloseOnSelection = true;
}

void __fastcall TForm1::TMSFNCStrokeKindPicker1StrokeKindSelected(TObject *Sender,
  TTMSFNCGraphicsStrokeKind AStrokeKind)
{
  ShapeStroke->Kind = AStrokeKind;
}

Combining selector and picker

When the application shows both a settings panel (selector) and a toolbar (picker), keeping their SelectedStrokeKind in sync provides a consistent UI.

Combining selector and pickerPascalC++
// Use selector and picker together: selector previews all choices in-place,
// picker saves space in a toolbar. Both share the same selected kind.
procedure TForm1.FormCreate(Sender: TObject);
begin
  TMSFNCStrokeKindSelector1.SelectedStrokeKind := gskSolid;
  TMSFNCStrokeKindPicker1.SelectedStrokeKind   := gskSolid;

  // Keep them in sync when the selector changes
  TMSFNCStrokeKindSelector1.OnStrokeKindSelected := SelectorKindChanged;
end;

procedure TForm1.SelectorKindChanged(Sender: TObject;
  AStrokeKind: TTMSFNCGraphicsStrokeKind);
begin
  TMSFNCStrokeKindPicker1.SelectedStrokeKind := AStrokeKind;
  ApplyStrokeKindToCanvas(AStrokeKind);
end;
// Use selector and picker together: selector previews all choices in-place,
// picker saves space in a toolbar. Both share the same selected kind.
void __fastcall TForm1::FormCreate(TObject *Sender)
{
  TMSFNCStrokeKindSelector1->SelectedStrokeKind = gskSolid;
  TMSFNCStrokeKindPicker1->SelectedStrokeKind = gskSolid;

  // Keep them in sync when the selector changes
  TMSFNCStrokeKindSelector1->OnStrokeKindSelected = SelectorKindChanged;
}

void __fastcall TForm1::SelectorKindChanged(TObject *Sender,
  TTMSFNCGraphicsStrokeKind AStrokeKind)
{
  TMSFNCStrokeKindPicker1->SelectedStrokeKind = AStrokeKind;
  ApplyStrokeKindToCanvas(AStrokeKind);
}

Sizing for high-DPI displays

Rows and Columns are counts, so they are never rescaled - the grid keeps its shape and the cells grow with the display scale. The measurements that are rescaled are the selector's Appearance.HorizontalSpacing / VerticalSpacing and Appearance.Font, and the picker's DropDownWidth / DropDownHeight. Assign all of those in design units once, before the first paint, and let the DPI pass grow them; assigning already-scaled values scales them twice.

Size the stroke-kind grid and dropdown for every display scalePascalC++
uses
  System.SysUtils, FMX.TMSFNCUtils, FMX.TMSFNCCustomSelector,
  FMX.TMSFNCStrokeKindSelector, FMX.TMSFNCStrokeKindPicker;

procedure TForm1.ApplyStrokeKindMetrics;
begin
  TMSFNCStrokeKindSelector1.BeginUpdate;
  try
    { Rows and Columns are counts, so they are never rescaled: the grid keeps
      its shape and the cells grow with the display scale. }
    TMSFNCStrokeKindSelector1.Columns := 3;
    TMSFNCStrokeKindSelector1.Rows := 2;

    { The selector rescales Appearance.HorizontalSpacing, VerticalSpacing and
      Appearance.Font when the display scale changes, so these are design
      values - assign them once, before the first paint. }
    TMSFNCStrokeKindSelector1.Appearance.HorizontalSpacing := 4;
    TMSFNCStrokeKindSelector1.Appearance.VerticalSpacing := 4;
    TMSFNCStrokeKindSelector1.Appearance.Font.Size := 10;
  finally
    TMSFNCStrokeKindSelector1.EndUpdate;
  end;

  TMSFNCStrokeKindPicker1.BeginUpdate;
  try
    { The picker rescales DropDownWidth and DropDownHeight in the same pass,
      so give it the design-DPI box and let the pass grow it. }
    TMSFNCStrokeKindPicker1.DropDownWidth := 180;
    TMSFNCStrokeKindPicker1.DropDownHeight := 120;
    TMSFNCStrokeKindPicker1.CloseOnSelection := True;
  finally
    TMSFNCStrokeKindPicker1.EndUpdate;
  end;

  if TTMSFNCUtils.IsHighDPIScale(Self) then
    StatusLabel.Text := Format('Stroke kind grid laid out at %.2fx',
      [TTMSFNCUtils.GetDPIScale(Self)]);
end;
void __fastcall TForm1::ApplyStrokeKindMetrics()
{
  TMSFNCStrokeKindSelector1->BeginUpdate();
  try
  {
    // Rows and Columns are counts, so they are never rescaled: the grid keeps
    // its shape and the cells grow with the display scale.
    TMSFNCStrokeKindSelector1->Columns = 3;
    TMSFNCStrokeKindSelector1->Rows = 2;

    // The selector rescales Appearance.HorizontalSpacing, VerticalSpacing and
    // Appearance.Font when the display scale changes, so these are design
    // values - assign them once, before the first paint.
    TMSFNCStrokeKindSelector1->Appearance->HorizontalSpacing = 4;
    TMSFNCStrokeKindSelector1->Appearance->VerticalSpacing = 4;
    TMSFNCStrokeKindSelector1->Appearance->Font->Size = 10;
  }
  __finally
  {
    TMSFNCStrokeKindSelector1->EndUpdate();
  }

  TMSFNCStrokeKindPicker1->BeginUpdate();
  try
  {
    // The picker rescales DropDownWidth and DropDownHeight in the same pass,
    // so give it the design-DPI box and let the pass grow it.
    TMSFNCStrokeKindPicker1->DropDownWidth = 180;
    TMSFNCStrokeKindPicker1->DropDownHeight = 120;
    TMSFNCStrokeKindPicker1->CloseOnSelection = true;
  }
  __finally
  {
    TMSFNCStrokeKindPicker1->EndUpdate();
  }

  if (TTMSFNCUtils::IsHighDPIScale(this))
    StatusLabel->Text = Format("Stroke kind grid laid out at %.2fx",
      ARRAYOFCONST(((Extended)TTMSFNCUtils::GetDPIScale(this))));
}