Q: How do you change the background color of a Windows tab control? I don't mean
the background of the individual tabs but rather the overall background behind the control, such as the empty tab space (figure a.).
A: The short answer is, you can't. This part of the painting is integrated into the WM_PAINT
processing with the rest of the control, so there's no message you can subclass to affect only the background
painting. That area will always be painted using the COLOR_BTNFACE color, with the exceptions as follows: I found some evidence that people have gotten this to work by using property
sheets instead of the tab control, which suggests that the (internal) tab control code is not exactly independent of
wrapping bodies, as it should be. Similar observations have been made regarding XP "theming" support and the tab
control (offite link: PN Devlog). Intercepting the WM_ERASEBKGND message
is of no use with this control.
After having pulled out most of the stops regarding subclassing, to no avail, I had to resort to a much more hideous
solution, as follows:
...
WNDPROC g_pfnTab;
HBRUSH g_hbrBkgnd = CreateSolidBrush(0); // desired background brush
...
LRESULT CALLBACK SubclassTabProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
if (uMsg==WM_PAINT)
{
LRESULT l = CallWindowProc(g_pfnTab,hwnd,uMsg,wParam,lParam);
HDC hdc = GetDC(hwnd);
COLORREF cr = GetSysColor(COLOR_BTNFACE);
SelectObject(hdc,g_hbrBkgnd);
RECT r;
GetClientRect(hwnd,&r);
ExtFloodFill(hdc,r.right-3,3,cr,FLOODFILLSURFACE);
SelectObject(hdc,GetStockObject(WHITE_BRUSH));
ReleaseDC(hwnd,hdc);
return l;
}
return CallWindowProc(g_pfnTab,hwnd,uMsg,wParam,lParam);
}
...
HWND hwnd_tab = CreateWindow(WC_TABCONTROL,L"",WS_CHILD,10,10,300,300,hWnd,(HMENU)ID_TABCTRL,g_hInst,0);
g_pfnTab = (WNDPROC)SetWindowLong(hwnd_tab,GWL_WNDPROC,(long)SubclassTabProc);
...
As you can see from figure b., this solution is not perfect, but it was easy and I'm moving on.
No, I'm not proud to post this. It's really sad. I originally was going to try to get CallWindowProc to
do its painting into a memory DC, in order to eliminate any flicker, but then I realized that there's no
way to replace the DC which the internal WM_PAINT processing will fetch when it calls BeginPaint. And I
theorized that the XP GDI caching hopefully would take care of any flicker, and by my observation, it does
a good enough job. Pronounced flicker is visible when drag-resizing the control.
If somebody finds out a better way to do this, please contact me. Glenn out.
2007-11-02 UPDATE
-----Original Message-----
From: ******
Sent: Saturday, November 03, 2007 5:24 PM
To: glenn@glennslayden.com
Subject: Re:Tab Control
I coded something like that and it's working ;]
first:
WNDPROC tabctl = reinterpret_cast(SetWindowLong(htab, GWL_WNDPROC, reinterpret_cast(TabProc)));
in tabctl proc:
switch (imsg)
{
case WM_ERASEBKGND:
GetClientRect(hwnd, &rect);
FillRect(((HDC)wParam), &rect, CreateSolidBrush(RGB(255,255,255)));
UpdateWindow(hwnd);
return TRUE;
};
and in parent window on WM_DRAWITEM:
TCITEM tabitem;
TCHAR buff[30] = {0};
HWND htab = GetDlgItem(hwnd, IDC_TABCTL);
if ( htab == dis->hwndItem )
{
FillRect(dis->hDC, &dis->rcItem, reinterpret_cast(COLOR_WINDOW));
SetBkMode(dis->hDC, TRANSPARENT);
memset(&tabitem, 0, sizeof(TCITEM));
tabitem.mask = TCIF_TEXT;
tabitem.pszText = buff;
tabitem.cchTextMax = 30;
SendMessage(htab1, TCM_GETITEM, static_cast(dis->itemID), reinterpret_cast(&tabitem));
TextOut(dis->hDC, (dis->rcItem.left + 6), (dis->rcItem.top + 2), tabitem.pszText, lstrlen(tabitem.pszText));
Thanks
See you...