Theming Owner-Drawn Tabs

[Updated 17/5/11: fixed a bug that meant pages with their Enabled property set to False weren’t being painted properly.]

The problem: say you want to customise the font colour of a tab on a tab or page control.  Back in the day, this was simple — set OwnerDraw to True and provide a handler for OnDrawTab that just sets the font and draws the text.  With Windows themes, however, things are not so simple, since setting OwnerDraw to True disables theming for the control.  Not a VCL limitation as such, this merely reflects the behaviour of the underlying API control, and if you think about it, it makes good sense from a backwards compatibility point of view.  Nonetheless, this is still an issue if you know what you want, which is simply to customise a tab’s caption.  Enter, then, TTabControlEx and TPageControlEx, which use the theming API to provide the proper ‘look’ even when OwnerDraw is set to True.

Limitations: TabPosition must be tpTop (the default) and Style tsTabs (ditto) for the code to take effect. However, my code does support the Multiline property being set to True.

Minimum Delphi version: Delphi 7.

Delphi 2009+ compatibility: should be fine, since the code does not do any character crunching.

Licence: MPL 1.1.

Download: from Embarcadero CodeCentral here.

6 thoughts on “Theming Owner-Drawn Tabs

  1. Many thanks for the TabControlEx which mostly works great.

    I’ve hit a bug though: if you set the control to be MultiLine=true, the tabs no longer draw correctly. I think is due to using R.Top as an offset in DoDrawTab. Not entirely sure what the best way to get the correct offsets is.


  2. Bug is detected. If PageControl contains many tabs and not multiline that appear on PageControl UpDown control. The region UpDown control’s redraw is not correct. From the field of redrawing the PageControl to exclude region UpDown control’s. In the procedure DoDrawTab can add the code befor line ThemeServices.DrawElement(DC, Details, R);
    (code in c++):

    if (!Owner->MultiLine) {
    HWND UpDownCtrl = FindWindowExW(Owner->Handle, NULL, L”msctls_updown32″, NULL);
    if (UpDownCtrl && IsWindowVisible(UpDownCtrl)) {
    TRect Rect;
    GetWindowRect(UpDownCtrl, &Rect);
    TPoint LT = TPoint(Rect.Left, Rect.Top);
    ScreenToClient(Owner->Handle, &LT);
    if (R.Right > LT.x)
    ExcludeClipRect(DC, LT.x, Owner->ClientRect.Top, Owner->ClientRect.Right, R.Bottom);

Comments are closed.