Custom drawing
When the appearance properties cannot express the look you need, the tab set
exposes before/after draw events for every visual part of a tab — background,
text, bitmap, badge, progress indicator and close button — plus the menu
buttons. A Before* event hands you a var ADefaultDraw: Boolean you can set to
False to fully replace the built-in rendering; an After* event lets you
overlay extra graphics on top of the finished tab. Reach for these only when a
property cannot do the job: they run on every repaint, so keep the handlers
cheap.
Before and after draw hooks
| Tab part | Before (can suppress) | After (overlay) |
|---|---|---|
| Background | OnBeforeDrawTabBackground |
OnAfterDrawTabBackground |
| Text | OnBeforeDrawTabText |
OnAfterDrawTabText |
| Bitmap | OnBeforeDrawTabBitmap |
OnAfterDrawTabBitmap |
| Badge | OnBeforeDrawTabBadge |
OnAfterDrawTabBadge |
| Progress | OnBeforeDrawTabProgress |
OnAfterDrawTabProgress |
| Close button | OnBeforeDrawTabCloseButton |
OnAfterDrawTabCloseButton |
| Menu buttons | OnBeforeDrawMenuButton |
OnAfterDrawMenuButton |
Each handler receives the AGraphics surface, the ATabIndex, and the ARect
to draw into. Background, text and close-button events also pass the tab's
state — a TTMSFNCTabSetTabState (ttsNormal, ttsHover, ttsDown,
ttsActive, ttsDisabled) for the tab, or a TTMSFNCTabSetButtonState for the
buttons.
Accent the active tab
OnAfterDrawTabBackground paints after the default tab, so an overlay never
hides the label. Here it draws a top accent bar on the active tab:
procedure TForm1.TMSFNCTabSet1AfterDrawTabBackground(Sender: TObject;
AGraphics: TTMSFNCGraphics; ATabIndex: Integer; ARect: TRectF;
AState: TTMSFNCTabSetTabState);
begin
// After-draw paints over the finished tab, so the accent never hides text.
if AState = ttsActive then
begin
AGraphics.Fill.Color := gcSteelBlue;
AGraphics.Fill.Kind := gfkSolid;
AGraphics.DrawRectangle(RectF(ARect.Left, ARect.Top, ARect.Right, ARect.Top + 3));
end;
end;
Replace the badge
To draw a part from scratch, handle its Before* event and set
ADefaultDraw := False. This replaces the default badge with a filled circle:
procedure TForm1.TMSFNCTabSet1BeforeDrawTabBadge(Sender: TObject;
AGraphics: TTMSFNCGraphics; ATabIndex: Integer; ARect: TRectF; AText: String;
var ADefaultDraw: Boolean);
begin
// Replace the built-in badge entirely.
ADefaultDraw := False;
AGraphics.Fill.Color := gcCrimson;
AGraphics.Fill.Kind := gfkSolid;
AGraphics.Stroke.Color := gcWhite;
AGraphics.DrawEllipse(ARect);
AGraphics.Font.Color := gcWhite;
AGraphics.DrawText(ARect, AText, False, gtaCenter, gtaCenter);
end;
Combining an accent and a custom badge
Putting it together — wire the after-draw background accent and the before-draw
badge so the active tab gets an accent bar while every badge is a custom circle.
Because one is an After* overlay and the other a Before* replacement, they
compose without conflicting:
procedure TForm1.FormCreate(Sender: TObject);
begin
TMSFNCTabSet1.OnAfterDrawTabBackground := TMSFNCTabSet1AfterDrawTabBackground;
TMSFNCTabSet1.OnBeforeDrawTabBadge := TMSFNCTabSet1BeforeDrawTabBadge;
end;
procedure TForm1.TMSFNCTabSet1AfterDrawTabBackground(Sender: TObject;
AGraphics: TTMSFNCGraphics; ATabIndex: Integer; ARect: TRectF;
AState: TTMSFNCTabSetTabState);
begin
if AState = ttsActive then
begin
AGraphics.Fill.Color := gcSteelBlue;
AGraphics.Fill.Kind := gfkSolid;
AGraphics.DrawRectangle(RectF(ARect.Left, ARect.Top, ARect.Right, ARect.Top + 3));
end;
end;
procedure TForm1.TMSFNCTabSet1BeforeDrawTabBadge(Sender: TObject;
AGraphics: TTMSFNCGraphics; ATabIndex: Integer; ARect: TRectF; AText: String;
var ADefaultDraw: Boolean);
begin
ADefaultDraw := False;
AGraphics.Fill.Color := gcCrimson;
AGraphics.Fill.Kind := gfkSolid;
AGraphics.Stroke.Color := gcWhite;
AGraphics.DrawEllipse(ARect);
AGraphics.Font.Color := gcWhite;
AGraphics.DrawText(ARect, AText, False, gtaCenter, gtaCenter);
end;
Common mistakes
- Forgetting
ADefaultDraw := False. In aBefore*handler your drawing is layered under the default unless you suppress it, so custom fills look wrong. - Heavy work in a draw handler. These fire on every repaint; precompute colors and avoid allocations inside them.
- Drawing outside
ARect. Stay within the supplied rectangle, or your graphics bleed into neighbouring tabs.
Related API
TTMSFNCTabSet—OnBeforeDrawTabBackground,OnAfterDrawTabBackground,OnBeforeDrawTabBadge,OnBeforeDrawTabText,OnBeforeDrawMenuButton