Environment: This control was developed with VC6 and ATL.
I have seen a lot of code on the Internet about round buttons but none on Round SysMenu buttons, so here it is. The code also explains how to do flicker-free painting in the caption and throws light how to do the same in other non-client areas. As usual, I explain a lot.
To handle the painting and message processing on the caption area, you need to handle WM_NCPAINT, WM_LBUTTONUP, and WM_MOUSEMOVE. First of all, I remove the sysmenu while designing the Dialog; then I see the code how to add the WM_NCPAINT, WM_LBUTTONUP, and WM_MOUSEMOVE messages.
In WM_NCPAINT, flicker occurs because, by default, Windows paints blue in the caption area; then over that, you paint red, and when that you-paint-red happens again and again, a flicker is generated.
When a WM_NCPAINT message is generated, the region to be invalidated will be sent in the WPARAM of the MSG structure that contains the WM_NCPAINT. We intercept the message and give Windows a new region to be invalidated and Windows will faithfully use that region invalidates only that region. If you look into the code in the OnNcPaint(..) function, I create a rectangular region that is the whole window minus the caption area (if you return the form there, Windows will not paint the caption area at all).
Then I call DefWindowProc(WM_NCPAINT,(WPARAM)hRgnCaption,NULL) to invalidate all the window except the caption area. Continuing further, I locate the place to put the close button. I create an elliptic region in that area, select that region into the Window DC, and do the following:
dc.SelectClipRgn(&rgnClose); // paint only in this region dc.DrawFrameControl(m_rcButClose,DFC_CAPTION,DFCS_CAPTIONCLOSE); // draw the Close button
Go inside and fill the region with the selected brush (brYellow VARAIBLE). FloodFill will fill the entire region until it meets the RGB(0,0,0) (sent in as a parameter) color that happens to be the color of the cross mark in the Close button.
Repeat the same thing for all the other buttons.
If you get too excited and paint the caption area with red you will generate a flicker. At the recently drawn buttons, because again and again you will paint the caption area with red, and again paint the buttons. So this is not a bright idea. Just take care to remove the recently painted areas—the three buttons from the caption area
HRGN hRgnCap = ::CreateRectRgnIndirect(&rb); //region = full caption area HRGN rgnTemp = ::CreateRectRgnIndirect(&rb); ::CombineRgn(rgnTemp,hRgnCap,rgnClose,RGN_DIFF); //remove the Close button region ::CombineRgn(rgnTemp,rgnTemp,rgnMin,RGN_DIFF); //remove the Minimize button region ::CombineRgn(rgnTemp,rgnTemp,rgnMaxRes,RGN_DIFF); //remove the maximize/restore button region ::SelectClipRgn(dc.m_hDC,rgnTemp); //Select this region dc.FillSolidRect(0,0,rc.Width(),22,RGB(255,0,0)); // now boldly paint the full caption with red.
How to Increase the Non-Client Area
This material was added to update the article.
Some time back, I desperately needed to increase the caption area and the borders usually found in multimedia applications. After some R & D, here it is. The answer is the WM_NCCALCSIZE message; trap this message and shrink the size of the client area. The remaining is the non-client area, the caption, and the borders; hence, you get an increase in the caption and borders. See OnNcCalcSize (..) for how this is done.
But there will be a problem if you have a menu. The menu will appear where it usually appears, so what to do?. We software engineers always have a trick or two up our sleeves in such situations. I take a dialog, append the menu to this dialog, and append the dialog to the main dialog. IDD_MENU_CONTAINER exists on earth solely to serve this purpose.
Note: There is one more trick to make the static textbox show only the text without disturbing the background image. I use SetWindowLong to change the WndProc of the static textbox, where I trap the WM_PAINT and WM_SETTEXT; see the code. Please be aware that there can be a thousand ways to do this and I have one way to do it. I do it because I get to play around with an unseen WndProc.