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.
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
OnSelectedValueChangedhas five parameters. The handler signature is(Sender: TObject; AColumn, ASelectedCustomIndex: Integer; ASelectedValue: Double; ARangeType: TTMSFNCSpinnerRangeType).AColumnis the column index, not a column object — index intoColumns[AColumn]to read the column.- Match the range setter to the
RangeType. Numeric columns useRangeFrom/RangeTo; date/time columns useDateRangeFrom/DateRangeTowith aStepType; custom columns useCustomItems. Setting numeric bounds on artDateTimecolumn has no effect. - Custom selection is an index. For
rtCustom, readASelectedCustomIndex(orColumns[i].SelectedCustomIndex) —SelectedValuecarries the item'sValue, not its position. SmoothScrollingchanges snap timing. WithSmoothScrolling := Truethe wheel snaps to the nearest value only on release, not during the drag; account for this if you preview the value mid-scroll.