Values, events, and interaction
TTMSFNCRangeSlider is a two-thumb slider for selecting a numeric range rather than a single value. Reach for it whenever a user needs to bound a quantity from both sides — a price band, a date span expressed as numbers, a min/max threshold for a filter, or a zoom window. It shares the appearance and interaction infrastructure of TTMSFNCTrackBar, but instead of one Value it exposes two independent positions, ValueLeft and ValueRight, that always stay ordered within the Min/Max bounds. This guide covers the value model, the snapping behaviour, the three change events and when each one fires, and how to wire the slider to live data. For visual styling — orientation, fill zones, tick marks, and custom thumb drawing — see Appearance and rendering.
Quick start
Drop a TTMSFNCRangeSlider on a form, define the bounds with Min/Max, set the initial selection with ValueLeft/ValueRight, and handle OnValueChanged to read the result after the user releases a thumb. The ALeft parameter tells you which thumb moved, but in most cases you simply read both bounds.
// Configure a price range filter from 0 to 1000
TMSFNCRangeSlider1.Min := 0;
TMSFNCRangeSlider1.Max := 1000;
TMSFNCRangeSlider1.ValueLeft := 100;
TMSFNCRangeSlider1.ValueRight := 750;
procedure TForm1.TMSFNCRangeSlider1ValueChanged(Sender: TObject;
AValue: Single; ALeft: Boolean);
begin
if ALeft then
LabelMin.Caption := 'Min: ' + Format('%.0f', [TMSFNCRangeSlider1.ValueLeft])
else
LabelMax.Caption := 'Max: ' + Format('%.0f', [TMSFNCRangeSlider1.ValueRight]);
end;
The value model
Four Single properties define the slider:
| Property | Meaning | Constraint |
|---|---|---|
Min |
Lower bound of the track. | — |
Max |
Upper bound of the track. | Must be greater than Min. |
ValueLeft |
Position of the left (lower) thumb. | Clamped between Min and ValueRight. |
ValueRight |
Position of the right (upper) thumb. | Clamped between ValueLeft and Max. |
The two thumbs can never cross: assigning a ValueLeft greater than ValueRight (or vice versa) is clamped to the neighbouring thumb, so your handlers never see an inverted range. This means you can read ValueLeft and ValueRight directly without re-sorting them.
Snapping with Frequency
By default the thumbs move continuously. Set Interaction.Frequency to snap both thumbs to a fixed step while dragging — for example Frequency := 1 for whole numbers, or Frequency := 5 to allow only multiples of five. A Frequency of 0 restores free, continuous movement.
{ Allow only multiples of 5 between the thumbs: }
TMSFNCRangeSlider1.Min := 0;
TMSFNCRangeSlider1.Max := 100;
TMSFNCRangeSlider1.Interaction.Frequency := 5;
Frequency is a presentation-and-input constraint: it governs the values the user can land on by dragging, but assigning ValueLeft/ValueRight in code is not rounded to the frequency, so keep code-set values aligned to the step if you want them to match the user-reachable grid.
Change events and timing
The range slider raises three distinct events. Choosing the right one matters for performance: the difference between while dragging and after release is the difference between recomputing a filter on every pixel of movement versus once per gesture.
| Event | Signature | When it fires | Use for |
|---|---|---|---|
OnValueChange |
(Sender; AValue: Single; ALeft: Boolean) |
Continuously while a thumb is being dragged. | Cheap live preview — updating a caption or a highlight. |
OnValueChanged |
(Sender; AValue: Single; ALeft: Boolean) |
Once after the thumb is released. | The expensive commit — querying, filtering, saving. |
OnChanged |
(Sender) |
Whenever the control state changes and is invalidated. | Coarse "something changed" notifications. |
In both value events, AValue is the new value of the thumb that moved and ALeft is True for the left thumb, False for the right. Because the control keeps the bounds ordered for you, reading ValueLeft and ValueRight inside the handler is always safe.
{ Inside your form's OnCreate, after dropping a TTMSFNCRangeSlider: }
procedure TForm1.FormCreate(Sender: TObject);
begin
TMSFNCRangeSlider1.Min := 0;
TMSFNCRangeSlider1.Max := 100;
TMSFNCRangeSlider1.ValueLeft := 25;
TMSFNCRangeSlider1.ValueRight := 75;
// Snap both thumbs to whole units while dragging.
TMSFNCRangeSlider1.Interaction.Frequency := 1;
TMSFNCRangeSlider1.OnValueChange := RangeSliderValueChange;
TMSFNCRangeSlider1.OnValueChanged := RangeSliderValueChanged;
end;
// Fires continuously WHILE a thumb is being dragged - use for live preview only.
procedure TForm1.RangeSliderValueChange(Sender: TObject; AValue: Single;
ALeft: Boolean);
begin
LabelRange.Caption := Format('%.0f - %.0f',
[TMSFNCRangeSlider1.ValueLeft, TMSFNCRangeSlider1.ValueRight]);
end;
// Fires once AFTER the thumb is released - use for the expensive commit (query, filter, save).
procedure TForm1.RangeSliderValueChanged(Sender: TObject; AValue: Single;
ALeft: Boolean);
begin
ApplyPriceFilter(TMSFNCRangeSlider1.ValueLeft, TMSFNCRangeSlider1.ValueRight);
end;
Note
OnValueChange was added in version 1.0.3.0. On older builds only OnValueChanged (the after-release event) is available.
Tip
The range slider also participates in TMS FNC data binding: as a thumb moves it notifies any attached TTMSFNCDataBinder, so a property bound through the data binding editor updates without manual event wiring. When the slider is bound this way, keep your own commit logic in OnValueChanged so you do not duplicate work the binder already performs during the drag.
Combining live preview with a committed filter
The highest-value pattern wires both value events together: OnValueChange keeps an on-screen label in sync as the user drags, while OnValueChanged runs the actual filter only once the thumb is released. This keeps the UI responsive without re-querying the data on every pixel of movement.
// Apply a range filter to a list whenever either thumb moves
procedure TForm1.TMSFNCRangeSlider1ValueChanged(Sender: TObject;
AValue: Single; ALeft: Boolean);
var
lo, hi: Single;
begin
lo := TMSFNCRangeSlider1.ValueLeft;
hi := TMSFNCRangeSlider1.ValueRight;
LabelRange.Caption := Format('%.0f – %.0f', [lo, hi]);
// Re-filter the data grid to show only rows within the price range
DataGrid1.BeginUpdate;
try
FilterProductsByPrice(lo, hi);
finally
DataGrid1.EndUpdate;
end;
end;
The filter handler reads ValueLeft and ValueRight together in a single place, so the logic is identical regardless of which thumb triggered the event, and the grid is refreshed inside a BeginUpdate/EndUpdate pair to avoid flicker.
Common pitfalls
- Filtering inside
OnValueChange. This event fires on every step of the drag; running a database query or full re-filter there causes visible lag. Do live, cheap work inOnValueChangeand the expensive commit inOnValueChanged. - Expecting
ValueLeft > ValueRight. The thumbs cannot cross. If you need an inverted interpretation, swap the values yourself when reading them. - Code-set values ignoring
Frequency.Frequencysnaps user dragging only; values assigned in code are not rounded. Align them to the step if they must match.
See also
- Appearance and rendering — orientation, fill zones, tick marks, labels, and custom thumb drawing.
- Get started
TTMSFNCRangeSliderAPI reference- Range Slider release notes