Table of Contents

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;