c++mfcctabctrl

How to change CTabCtrl tab colors?


Hello and happy new year, (it is acceptable to say it until Thursday)

I am trying to change the color of the tabs in the CTabCtrl class. I am trying to create my own ReskinCTablCtrl so that I can just call it in separate classes and easily use it throughout my program.

Currently I am able to change the background color of the CTabCtrl but I cannot modify the tab's themselves.

I used ON_WM_ERASEBKGND() for painting the background and it worked without a problem:

BOOL ReskinCTabCtrl::OnEraseBkgnd(CDC* pDC)
{
    CRect rect;
    GetClientRect(&rect);
    CBrush myBrush(RGB(51, 51, 51));    // dialog background color
    BOOL bRes = pDC->PatBlt(0, 0, rect.Width(), rect.Height(), PATCOPY);
    pDC->SetBkColor(RGB(51, 51, 51));
    pDC->FillRect(&rect, &myBrush);
    return bRes;
}

However, I have been unsuccesfull at changing the tab colors themselves. They are still the default MFC colors. I have tried to implement ON_WM_PAINT() and ON_WM_DRAWITEM() without any success. I think I can get to the specific tab rect with using both OnDraw and DrawItem similar to the second link example that I have posted in the end of this question.

void ReskinCTabCtrl::OnPaint() {

    ...

    // paint the tabs first and then the borders
    int nTab = GetItemCount();
    int nSel = GetCurSel();

    if (!nTab) // no pages added
        return;

    while (nTab--)
    {
        if (nTab != nSel)
        {
            dis.itemID = nTab;
            dis.itemState = 0;

            VERIFY(GetItemRect(nTab, &dis.rcItem));

            dis.rcItem.bottom -= 2;
            DrawItem(&dis);
            DrawItemBorder(&dis);
        }
    }

    ...

}

I would really appreciate at least some direction to go about this problem, perhaps some more examples or what methods I should focus on using. I don't need the tabs to be different colors, maybe there is an easy way of doing this?

I've been trying to follow some examples like the following links but I still couldn't figure out the right way to do it.

https://support.microsoft.com/en-us/help/179909/how-to-change-the-background-color-of-a-tab-control

https://www.codeproject.com/Articles/1786/Ownerdraw-Tab-Controls-Borders-and-All


Solution

  • Enable OwnerDraw for tab control, either in resource editor, or set TCS_OWNERDRAWFIXED in OnInitDialog

    CTabCtrl has message reflection for WM_DRAWITEM therefore we don't want to override WM_DRAWITEM/OnDrawItem from parent class. Instead override in CTabCtrl::DrawItem(LPDRAWITEMSTRUCT).

    Unfortunately the result is rather ugly. It's sort of like overriding DrawItem in a button.

    If Visual Style is available and enabled, then you can override CTabCtrl::OnPaint and draw everything manually. Example:

    void CMyTabCtrl::OnPaint()
    {
        CPaintDC dc(this);
    
        dc.SelectObject(GetFont());
    
        CPen pen, pen_active;
        COLORREF color_off = RGB(240, 240, 240);
        COLORREF color_active = RGB(200, 240, 240);
        CBrush brush_off, brush_active;
        brush_off.CreateSolidBrush(color_off);
        brush_active.CreateSolidBrush(color_active);
        pen.CreatePen(PS_SOLID, 1, RGB(200, 200, 200));
        pen_active.CreatePen(PS_SOLID, 1, color_active);
    
        CRect rcitem;
        GetItemRect(0, &rcitem);
    
        CRect rc;
        GetClientRect(&rc);
        rc.bottom = rcitem.bottom;
        dc.FillSolidRect(&rc, GetSysColor(COLOR_3DFACE));
    
        GetClientRect(&rc);
        rc.top = rcitem.bottom - 1;
        dc.SelectObject(&pen);
        dc.SelectObject(&brush_active);
        dc.Rectangle(&rc);
    
        for(int i = 0; i < GetItemCount(); i++)
        {
            dc.SelectObject(&pen);
            if(i == GetCurSel())
            {
                dc.SelectObject(&brush_active);
                dc.SetBkColor(color_active);
            }
            else
            {
                dc.SelectObject(&brush_off);
                dc.SetBkColor(color_off);
            }
    
            GetItemRect(i, &rcitem);
            rcitem.right++;
            dc.Rectangle(&rcitem);
    
            if(i == GetCurSel())
            {
                dc.SelectObject(pen_active);
                dc.MoveTo(rcitem.left+1, rcitem.bottom - 1);
                dc.LineTo(rcitem.right, rcitem.bottom - 1);
            }
    
            TCITEM item = { 0 };
            wchar_t buf[32];
            item.pszText = buf;
            item.cchTextMax = 32;
            item.mask = TCIF_TEXT;
            GetItem(i, &item);
            dc.DrawText(buf, &rcitem, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
        }
    }
    
    BOOL CMyTabCtrl::OnEraseBkgnd(CDC*)
    {
        return TRUE;
    }
    

    enter image description here