Progress and rating
TTMSFNCProgressBar displays a horizontal or vertical fill that represents a numeric value between Minimum and Maximum. The same base class powers TTMSFNCRating, which divides the bar into discrete blocks so users can tap or drag to set a star-style rating.
Basic usage
Set Minimum, Maximum, and Value at design time or runtime. The bar redraws immediately when Value changes:
// Show progress of a background operation
procedure TForm1.StartOperation;
begin
TMSFNCProgressBar1.Minimum := 0;
TMSFNCProgressBar1.Maximum := 100;
TMSFNCProgressBar1.Value := 0;
end;
procedure TForm1.UpdateProgress(APercent: Integer);
begin
TMSFNCProgressBar1.Value := APercent;
end;
Shape and segmented display
Layout.Shape switches between psHorizontalLine (default) and psVerticalLine. Layout.Blocks splits the filled area into equal segments with Layout.Spacing pixels of gap between them:
TMSFNCProgressBar1.Layout.Shape := psVerticalLine;
TMSFNCProgressBar1.Layout.Blocks := 8;
TMSFNCProgressBar1.Layout.Spacing := 2;
Displaying the value label
Set Layout.ShowValue to True to draw a text label. Layout.ValueType controls the content:
| Value | Label shows |
|---|---|
pvtPercentage |
Percentage of the range (default) |
pvtValue |
The raw numeric Value |
pvtCustom |
The string you assign to CustomValue |
Position the label with Layout.TextPosition (ptpStart, ptpCenter, ptpEnd, ptpProgressEnd, ptpFollowCenter, ptpFollowEnd, ptpProgressCenter). Rotate the label with Layout.TextOrientation (ptoHorizontal, ptoLeft, ptoRight, ptoUpsideDown).
Add Layout.Prefix and Layout.Suffix strings to wrap the label, for example '%'.
Appearance and threshold levels
Appearance.Fill styles the filled (active) portion; Appearance.DefaultLevelFill styles the unfilled (inactive) background. Use the Levels collection to change colour automatically when Value crosses a threshold:
// Vertical segmented bar with percentage label
procedure TForm1.ConfigureProgressBar;
begin
with TMSFNCProgressBar1 do
begin
// Five equal blocks with gaps between them
Layout.Blocks := 5;
Layout.Spacing := 3;
Layout.Shape := psVerticalLine;
// Label: "72%" centred inside the filled area
Layout.ShowValue := True;
Layout.ValueType := pvtPercentage;
Layout.Decimals := 0;
Layout.Suffix := '%';
Layout.TextPosition := ptpCenter;
// Progress fill colour and background
Appearance.Fill.Color := $0015A34A;
Appearance.DefaultLevelFill.Color := $00E0E0E0;
end;
end;
// Threshold-based colour change using Levels
procedure TForm1.AddLevels;
var
L: TTMSFNCCustomProgressBarLevel;
begin
TMSFNCProgressBar1.Levels.Clear;
L := TTMSFNCCustomProgressBarLevel(TMSFNCProgressBar1.Levels.Add);
L.LevelPosition := 50; // switches at 50 %
L.Fill.Color := $000080FF; // blue above 50 %
L := TTMSFNCCustomProgressBarLevel(TMSFNCProgressBar1.Levels.Add);
L.LevelPosition := 80;
L.Fill.Color := $00E53935; // red above 80 %
end;
Appearance.Stacked makes multiple levels stack visually from left to right rather than replacing each other.
Reacting to level changes
OnLevelsChanged fires whenever the Levels collection is modified: an item is added, removed, or reordered, or an existing level's LevelPosition, Fill, Stroke, or ActiveFont is edited. Use it to keep dependent UI in sync — for example a legend or status label that reports how many thresholds are configured — without polling Levels.Count elsewhere in your code:
procedure TForm1.TMSFNCProgressBar1LevelsChanged(Sender: TObject);
begin
// Fires for any change to the Levels collection: items added or removed,
// reordered, or a level's LevelPosition/Fill/Stroke/ActiveFont edited.
LabelLevelCount.Text := Format('%d threshold level(s) configured',
[TMSFNCProgressBar1.Levels.Count]);
end;
procedure TForm1.AddHighUsageLevel;
var
L: TTMSFNCCustomProgressBarLevel;
begin
// Any mutation below triggers OnLevelsChanged once the change is applied.
L := TTMSFNCCustomProgressBarLevel(TMSFNCProgressBar1.Levels.Add);
L.LevelPosition := 90;
L.Fill.Color := $00E53935;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
TMSFNCProgressBar1.OnLevelsChanged := TMSFNCProgressBar1LevelsChanged;
end;
Applying a global font
Both TTMSFNCProgressBar and TTMSFNCRating expose GlobalFont, a single font descriptor that cascades to every text-bearing sub-object in the control instead of requiring separate edits to Appearance.Font, Appearance.DefaultLevelFont, and each Levels item's ActiveFont. Reach for it whenever the value label needs to match your application's typography, especially after configuring threshold levels as above: without GlobalFont you would otherwise set the font on the base appearance and again on every level.
Setting GlobalFont.Name, .Size, or .Style immediately reapplies that attribute everywhere; leave an attribute at its default to keep that aspect per-object:
procedure TForm1.ApplyGlobalFont;
begin
with TMSFNCProgressBar1 do
begin
// GlobalFont cascades to Appearance.Font, Appearance.DefaultLevelFont,
// and ActiveFont on every item in Levels - no need to touch each one.
GlobalFont.Name := 'Segoe UI';
GlobalFont.Size := 12;
GlobalFont.Style := [TFontStyle.fsBold];
end;
end;
Rating control
TTMSFNCRating shares the same base API. Set Layout.Blocks to the number of rating steps and enable interaction:
| Property | Effect |
|---|---|
Interaction.SnapToValue |
Click snaps to the nearest block |
Interaction.SlideToValue |
Drag slides to a new rating |
Interaction.KeyboardSupport |
Arrow keys change the value |
Interaction.ReadOnlyMode |
Disables all mouse and keyboard input |
// Five-star interactive rating control
procedure TForm1.ConfigureRating;
begin
with TMSFNCRating1 do
begin
Minimum := 0;
Maximum := 5;
Value := 0;
// Five discrete star blocks; click to snap to nearest integer
Layout.Blocks := 5;
Layout.Spacing := 4;
Layout.ShowValue := False;
// Allow the user to drag or click a new rating
Interaction.SnapToValue := True;
Interaction.SlideToValue := True;
Interaction.KeyboardSupport := True;
end;
end;
// React when the user changes the rating
procedure TForm1.TMSFNCRating1ValueChanged(Sender: TObject; AValue: Single);
begin
StatusLabel.Text := Format('Rating: %.0f / 5', [AValue]);
end;
Use OnSlideValueChanged, OnSnapValueChanged, or OnKeyboardValueChanged to react after a change, and their *Change counterparts (with var AAllow: Boolean) to cancel one.
Custom draw hook
OnBeforeDrawBlock fires before each block is rendered and lets you fully replace its drawing by setting ADefaultDraw := False. OnBeforeDrawValue fires before the label and receives the current text string so you can override it:
procedure TForm1.TMSFNCProgressBar1BeforeDrawBlock(Sender: TObject;
AGraphics: TTMSFNCGraphics; ARect: TRectF;
ACorners: TTMSFNCGraphicsCorners;
var AAllow, ADefaultDraw: Boolean);
begin
AGraphics.Fill.Color := $00FF9800;
ADefaultDraw := True; // let the bar draw, but fill colour is already set
end;
Combining blocks, levels, and a custom label
The example below builds a segmented upload indicator that changes colour at 75 % and 90 % and shows a "12 MB / 50 MB" label using pvtCustom:
// Segmented upload progress bar with threshold colours and a custom label
procedure TForm1.FormCreate(Sender: TObject);
begin
with TMSFNCProgressBar1 do
begin
Minimum := 0;
Maximum := 100;
Value := 0;
Layout.Blocks := 10;
Layout.Spacing := 2;
Layout.Shape := psHorizontalLine;
Layout.ShowValue := True;
Layout.ValueType := pvtCustom; // controlled via OnBeforeDrawValue
Layout.TextPosition := ptpCenter;
// Base fill (0–50 %)
Appearance.Fill.Color := $00519657;
// Amber above 75 %
with TTMSFNCCustomProgressBarLevel(Levels.Add) do
begin
LevelPosition := 75;
Fill.Color := $00E69820;
end;
// Red above 90 %
with TTMSFNCCustomProgressBarLevel(Levels.Add) do
begin
LevelPosition := 90;
Fill.Color := $00C62828;
end;
end;
end;
// Supply a friendly "12 MB / 50 MB" label instead of a percentage
procedure TForm1.TMSFNCProgressBar1BeforeDrawValue(Sender: TObject;
AGraphics: TTMSFNCGraphics; AText: string; ARect: TRectF;
APosition: TTMSFNCProgressBarTextPosition;
AOrientation: TTMSFNCProgressBarTextOrientation;
var AAllow: Boolean; var ADefaultDraw: Boolean);
begin
TMSFNCProgressBar1.CustomValue :=
Format('%d MB / %d MB',
[Round(TMSFNCProgressBar1.Value / TMSFNCProgressBar1.Maximum * 50), 50]);
end;