ITQuants blog

Win32 SDK: how to change the title bar (color, title...)?

Sep 15

Written by:
9/15/2014 5:07 PM  RssIcon

I was asked recently if I knew how to change the color of the titlebar on Sophis Risque in order to catch the user attention. At first, I've made a wrong answer: "Easy, you just need to paint on the WM_NCPAINT as Sophis Risque does it when the simulation mode is selected (and as I've developed it on the v4)" !! ...

Unfortunately, this mode does not work anymore as it was developped for Window 95/2000.... A lot of articles can be found on the Net (like codeproject or codeguru), but no one gives the code to run it with the themes that were developed on XP, or on Vista/7 when the Desktop Window Manager service is started. For example, when the Windows Desktop Manager is disabled, as best effort, and using the MS SDK functions ::DrawCaption and ::DrawFrameControl, the dialog will look like this one:

Note that the minimize, maximize and close buttonsdo not have the new style as expected.

The goal of this post is to describe how to do it.

After a lot of search, the code in C# or C++ that I've found on the Net does not take into account the fact that themes are selected, or that the Desktop Windows Manager overrides completely the painting of the title bar and the frames. I just want to override the title bar without activating the visualizer management done by the MFC VS2010 or later, in order to have code that could be compatible with previous versions of Visual Studio

 Since we want to catch the attention of the user, the best is not to make the title bar transparent if the Windows Desktop Manager is running. In order to enable it or disable it, the following code does it:

DWMNCRENDERINGPOLICY ncrp = m_enable? DWMNCRP_DISABLED : DWMNCRP_ENABLED;     // Enable/disable non-client area rendering on the window.
HRESULT hr = DwmSetWindowAttribute(m_hWnd, DWMWA_NCRENDERING_POLICY, &ncrp, sizeof(ncrp));

 

As you can see above, the DwmSetWindowAttribute function accepts as parameter the handle of the window to be deactivated, it means that it does not deactivate the whole application. By the way, I've found too that's the only way to paint the title bar: if kept enabled, the Windows Desktop Manager overrides the painting of the title bar and the frames, even if some code was added on WM_NCPAINT and that the parent WindowProc callback is not called.

Once the Desktop Windows Manager is deactivated, we just have to paint the title bar using the Windows theme API. In order to get information on the title bar, we need to use the Theme Window class, by open it using the OpenThemeData function. In order to get the size of the different elements composing the title bar (close button, frame width and height, caption height), we call GetThemePartSize which returns either the width or the height. To paint the frames with the default right color, DrawThemeBackground is called. We add a gradient and paint over just after by painting lines and changing the color at the same time. Even using the sizes returned by GetThemePartSize, it appears to me that some offset was missing. I've added an extra offset of 3 pixels inorder to enlarge the width of the close button. The control of the right size can be easily detected by highlighting the close button. I've detected that the correct font is not used by default. I've tried to use GetThemeFont, but without success, only GetThemeSysFont works fine on my computer.

I wanted to add some icon too. I've got some problem to get the right size of the icon. The following code does the job correctly: 

m_hIcon = (HICON)LoadImage(AfxGetApp()->m_hInstance,
    MAKEINTRESOURCE(IDR_MAINFRAME),
    IMAGE_ICON,
    GetSystemMetrics(SM_CXSMICON),
    GetSystemMetrics(SM_CYSMICON),
    LR_DEFAULTCOLOR);

 

And using the following code to paint it on 16x16 pixels:

DrawIconEx(hDC, cxIcon/2, cyIcon/2, m_hIcon, cxIcon, cyIcon, 0, NULL,DI_NORMAL);

 

At the end, we finally otbain the following title bars, first without overriding, when the Windows Desktop Manager is used:

Without Desktop Windows Manager, but with themes:

When overriding the NCPAINT, which looks like a little better than the code used for Windows 95/2000:

 

I haven't added the code to override the minimize and maximize buttons, but this can easily done by detecting the windows style and using the right windows theme parts (WP_MINBUTTON and WP_MAXBUTTON instead of WP_CLOSEBUTTON). The theme API used is the one delivered with VS2010. On VS2005, adding a  header file with the right callback as it is already done in the MFC code is sufficient to run it on older versions of MFC.

I've forgot to indicate that to force to paint the non-client zone, the following code runs perfectly:

// force to redraw the NC client area
SetWindowPos(0, 0, 0, 0, 0,  SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE);

 

Code provided: ITQThemeAPI.zip

Tags: win32 , NCPAINT , uxtheme
Categories: Sophis, C++

Search blog