StaticText background in TabControl... What events should be processed? - c++

I am rewiting the older application to get the modern look in Windows 7. The GUI elements are created and moved via the explicit code (no special layout manager). Because of some historic reasons, the GUI events were not processed the standard way. They were redirected. It is rather difficult to track them, especially when I do not know what should I focus for.
Here is the example of the window that should be polished. It is not a dialog. It is classical, sizable window placed on the top. However, it should mimic the dialog look. The window uses WTL::CTabControlT<CControl> where the CControl is my class based on the ATL::CWindow -- see the picture first:
It man not be well visible from the picture, but the ends of the red arrows show the grey background of the static texts. However, the backround of the tab itself is white.
What event causes the grey background of the static texts? Is it the WM_CTLCOLORSTATIC. Or, where is the problem? Is the background of the tab expected to be white or grey (standard behaviour)?
Is it recommended (by designers of the visual interface, user experience) to have also the listboxes in the tab the same (white) background?
The outer window has COLOR_3DFACE defined via ATL macro DECLARE_WND_CLASS_EX. What event is responsible for painting the bacground around the tab windows?
Thanks for your help,
Petr

The missing grey background around the tabs was solved by processing the WM_ERASEBKGND message explicitly (the redirection somehow prevented the default processing):
if (uMsg == WM_ERASEBKGND)
{
WTL::CDCHandle dc(reinterpret_cast<HDC>(wParam));
RECT rc;
GetClientRect(&rc);
dc.FillRect(&rc, GetWndClassInfo().m_wc.hbrBackground);
return TRUE;
}
Similarly, the grey background of the static texts on the white tabs was whitened by processing WM_CTLCOLORSTATIC in the ProcessWindowMessage of the TabControl window this way:
else if (uMsg == WM_CTLCOLORSTATIC)
return TRUE;

Related

How to set a bitmap as the background of a CHILD window (WIN32)

First of all this is not MFC.
Here is a cropped version of the GUI I have been working on:
As you can see there is a bitmap image above the tab control (which uses the default window's handle), I will refer to it as "Blue Bitmap" I have no problems with it, it is functioning perfectly.
The problem I am having is setting another bitmap (from a bitmap file on disk) to the background of the child window (more specifically the tab control child window), replacing the grey colour. Here are some things I did to try and set the bitmap as the background for the child window (the tab).
1) I used the same method as was used to assign the blue bitmap to the window by first importing the bitmap using LoadImage like so:
index->hbmBitmapBanner = (HBITMAP)LoadImage(index->hInstance,L"Images\\horizontal.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
Then in the WM_PAINT message:
case WM_PAINT:
{
PAINTSTRUCT ps1;
if (BeginPaint(WINDOWHANDLE,&ps1))
{
HDC hdcBanner = CreateCompatibleDC(ps1.hdc);
HBITMAP hbmOldHorizontal = (HBITMAP)SelectObject(hdcBanner,index->hbmBitmapBanner); // banner
BitBlt(ps1.hdc,0,0,516,101,hdcBanner,5,0,SRCCOPY);
SelectObject(hdcBanner,hbmOldHorizontal);
DeleteDC(hdcBanner);
EndPaint(WINDOWHANDLE,&ps1);
}
break;
}
That code was successful in setting blue bitmap to the window, but not in setting the other bitmap into the window, (with the blue bitmap's variables substituted into, size and orientation changed as well of course). It simply did not display the bitmap on the screen, and the window remained unchanged, yet no functions failed. ALSO, when I swapped the file locations around, it loaded the bitmap into where the blue bitmap was, so there is definitely no failure in the loading function.
2) Using the above code, I tried changing the first argument of BeginPaint to the window handle of the tab control box, this proved yet again of no use, and the window remained unchanged. I also tried changing the first argument of BeginPaint to a child window which is displayed on the information tab, this caused the child window (which is a groupbox) to disappear, yet still no bitmap appeared on the window.
3) As a last resort I stupidly attempted to use this function on the child window
SetClassLongPtr(tab->hTabIndex[0],GCLP_HBRBACKGROUND,(DWORD)GetStockObject(BLACK_BRUSH));
And of course, it didn't work
If it is of any use, here is how I created the tabbing in my window:
TCITEM tie = {0};
tab->hTab = CreateWindowEx(0,WC_TABCONTROL,L"",WS_CHILD | WS_VISIBLE,0,101,600,400,
WINDOWHANDLE,NULL,(HINSTANCE)GetWindowLong(WINDOWHANDLE,GWLP_HINSTANCE),NULL
);
Is the main tab control, followed by inserting individual the tabs (i.e the information tab)
TCHAR pszTab1 [] = L"Information"; // tab1's text
tie.pszText = pszTab1; // the tab's text/caption
TabCtrl_InsertItem(tab->hTab, 0, &tie); // insert the tab
Additional info: The tab control, and the windows are working/interacting perfectly apart from this. I was testing/attempting this for only one tab, because I knew if it worked on one tab, then it would work on all the tabs, hence saving time.
I have Programming For Windows Fifth Edition, by Charles Petzold beside me as reference, and there is a rather large section on bitmaps, but he doesn't go into talking about loading a bitmap to a child window, the closest he gets is using a bitmap in a menu, which is completely different than a window.
I think that the issue here is that Windows Common Controls do not interact with your application via your main thread's window procedure but via their own (system-defined) window procedures.
So, for example, when your tab window redraws itself, it does not do so in response to a WM_PAINT message arriving at the window procedure defined in your code. You should be able to verify for yourself that this is so by examining the window handles of messages passed to your window procedure, eg WM_PAINT.
If you wish to 'tap into' the default behaviour of a common control you have to 'subclass' it (see here) but in my experience attempting to change the re-draw behaviour is generally problematic.
In the case of a tab control the best thing to do is create an array of child windows sized to the client area of the tab control and arrange that the one displayed at any one time corresponds to the tab that is selected.
If you're using C++ you might consider creating a base class to wrap these windows in which the background (bitmap) drawing is handled. You can then derive a series of wrappers to handle each individual tab. I've used exactly this approach in the past and it works well.
It may be helpful to bear in mind that the tabs of the tab control do not extend over the whole of the control's client area but are just literally the tabs themselves. I don't remember the details but I think they are handled internally by the control and that manipulating them in any way is rather trick, even when the control is subclassed - may be wrong about that.
Hope that helps.
Cheers, Ian.

