Table of Contents

Spinner Guide

TTMSFNCSpinner is a scrollable drum-wheel selector — the touch-friendly picker familiar from mobile date and time entry. It holds one or more independent columns in its Columns collection, and the user flicks each column up or down to change its selected value. Each column is configured by its RangeType: a numeric range (rtNumber), a date/time range (rtDateTime), or a custom list of named items (rtCustom). Reach for the spinner instead of separate edits or combo boxes when you want a compact, gesture-driven way to pick one value per column — a quantity, a date, a time, or a labelled choice — especially on touch devices. This guide covers each column type, mixing types in one control, appearance and scrolling behaviour, and reading the result from OnSelectedValueChanged.

TTMSFNCSpinner with date and time columns

Numeric columns

Set RangeType := rtNumber, configure RangeFrom, RangeTo, and ValueFormat, then read SelectedValue from the OnSelectedValueChanged event. Set Cyclic := True to make the wheel wrap at either end.

// Two-column spinner: day-of-week and an hour selector
procedure TForm1.FormCreate(Sender: TObject);
begin
  // First column: numeric range 1 to 7 formatted as day abbreviations
  TMSFNCSpinner1.Columns[0].RangeType := rtNumber;
  TMSFNCSpinner1.Columns[0].RangeFrom := 1;
  TMSFNCSpinner1.Columns[0].RangeTo   := 7;
  TMSFNCSpinner1.Columns[0].ValueFormat := '%.0f';

  // Second column: hours 0-23, cyclic so it wraps around
  TMSFNCSpinner1.Columns[1].RangeType := rtNumber;
  TMSFNCSpinner1.Columns[1].RangeFrom := 0;
  TMSFNCSpinner1.Columns[1].RangeTo   := 23;
  TMSFNCSpinner1.Columns[1].ValueFormat := '%02.0f';
  TMSFNCSpinner1.Columns[1].Cyclic := True;
end;

procedure TForm1.TMSFNCSpinner1SelectedValueChanged(Sender: TObject;
  AColumn, ASelectedCustomIndex: Integer; ASelectedValue: Double;
  ARangeType: TTMSFNCSpinnerRangeType);
begin
  LabelDay.Caption  := 'Day '  + IntToStr(Round(TMSFNCSpinner1.Columns[0].SelectedValue));
  LabelHour.Caption := 'Hour ' + IntToStr(Round(TMSFNCSpinner1.Columns[1].SelectedValue));
end;

Date and time columns

RangeType := rtDateTime combined with StepType (stDay, stHour, stMinute, etc.) configures a date or time drum wheel. DateRangeFrom/DateRangeTo set the calendar boundaries; DateTimeValueFormat formats the displayed value using FormatDateTime patterns.

// Date/time spinner: day column + hour and minute columns
procedure TForm1.FormCreate(Sender: TObject);
begin
  // Day column spanning the next 10 years
  TMSFNCSpinner1.Columns[0].RangeType := rtDateTime;
  TMSFNCSpinner1.Columns[0].StepType  := stDay;
  TMSFNCSpinner1.Columns[0].DateRangeFrom := Now;
  TMSFNCSpinner1.Columns[0].DateRangeTo   := Now + 365 * 10;
  TMSFNCSpinner1.Columns[0].DateTimeValueFormat := 'ddd dd MMM';

  // Hour column 0-23, cyclic
  TMSFNCSpinner1.Columns[1].RangeType := rtNumber;
  TMSFNCSpinner1.Columns[1].RangeFrom := 0;
  TMSFNCSpinner1.Columns[1].RangeTo   := 23;
  TMSFNCSpinner1.Columns[1].ValueFormat := '%d';
  TMSFNCSpinner1.Columns[1].Cyclic := True;

  // Minute column in steps of 5, cyclic
  TMSFNCSpinner1.Columns[2].RangeType := rtNumber;
  TMSFNCSpinner1.Columns[2].RangeFrom := 0;
  TMSFNCSpinner1.Columns[2].RangeTo   := 55;
  TMSFNCSpinner1.Columns[2].Step      := 5;
  TMSFNCSpinner1.Columns[2].ValueFormat := '%.2d';
  TMSFNCSpinner1.Columns[2].Cyclic := True;
end;

Custom items

When neither a numeric nor a datetime range suits the use case, set RangeType := rtCustom and populate the CustomItems collection. Each item carries a Text, Value, and optionally a PictureName for image display.

