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.
// TTMSFNCStrokeKindSelector shows all available stroke patterns in a grid.
// Read or set the selected kind via SelectedStrokeKind.
procedure TForm1.ConfigureStrokeKindSelector;
begin
TMSFNCStrokeKindSelector1.SelectedStrokeKind := gskSolid;
end;
procedure TForm1.TMSFNCStrokeKindSelector1StrokeKindSelected(Sender: TObject;
AStrokeKind: TTMSFNCGraphicsStrokeKind);
begin
// Apply the chosen stroke kind to a drawing stroke
ShapeStroke.Kind := AStrokeKind;
Panel1.Repaint;
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();
}
A selector dropped on a form in the designer is filled with the six stroke kinds, and those items are stored with the form. A selector created in code starts with an empty Items collection, so you can fill it with your own items; call InitializeDefault to add the standard set. InitializeDefault clears Items first and resets the grid to one column of six rows, so call it before you set Columns and Rows, and call it again to restore the defaults after editing Items. The picker calls it for its own dropdown selector, so a picker created in code needs no extra step.
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.
// TTMSFNCStrokeKindPicker opens the selector in a dropdown.
// Connect it to a graphics stroke via OnStrokeKindSelected.
procedure TForm1.ConfigureStrokeKindPicker;
begin
TMSFNCStrokeKindPicker1.SelectedStrokeKind := gskDash;
TMSFNCStrokeKindPicker1.CloseOnSelection := True;
end;
procedure TForm1.TMSFNCStrokeKindPicker1StrokeKindSelected(Sender: TObject;
AStrokeKind: TTMSFNCGraphicsStrokeKind);
begin
ShapeStroke.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.
// 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.
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))));
}