Framework for Creating Custom Window Captions

Bitmap Caption Image

Miscellaneous Captions Image

Environment: VC++ 5.0-6.0, NT 4.0, Win95/98, Win2000

Overview

This article presents some classes that make customising window captions really quick and easy.

Using these classes you can change the background colour and the text font and colour of both the active and inactive window captions independently for any windows you choose. I also provide an example class that allows a bitmap to be used as the caption background, e.g. for logos, or wood or stone effects.

More unusually, you can have your child window captions automatically wrap onto more than one line to accommodate extra long titles. This caption auto-sizing facility also allows extra large fonts and bitmaps to be used as banner captions on selected windows.

Background

A couple of years ago, I was writing an VC++/MFC MDI database query application, which presented the query results as tables or lists of data in MDI child windows.

A problem arose with the display of titles based on the queries used for particular windows, such as "Productivity League Table: UK Media companies where Net Turnover > #25,000,000 and Number of Employees > 3500". Some titles would be even longer than this, and were often truncated when displayed as window captions. Things got much worse when users tiled a number of these windows, because they would end up with five or six windows titled "Productivity League Table: UK..." and "Company News Headlines: Finan...", etc. It was obviously a headache to find any particular window of interest when their captions all seemed to say the same thing!

There are several solutions to this kind of problem, such as tooltips or just relying on titles in the client area, but all seem to have irritating drawbacks. I couldn't help feeling that the caption really was the right place even for long titles like these, so you could see at a glance what the window was about. I experimented with owner-draw captions using a 1997 Microsoft Systems Journal C++ Q&A article by Paul DiLascia, that described how to draw shaded colour gradient caption backgrounds. The results were better than I'd expected, so I thought I'd rework them into something anyone could use.

The result is this little kit of classes.

The Classes

CCaption:The abstract main class, installs itself into the frame window and manages the caption painting using CCaptionBackground and CCaptionTextAttributes (see below). By default it paints a standard window caption.

CCaptionBackground: Used by CCaption. Paints the caption active and inactive background. By default it uses the system caption colours.

CCaptionTextAttributes: Used by CCaption. Provides the active and inactive caption text fonts and colours. By default it uses the system caption font and colours.

CSingleLineCaption: Derived from CCaption, this provides standard height custom captions for dialogs and other non-MDI Child windows (e.g. frames with menus).

CMultiLineCaption: Derived from CCaption, this wraps the caption onto additional lines according to the length of the title and the font size. Only suitable for windows without menus (e.g. MDI child windows), as I haven't yet found a way to modify the default position of menus...

CMultiLineCaptionEx: Derived from CMultiLineCaption and extended to fix a weird Windows 95 and WinNT 'feature' that secretly draws a standard caption when mouse activity occurs in the non-client area once the system menu has been displayed. This can spoil the clean appearance of multiline captions, but doesn't affect Windows 2000.

CGradientCaptionBackground: Used by CCaption. Derived from CCaptionBackground, paints the active background as a shaded colour gradient (shading to black).

CBmpCaptionBackground: Used by CCaption. Paints a bitmap as the active background. An example class in the CaptionDemo project to show how easy it is to derive your own custom background painters.

[Sundry helper classes are also used, notably Paul DiLascia's CSubclassWnd but are not part of the public interface].

How to Use

Declare a caption object (CSingleLineCaption, CMultiLineCaption, or CMultiLineCaptionEx) as a member of your frame window class:

#include "..\CustomCaption\MultiLineCaptionEx.h"

class CChildFrame : public CMDIChildWnd
{
    ...
private:
    ////////////
    // Declare a caption object (this one is an extended multi-line caption)
    //
    CMultiLineCaptionEx m_Caption;
    ...
};

Install the caption in the OnCreate() method of your frame window class (you may need to use the ClassWizard to insert this function. Note: select the 'WM_CREATE' message, not the 'Create' message):
int CChildFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
   if (CMDIChildWnd::OnCreate(lpCreateStruct) == -1)
       return -1;
	
    // TODO: Add your specialized creation code here

   ///////
   // Install caption into frame
   //
   m_Caption.Install(this);

   return 0;
}
That's all there is to it! If you install a Multi-line Caption as above, caption titles too long to fit on a single line will automatically wrap onto new lines. You can change the maximum number of lines the caption will allow, using the SetMaxLines() method.