MFC custom OnPaint not being called reliably

I inherited an MFC app, and it has a window that has several owner-draw widgets that respond to OnPaint and do various drawing.
I noticed that in order to force the controls to redraw in response to various user actions, there was the following code:
CRect rect;
m_myControl.GetWindowRect(&rect);
ScreenToClient(&rect);
InvalidateRect(&rect, FALSE);
I thought this could be simplified like so:
m_myControl.Invalidate(FALSE);
But, in practice, when I do it this way, the control paints sometimes but not others. Specifically, when I'm interacting with controls in the window, sometimes myControl ends up just painting as solid gray. I changed the code back to the more-complicated InvalidateRect style and it's working great again.
Why would there be a difference here?
When you invalidate a window, you don't invalidate the window underneath it. If the parent window is responsible for drawing the control it won't get triggered because you didn't tell it that it needed updating. The original code does the right thing in that case.

flicker free tab control with WS_EX_COMPOSITED

I have a VS2008 C++ application for Windows XP SP3 developed using WTL 8.1. My application contains a tab control that flickers when the application border is resized.
My window hierarchy looks like this:
CFrameWindowImpl CMainFrm
|-CSplitterWindow Splitter
|-CTabView Configuration Tabs
| |-CDialogImpl Configuration View 1
| |-CDialogImpl Configuration View 2
| |-CDialogImpl Configuration View 3
|-CDialogImpl Control View
The solution I'm trying is to make the CFrameWindowImpl derived class use the WS_EX_COMPOSITED style and all windows beneath it use the WS_EX_TRANSPARENT style. Unfortunately, this makes the tab control buttons show as an empty black bar and the controls of any Configuration View to not show at all.
If I remove the WS_EX_COMPOSITED and WS_EX_TRANSPARENT styles, the form displays properly, but the CTabView and everything beneath it flickers horribly when resized.
What do I need to change to eliminate the flicker and draw the controls properly?
Thanks,
PaulH
Edit:
Got it working. I removed all the WS_EX_TRANSPARENT styles per Mark Ransom's suggestion. I put the WS_EX_COMPOSITED style on only the CTabCtrl (contained within the CTabView). Other controls get double-buffering as needed through WTL::CDoubleBufferImpl<>.
A window flickers because it gets erased before it's drawn. To eliminate this you need to disable erasing of the window entirely and use double buffering - draw the window contents into a bitmap, then copy the bitmap to the window. Because the bitmap contains the entire contents including the background, there's no need to erase anymore.
It looks like WS_EX_COMPOSITED will handle the double buffering automatically, but you still probably need to use a NULL background brush and/or handle the WM_ERASEBKGND message.
Whats not mentioned in MSDN is that the Desktop Window Manager - the component that hooks window painting on Windows Vista and 7 to perform the desktop composition necessary to get the aero glass effect - does NOT implement WS_EX_COMPOSITED.
Which means all the work you put into getting this style to work on XP, is doomed to become irrelevent on Vista or later.
The other problem with WS_EX_COMPOSITED - and why it was an optional style and not a default on XP: The double buffering only picks up painting performed during the BeginPaint / EndPaint block of the parent window. Lots of, even standard controls, perform painting outside of their WM_PAINT handlers, and as a result the backbuffer gets only partially painted.
Sadly, the result is, the only way to "eliminate" flicker in native API apps is to try to minimize it: WS_CLIPCHILDREN and WS_CLIPSIBLINGS can help if you dont have overlapping controls - to ensure that each control's area is painted only once. And ensure that the main dialog does not perform any flood filling in WM_ERASEBKGND
It is not, in my experience, possible to use double-buffering for anything that contains child controls (unless they all fully support WM_PRINT, which most do not).

