Introduction

I recently needed to add a message to a list when some items which would normally be shown in the list were excluded due to access permissions. I wanted a message bar which I could easily add to the control, similar in appearance to the message bar seen in most web browsers when pop-ups and other content are blocked.

How I did it

I wanted to show the message at the top of a listview control, but I didn't want to have to resize the control to fit something above the control as a sibling, so I decided to add it to the non-client area above the main client area, by intercepting the WM_NCCALCSIZE message in the control and leaving enough space to draw a message. I also wanted to provide the user with a way of dismissing the message, so I needed to respond to WM_NCLBUTTONDOWN etc. messages within the message bar area.

Not wanting to restrict myself to only being able to use the message bar in a listview control, I looked at how I could make it generic enough to be easily added to any control. My first attempt was to create a class from which I would co-inherit some MFC classes, thereby adding support for a message bar to any of my control classes. The main downside of this was that I would need a class derived from the standard MFC class for every control that I wanted to give the message bar to. I, therefore, wanted a method which allowed me to simply add the functionality to any existing control.

Adding functionality to a control by sub-classing is simple, but when the control is already sub-classed, it is not possible to subclass the control a second time. Looking into this brought me to an article by Ralph Varjabedian on Double Subclassing, which allows a previously subclassed control to be subclassed again, providing me with exactly what I needed.

Once the control has been (double-)subclassed in order to provide the message bar functionality, the message bar code is then able to intercept the window's messages, which allows it to modify the control's non-client area to provide a space for the message bar. Also, the message bar can intercept mouse messages so that the program can respond to clicks on the message bar to show a menu or perform any other action.

Some controls make their own use of the non-client area, which can interfere with the message bar's calculated area. The most common example is the list-view control, which uses the non-client area to place the column header. We are fortunate though that the list control sends a message when calculating the header's size and position, and so the message bar intercepts this message to adjust the area used by the header control, thus allowing it to keep its own space.

How to use it

Using the CCtrlMessageBar class is simple. Follow the steps below to add one to an existing project:

After putting the source files (CtrlMessageBar.cpp and CtrlMessageBar.h) into the directory you wish to use them from, add the files to your Visual Studio project.

Make sure you have a control to which you wish to add the message bar. I'll assume this is a list control, with a variable name of m_list.

Add a CCtrlMessageBar variable to your dialog class. I'll assume this is called m_barList.

Add a handler for WM_INITDIALOG in your dialog class if you don't already have one, and add the following code to it to add a message bar and set its text:

That's all you need to do to get started. See the features or member function documentation below, or the demo application, for more advanced options.

Features

Some of the features included in the control, which you may find useful, are as follows:

Bars may have images

As in the example above, you may optionally set an image-list for the message bar, and then specify an image in that image-list to be displayed at the top left of the bar.

Text may be set to wrap

Text that is too long to fit in the bar's rectangle may be set to wrap, by calling the SetWrapText method, thus increasing the height of the bar to accommodate the text. Without wrapping enabled, the text is truncated with an ellipsis.

Bar text is displayed in a tool-tip if truncated

If text is too long to fit in the message bar, and you have not set wrapping, then the text is right-truncated with an ellipsis (...), in which case hovering the mouse over the bar will display the entire text in a tool-tip. This can be used where text is likely to be too long to fit, but you do not want the height of the bar to be increased.

The bar background and text may be set to any colour

By default, the bar uses the system tool-tip background colour for its background, and the system tool-tip text colour for the text. These, along with the highlight colours (see below), may be overridden by specifying new colours to the SetColours method. Specifying any colour as CLR_DEFAULT causes it to be used as the default colour.

Bars may be set to 'light up' on mouse-over

You can make the bar 'light up' when the user moves their mouse over it by calling the SetHighlightOnMouseOver method. The highlight background and text colours default to the system highlight colours, but may be set by calling the SetColours method (see above).

The bar can be set to resize its parent to make room

Normally, attaching a bar to a control will resize the control's client area to make room for the bar, while leaving the control's window size as it was. While this is probably ideal for most controls, there are times when the opposite is required, for instance, adding a bar to the top of a dialog. By calling the SetResize method with TRUE, the bar will resize the window to enable the bar to fit while leaving the size of the window's client area as it was.