You can now use the caption object to set the colours and fonts of your caption. To do this, use the CCaption methods to access the CCaptionBackground and CCaptionTextAttributes objects, and modify their contents, e.g:

/////////////////
// Set active text and background colours
//
COLORREF colorText = RGB(txt_red, txt_green, txt_blue);
COLORREF colorBk = RGB(bk_red, bk_green, bk_blue);

m_caption.GetTextAttributes()->SetActiveColor(colorTxt);
m_caption.GetBackground()->SetActiveColor(colorBk);

/////////////////////
// Get active font and change it to Arial italic
//
LOGFONT lf;
m_caption.GetTextAttributes()->GetActiveFont()->GetLogFont(&lf);
lf.lfFaceName = "Arial";
lf.lfItalic = TRUE;
m_caption.GetTextAttributes()->SetActiveFont(lf);

/////////////////
// Refresh caption so changes take immediate effect
//
m_Caption.Refresh();

To change to a different background style, or to replace all the text attributes in one go, you can create a new CCaptionBackground or CCaptionTextAttributes object, initialize it as required, and set it into the caption, using the CCaption methods SetBackground() or SetTextAttributes():

//////////
// Construct a gradient background object
//
CCaptionBackground* pNewBackground = new CGradientCaptionBackground();

//////////
// Set the colours required
//
pNewBackground->SetCustomColors(ACTIVE_COLOR, INACTIVE_COLOR);

////////////
// Set the new background
//
m_Caption.SetBackground(pNewBackground);

Don't worry about deleting the background or text attributes objects, the caption will take care of that for you.

For full working code showing how to manipulate the various custom captions, see the Caption Demo source code, especially mainfrm.cpp. I have done my best to document all the source code, so it shouldn't be difficult to follow.

Odds & Ends

If anyone can throw some light on the weird WinNT/Win95 secret caption drawing 'feature' that occurs after displaying the window system menu, I'd be very interested (try installing a CMultiLineCaption, set a long window title so the caption wraps to more than one line, then click on the window icon to display the system menu, and you'll see what I mean. The normal window caption gets redrawn whenever the mouse crosses the non-client area or is clicked in the caption, without any NC_PAINT messages being sent). Why?

A tip for displaying bitmaps in captions - if you want a short title to appear at the top of a larger bitmap, append a long string of spaces to the title to persuade it to wrap over more lines.

Employer's Disclaimer

I am required to include the following disclaimer for my employer:

The views expressed above are those of the author alone and do not necessarily reflect the views of Financial Times Information Limited.

The Downloads

Download demo executables -32 Kb
This contains the demo application executable CaptionDemo.exe and the custom caption DLL CustomCaption.dll

Download demo project - 75 Kb
The demo project is actually a VC++6.0 workspace containing two projects, the Caption Demo application, and the Custom Caption DLL project. They install to separate folders, so should be unzipped using the path information.

Download source only -25 Kb
This contains just the source code for the Custom Caption classes.