Visual Studio 2005: static text control won't display with transparent background

I'm using the Dialog editor in Visual Studio 2005 to create a Dialog box with a static text control. I'd like the background of the static text control to be transparent since I'm using an static image control underneath it and the grey text background looks hideous. In the editor, I set the "Transparent" attribute to True and it causes the background to go transparent just like I want it to. But as soon as I run my app and change the text using a SendMessage(hText, WM_SETTEXT, 0L, "newtext"), the background loses its transparency and goes grey again. Any ideas? Btw, I'm doing this in C++.
Thanks in advance for your help!
As Anthony Johnson said, handle the WM_CTLCOLORSTATIC message in the dialog box (you don't have to handle WM_NOTIFY - I don't believe static controls use that message, anyway). But it doesn't seem to be enough to set the background mode to transparent. You also have to set the background brush to a null brush. Something like this should work (in your DialogProc):
case WM_CTLCOLORSTATIC:
SetBkMode((HDC)wParam, TRANSPARENT);
return (INT_PTR)(HBRUSH)GetStockObject(NULL_BRUSH);
If you change the text on the static control, you may have to invalidate what's underneath it for it to draw correctly when you do this.
Try hiding the control, then setting the text, then showing it.
I don't know how you can do it in the dialog editor, but if you handle the WM_NOTIFY message in the static's parent window, the static will send a WM_CTLCOLORSTATIC message before drawing the static. There, if you call SetBkMode((HDC)wParam, TRANSPARENT);, that should make the static have a transparent background.

TAB control background in ATL App, XP styles

I have an ATL application with a dialog containing a TAB control. The App uses a common controls manifest.
Under XP with visual styles, the tab control background is a different color than the dialog and the controls (mostly checkboxes), so it looks quite ugly.
Screenshot
How can I fix that?
There is - apparently - one thing to do to get tab control pages colored correctly using XP visual styles.
In the WM_INITDIALOG handler for each page, call the uxtheme API EnableThemeDialogTexture
With the ETDT_ENABLETAB flag this automatically changes the background color of the dialog and all its child controls to paint appropriately on a tab.
The dialog pages do not need any kind of transparent flag, or indeed any style bit set differently from previously. If you have overridden WM_ERASEBKGND or WM_CTLCOLORDLG in your pages DialogProc you will need to revert to default handling (return FALSE).
Here you could find answer to your question.
The check boxes will post WM_CTLCOLORBTN notifications to their parent. If, for the checkbox control IDs, the parent window's message handler returns the result of
GetStockObject(HOLLOW_BRUSH)
then the check boxes should be drawn with a transparent background, which should give you the look you want.