Table of Contents

Building and populating rings

TTMSFNCWidgetMultiProgress shows several progress values as concentric rings in one control, so you can compare related percentages — regional sales, per-stage completion, per-team KPIs — without one progress control per metric. This chapter covers the data model: how to create the widget, populate its CircleItems collection, and control the title and legend. Reach for the multi-progress widget (rather than several single TTMSFNCWidgetProgress controls) when the metrics belong together and you want them visually nested and legended as a set. For the visual treatment — per-ring colours, ring thickness, and legend styling — see the Appearance and legend chapter.

Core concept: rings are collection items

The widget owns a CircleItems collection of TTMSFNCCircleItem. Each item is one ring and carries:

  • Caption — the label shown for the ring in the legend.
  • Value — the progress, as a percentage in the range 0..100. There is no per-ring minimum/maximum; convert raw figures to a percentage yourself. Values outside 0..100 are ignored.
  • Fill / UnfinishedFill — fill objects for the completed and remaining arcs (covered in the next chapter).

The first item added is the outermost ring; each subsequent item is drawn inside the previous one.

Populate the collection

Call CircleItems.Add once per metric and set its Caption and Value. Wrap a batch in BeginUpdate/EndUpdate so the widget repaints once after the whole batch rather than after every change, and call Clear first when you are rebuilding from fresh data.

procedure TForm1.FormCreate(Sender: TObject);
var
  Widget: TTMSFNCWidgetMultiProgress;
  Item: TTMSFNCCircleItem;
begin
  Widget := TTMSFNCWidgetMultiProgress.Create(Self);
  Widget.Parent := Self;
  Widget.SetBounds(20, 20, 360, 300);

  // Title above the rings.
  Widget.CaptionOptions.Text := 'Regional sales completion';
  Widget.CaptionOptions.Position := cpTop;

  // Show one row per ring in a legend in the top-right corner.
  Widget.Legend.Position := lpTopRight;

  // Build the rings. Each ring is an independent TTMSFNCCircleItem with its
  // own caption and a Value expressed as a percentage (0..100).
  Widget.CircleItems.BeginUpdate;
  try
    Widget.CircleItems.Clear;

    Item := Widget.CircleItems.Add;
    Item.Caption := 'North';
    Item.Value := 82;

    Item := Widget.CircleItems.Add;
    Item.Caption := 'South';
    Item.Value := 64;

    Item := Widget.CircleItems.Add;
    Item.Caption := 'East';
    Item.Value := 47;

    Item := Widget.CircleItems.Add;
    Item.Caption := 'West';
    Item.Value := 28;
  finally
    Widget.CircleItems.EndUpdate;
  end;
end;

The CaptionOptions.Text / CaptionOptions.Position pair places a title above (cpTop) or below (cpBottom) the rings, and any Legend.Position other than lpNone turns on the legend table.

Update values at runtime

Because each ring is a collection item, you can update a value in place and the widget repaints automatically — useful for live dashboards:

{ Inside a timer or data-refresh handler: }
if Widget.CircleItems.Count > 0 then
  Widget.CircleItems.Items[0].Value := 95;  // bump the outer ring to 95%

Pitfalls

  • Value is a percentage. Value := 12 is a 12% arc, not 12 units. Scale your data to 0..100 first; out-of-range values are dropped, not clamped.
  • Item order is ring order. Add the metric you want on the outside first.
  • Rebuild cleanly. When refreshing from a data source, call CircleItems.Clear inside the BeginUpdate/EndUpdate block before re-adding, or you will append rings to the existing set.

See also