Custom menus may be invoked

By default, clicking or right-clicking on the message bar will display a menu offering the user one option, which is to hide the bar. By specifying a callback function, you may intercept this behaviour and supply your own menu, or carry out any other action, or allow the default behaviour. An example of such a callback function is included in the demo application, in the CMBEdit class which subclasses the edit control (note though that it is not necessary to sub-class a control in order to provide a callback function).

Reference

Functions

The public member functions of the CCtrlMessageBar class are as follows:

CCtrlMessageBar();

Standard empty constructor.

virtual ~CCtrlMessageBar();

Standard virtual destructor.

BOOL Attach(CWnd* pCtrl);

Used to attach the message bar to the control specified by pCtrl. Returns TRUE is successful, else FALSE.

BOOL Attach(CWnd& ctrl)

Used to attach the message bar to the control specified by ctrl. Returns TRUE is successful, else FALSE.

BOOL Attach(CWnd* pDlg, UINT nID);

Used to attach the message bar to the control specified by nID on the dialog specified by pDlg. Returns TRUE is successful, else FALSE.

CWnd* Detach();

Detaches the message bar from the control. Returns the control to which the message had been attached.

CWnd* GetCtrl() const

Returns the controls to which the message bar is attached.

BOOL IsAttached() const

Returns whether the message bar is currently attached to a control.

void SetImageList(CImageList* piml);

Sets the image list containing the images to be used by the message bar.

Used to get the current colours used for the various aspects of the message bar.

void SetResize(BOOL bResize = TRUE)

Specifies whether the window hosting the message bar should be resized to accommodate the message bar. The normal behaviour is to reduce the window's client area to make room for the message bar while leaving the window's size and position untouched. This option is useful when adding a message bar to a dialog or other window, where the client area should not be resized, but the overall window size should be increased.

BOOL GetResize() const

Returns whether the message bar's host window will be resized to accommodate the message bar.

static CCtrlMessageBar* GetMessageBarCtrl(CWnd* pCtrl);

Returns a pointer to the CCtrlMessageBar currently attached to the specified Windows control.

void SetShowMenuCallback(PFNSHOWMENUCALLBACK pfnShowMenuCallback)

Sets a callback function to display a context menu. See the PFNSHOWMENUCALLBACK type documentation below, for details.

Types

The type of the callback function used to display a context menu. The callback function is called when the user left- or right-clicks on the message bar.

The pBar parameter is the message bar clicked on. The pCtrl parameter is the control hosting the message bar. nHitTest is the hit-test code, which specifies where the user clicked, and will be one of HTMESSAGEBAR to indicate the body of the bar, or HTMESSAGEBARCLOSE to indicate the close button. The point parameter specifies the user's mouse position at the time of click, in window co-ordinates.

The function may show - and act on - a context menu, or may choose to simply allow default handling. Return FALSE to perform default handling, else TRUE.

Share

About the Author

Originally from an electronics background, I moved into software in 1996, partly as a result of being made redundant, and partly because I was very much enjoying the small amount of coding (in-at-the-deep-end-C) that I had been doing!

I swiftly moved from C to C++, and learned MFC, and then went on to real-time C on Unix. After this I moved to the company for which I currently work, which specialises in Configuration Management software, and currently program mainly in C/C++, for Windows. I have been gradually moving their legacy C code over to use C++ (with STL, MFC, ATL, and WTL). I have pulled in other technologies (Java, C#, VB, COM, SOAP) where appropriate, especially when integrating with third-party products.

In addition to that, I have overseen the technical side of the company website (ASP, VBScript, JavaScript, HTML, CSS), and have also worked closely with colleagues working on other products (Web-based, C#, ASP.NET, SQL, etc).

For developing, I mainly use Visual Studio 2010, along with an in-house-designed editor based on Andrei Stcherbatchenko's syntax parsing classes, and various (mostly freeware) tools. For website design, I use Dreaweaver CS3.

When not developing software, I enjoy listening to and playing music, playing electric and acoustic guitars and mandolin.