Comments

  • A good Library for this goal: www.appspeed.com

    Posted by Legacy on 01/22/2003 12:00am

    Originally posted by: Joe

    I found a library named SkinMagic SDK can do this goal well.
    www.appspeed.com

    Reply
  • A good Library for this goal: www.appspeed.com

    Posted by Legacy on 01/22/2003 12:00am

    Originally posted by: Joe

    I found a library named SkinMagic SDK can do this goal well.
    www.appspeed.com

    Reply
  • How to fix the Sysmenu issue amongst others

    Posted by Legacy on 11/27/2002 12:00am

    Originally posted by: Andrew Ireland

    re the following...
    
    

    <If anyone can throw some light on the weird WinNT/Win95 <secret caption drawing 'feature' that occurs after <displaying the window system menu, I'd be very interested
    <(try installing a CMultiLineCaption, set a long window <title so the caption wraps to more than one line, then <click on the window icon to display the system menu, and <you'll see what I mean. The normal window caption gets <redrawn whenever the mouse crosses the non-client area or <is clicked in the caption, without any NC_PAINT messages <being sent). Why?

    The WM_SYSCOMMAND is responsible internally. Now in most cases you can get away with the style suppression of WS_VISIBLE however in the case of the SysMenu, the following will fix this issue....

    case WM_NCLBUTTONDOWN:
    {
    if ((wParam == HTSYSMENU) || (wParam == HTCLOSE) ||
    (wParam == HTMINBUTTON) || (wParam == HTMAXBUTTON))
    {
    LockWindowUpdate(m_hWnd)
    dwRetVal = DefFrameProc(m_hWnd, m_hWndClient, nMsg,
    wParam, lParam)
    LockWindowUpdate(0)
    return 0
    }
    else
    {
    // place the WS_VISIBLE suppression code here
    }
    }

    I had been attempting this for quite a while and had it working quite nicely but with the same issues you had however with perseverence I found that you could achieve the same in about < 1/4 the code. The above cannot however be used for caption moving etc but the style suppression works in this case. Now my code is in the context of a pure wndproc and pure API code so I do not know how it would fit in with MFC but I could post more info if people are interested. The style suppression also fixes the mouse moving over the NC areas. The code is performed in the WM_NCLBUTTONDOWN and associated messages.

    I therefore see none of these issues anymore.

    >A tip for displaying bitmaps in captions - if you want a >short title to appear at the top of a larger bitmap, >append a long string of spaces to the title to persuade it >to wrap over more lines.

    Alternatively, in the WM_NCCALCSIZE you can just set the top of the client area to the offset from the height you require for the caption then the amount of text is irrelevant and you just have the required area that you can draw into, graphics and text.

    ncsp.rgrc[0].top += (40 - GetSystemMetrics(SM_CYCAPTION))

    or something similar.

    Regards

    Andrew Ireland
    [Plugware Solutions.com]
    [Calne, Wilts, UK]

    Reply
  • some problems with the mouse

    Posted by Legacy on 09/06/2002 12:00am

    Originally posted by: Samuel Gerber

    Hello and thanks for your job.
    But there is a problem when I mouve the mouse in a CListBox control in the environment Win2000!
    Can you help me?
    Thank you
    Sam

    Reply
  • Caption width..

    Posted by Legacy on 06/25/2002 12:00am

    Originally posted by: Neha

    Hi,
    Is there anyway we can reduce the caption width?

    Regards
    Neha

    Reply
  • how to load bitmap from embedded resource?

    Posted by Legacy on 04/22/2002 12:00am

    Originally posted by: Peter Cullen

    Thanks for you custom caption code. I'm trying to load a bitmap from an
    embedded resource, not from a file. I see your load from a uint is
    incomplete.
    I made some modifications to your bmp code to get as far as the
    commented out function here:

    /*BOOL CDib::Load(UINT imageID)
    {
    if( m_hObject )
    Detach();

    MAKEINTRESOURCE
    if( Attach(::LoadImage(AfxGetResourceHandle(),
    MAKEINTRESOURCE(imageID), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION |
    LR_DEFAULTSIZE)) )
    {
    CFile file;
    BOOL bRet = FALSE;

    file.Open( szPathName, CFile::modeRead );
    if( Load( file )==TRUE )
    bRet = CreatePalette();
    file.Close();
    return bRet;
    }

    return FALSE;
    }*/

    My question is how to "Load" the bitmap as you suggest here without
    using the CFile object, so that the requirements of this function are
    met:

    BOOL CDib::Load(CFile &file)
    {
    BITMAPFILEHEADER hdr;
    DWORD len = file.Read(&hdr, sizeof(hdr));

    if ((len!=sizeof(hdr)) || (hdr.bfType!=BITMAP_TYPE))
    {
    TRACE0("***CDib: bad BITMAPFILEHEADER\n");
    return FALSE;
    }

    len = file.GetLength() - len;
    m_pbmih = (BITMAPINFOHEADER*)new char[len];
    file.Read(m_pbmih, len);

    return TRUE;
    }

    How do I get the m_pbmih pointer pointing to a valid struct?

    Reply
  • Custom captions on XP

    Posted by Legacy on 04/17/2002 12:00am

    Originally posted by: Christophe Meray

    Has anyone been successful at making this work on XP? I started working on this but ran into many problems. Here is one of them:

    * Problem with the system buttons: even though I handle WM_NCPAINT and WM_NCACTIVATE messages, there seems to be undocumented messages that bypass my paint code and paint the system buttons using the classic look. I use a manifest so I link with V6 of the common control DLL. The default behavior should be to paint the system buttons with the visual styles. Not calling the default proc on WM_NCPAINT and WM_NCACTIVATE seems to cause that behavior !!!?


    Can anyone refer me to any article/sample code/books that talk about how to deal with the non client area on XP?

    Reply
  • Point to Remember

    Posted by Legacy on 01/15/2002 12:00am

    Originally posted by: Dave Lorde

    It's nice to see there's still a bit of interest in Custom Captions, but please remember this code is now OLD... It was written nearly 3 years ago for child MDI windows in a Win9x application.

    Times move on, and operating systems follow. Even on Windows XP, custom captions should still work with MDI child windows under the 'classic' windows look, but with other GUI styles and/or other than MDI child windows (and dialogs with single line captions), they are not likely to work well.

    If they work for your purposes, fine. If not, the source code is there and you're welcome to hack it around to make it work. Let me know if you have any success, I'm still interested, although I no longer work much with C++ and the Windows API.

    Happy code-cutting,

    Dave

    Reply
  • App termination problems

    Posted by Legacy on 01/08/2002 12:00am

    Originally posted by: Mark Spano

    I have successfully used the Custom Caption feature for MDI child windows under Win2000. However, when I experimented with incorporating it into my mainframe, it caused my app to terminate without showing a window.

    I note that in the CaptionDemo, Dave Lorde never tries to modify the caption of the mainframe, only the captions of his MDI child windows. So I guess (without examining his code too closely) that he never designed the technique to be applied to the mainframe.

    I am also speculating that this is the cause of the "App terminates immediately?" and "New app crash but not the demo" comments.

    Basically it seems that the Refresh() method needs a good window handle, which it can't find for the mainframe. (It will also crash this way if you try to modify the captions of MDI child windows too early in the window creation process.) Trapping this failure might be useful.

    Reply
  • Dialog with minimize and maximize buttons

    Posted by Legacy on 11/02/2001 12:00am

    Originally posted by: agomez75

    Hi, i have a problem whit your class  .
    
    I'm using it on a Dialog window with a minimize and maximize buttons. When i hit on the caption window, the area where the system buttons are in gets paint with the active title bar color showing my caption window with two colors, one part with the color I defined using the class and another with the default system color .
    What can i do to fix this ?

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Today's agile organizations pose operations teams with a tremendous challenge: to deploy new releases to production immediately after development and testing is completed. To ensure that applications are deployed successfully, an automatic and transparent process is required. We refer to this process as Zero Touch Deployment™. This white paper reviews two approaches to Zero Touch Deployment--a script-based solution and a release automation platform. The article discusses how each can solve the key …

  • On-demand Event Event Date: December 18, 2014 The Internet of Things (IoT) incorporates physical devices into business processes using predictive analytics. While it relies heavily on existing Internet technologies, it differs by including physical devices, specialized protocols, physical analytics, and a unique partner network. To capture the real business value of IoT, the industry must move beyond customized projects to general patterns and platforms. Check out this webcast and join industry experts as …

Most Popular Programming Stories

More for Developers

RSS Feeds