// Custom items column: the user scrolls through named choices
// instead of a numeric or datetime range.
procedure TForm1.FormCreate(Sender: TObject);
var
  item: TTMSFNCSpinnerCustomItem;
begin
  TMSFNCSpinner1.Columns[0].RangeType := rtCustom;

  item := TMSFNCSpinner1.Columns[0].CustomItems.Add;
  item.Text := 'Small';
  item.Value := 1;

  item := TMSFNCSpinner1.Columns[0].CustomItems.Add;
  item.Text := 'Medium';
  item.Value := 2;

  item := TMSFNCSpinner1.Columns[0].CustomItems.Add;
  item.Text := 'Large';
  item.Value := 3;

  // Start on 'Medium'
  TMSFNCSpinner1.Columns[0].SelectedCustomIndex := 1;
end;

procedure TForm1.TMSFNCSpinner1SelectedValueChanged(Sender: TObject;
  AColumn, ASelectedCustomIndex: Integer; ASelectedValue: Double;
  ARangeType: TTMSFNCSpinnerRangeType);
begin
  // For rtCustom columns, ASelectedCustomIndex is the index into CustomItems.
  LabelSize.Caption := 'Size index: ' + IntToStr(ASelectedCustomIndex);
end;

Appearance

The ColumnAppearance property applies visual settings across all columns (fill, spacing, auto-size). AutoSize lets the spinner calculate column widths automatically. The Appearance property controls the selection rectangle and gradient overlays (TopLayerColor, BottomLayerColor, SelectedFill, SelectedStroke, SelectedHeight, SelectedRounding).

SmoothScrolling := True keeps the thumb snapping to the nearest value only on mouse-button release rather than on every move, producing a smoother feel for fast drags.

Combining date, time, and custom columns

The three column types can be mixed freely in a single spinner. The datetime example below combines a day column with hour and minute columns to build a date-and-time selection control:

// Date/time spinner: day column + hour and minute columns
procedure TForm1.FormCreate(Sender: TObject);
begin
  // Day column spanning the next 10 years
  TMSFNCSpinner1.Columns[0].RangeType := rtDateTime;
  TMSFNCSpinner1.Columns[0].StepType  := stDay;
  TMSFNCSpinner1.Columns[0].DateRangeFrom := Now;
  TMSFNCSpinner1.Columns[0].DateRangeTo   := Now + 365 * 10;
  TMSFNCSpinner1.Columns[0].DateTimeValueFormat := 'ddd dd MMM';

  // Hour column 0-23, cyclic
  TMSFNCSpinner1.Columns[1].RangeType := rtNumber;
  TMSFNCSpinner1.Columns[1].RangeFrom := 0;
  TMSFNCSpinner1.Columns[1].RangeTo   := 23;
  TMSFNCSpinner1.Columns[1].ValueFormat := '%d';
  TMSFNCSpinner1.Columns[1].Cyclic := True;

  // Minute column in steps of 5, cyclic
  TMSFNCSpinner1.Columns[2].RangeType := rtNumber;
  TMSFNCSpinner1.Columns[2].RangeFrom := 0;
  TMSFNCSpinner1.Columns[2].RangeTo   := 55;
  TMSFNCSpinner1.Columns[2].Step      := 5;
  TMSFNCSpinner1.Columns[2].ValueFormat := '%.2d';
  TMSFNCSpinner1.Columns[2].Cyclic := True;
end;

Read the result across columns in OnSelectedValueChanged using SelectedValue (numeric) or convert back to a TDateTime with EncodeDate/EncodeTime based on the column's RangeType.

Common pitfalls

  • OnSelectedValueChanged has five parameters. The handler signature is (Sender: TObject; AColumn, ASelectedCustomIndex: Integer; ASelectedValue: Double; ARangeType: TTMSFNCSpinnerRangeType). AColumn is the column index, not a column object — index into Columns[AColumn] to read the column.
  • Match the range setter to the RangeType. Numeric columns use RangeFrom/RangeTo; date/time columns use DateRangeFrom/DateRangeTo with a StepType; custom columns use CustomItems. Setting numeric bounds on a rtDateTime column has no effect.
  • Custom selection is an index. For rtCustom, read ASelectedCustomIndex (or Columns[i].SelectedCustomIndex) — SelectedValue carries the item's Value, not its position.
  • SmoothScrolling changes snap timing. With SmoothScrolling := True the wheel snaps to the nearest value only on release, not during the drag; account for this if you preview the value mid-scroll.

See also