Here are some more images with different button states:
Mouse hover state:
Introduction
This class inserts a variable number of bitmaps to the caption of your dialog and handles them like buttons (next to the close, maximize or minimize button). This means the size and the position will be calculated no matter if there is only 1 button (to close the Dialog) or 3 buttons (to close/maximize/minimize). It is also possible to draw the button to a dialog that has the toolwindow flag or the resizable flag set. The button shows a highlighted state if the mouse hovers above it and gets disabled if the window is deactivated. The reason for me to build this class was to bring a button to the caption of a dialog without using DrawFrameControl
, just to get some experience with Bitmaps and the NonClient area.
BTW: Constructive criticism is always welcome! :-)
Using the code:
The code in the demo project is well documented (at least in my view of things ;-)). Take a look!
- Add the files CaptionButton.h and CaptionButton.cpp to your project and include the header.
#include "CaptionButton.h"
- Create a member variable of class
CCaptionButton
. i.e. CCaptionButton *m_pCapBtn
- Call the static function
CCaptionButton::InitCapBtn
in your dialog's OnInitDialog()
handler. CCaptionButton::InitCapBtn(m_hWnd);
HWND m_hWnd
is the handle of the window where the button should be placed.
- While we are in the
OnInitDialog()
part of your dialog's source file, we tell the class some details.
Therefore we use the member function SetBmpID
:
m_pCapBtn->SetBmpID(1, IDB_LAMPOFF, IDB_LAMPON, FALSE);
UINT n
(in this case 1) is the number of the button (e.g.: 1 is the rightmost button, then comes button number 2 on the left side of button number 1...)
UINT IDB_LAMPOFF
is the bitmap for the unpressed button
UINT IDB_LAMPON
is the bitmap for the button in the pressed state of the button. This can be left clear in case you prefer to use the same bitmap for both button states (pressed/unpressed)
BOOL FALSE
presents the kind of the button (checkbutton or not). Important only for the hover-drawing.
You are able to use this function to change the bitmaps somewhere in the code as long as the first argument remains the same.
- Now, we have to react on the message
WMU_HITCAPBUTTON
:
We want to take some action when the button in the caption is clicked. Every time our button is clicked, a message is sent to our window. That's why we have to add a handler for the registered-message WMU_HITCAPBUTTON
. Follow these simple steps to accomplish the task:
- You define the handler
OnWMU_Hitcapbutton
in the message handler part of your dialog class' header file. Add the declaration:
afx_msg LRESULT OnWMU_Hitcapbutton(WPARAM wParam, LPARAM lParam);
BEGIN_MESSAGE_MAP(CTitleBarButtonDlg, CDialog)
virtual BOOL OnInitDialog();
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnNcLButtonDown(UINT nHitTest, CPoint point);
afx_msg void OnNcPaint();
afx_msg void OnDestroy();
afx_msg BOOL OnNcActivate(BOOL bActive);
afx_msg void OnNcMouseMove(UINT nHitTest, CPoint point);
afx_msg LRESULT OnWMU_Hitcapbutton(WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
- Create an entry in the table to dispatch on these messages, therefore you declare the registered message and add an entry in the message map of your dialog class' source file:
- For the Registered Message:
DECLARE_USER_MESSAGE(WMU_HITCAPBUTTON)
- The message map entry:
ON_REGISTERED_MESSAGE(WMU_HITCAPBUTTON, OnWMU_Hitcapbutton)
DECLARE_USER_MESSAGE(WMU_HITCAPBUTTON)
BEGIN_MESSAGE_MAP(CTitleBarButtonDlg, CDialog)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_NCLBUTTONDOWN()
ON_WM_NCPAINT()
ON_WM_DESTROY()
ON_WM_NCACTIVATE()
ON_WM_NCMOUSEMOVE()
ON_REGISTERED_MESSAGE(WMU_HITCAPBUTTON, OnWMU_Hitcapbutton)
END_MESSAGE_MAP()
Please notice that the entries in the message map are written outside the
- MFC-Tags!
- Now, when your class receives the
WMU_HITCAPBUTTON
message, it will call the handler for this message which should look like this:
LRESULT CTitleBarButtonDlg::OnWMU_Hitcapbutton(WPARAM wParam,
LPARAM lParam)
{
if((UINT)wParam == 1)
{
}
return 0;
}
That's it! Now your button should work.
Additional member functions:
Planned but not yet implemented:
- A function to show/hide a button
- A function to get/set the state of a button
- ..... ..... ..... .....
- Suggestions for more are welcome!
These are the messages that are handled in class CCaptionButton
:
WM_NCPAINT
: This is where the button will be drawn to the caption.
WM_NCACTIVATE
: The active/inactive state of the dialog will be "changed" here.
WM_NCLBUTTONDOWN
: The mouse-clicks will be handled here.
WM_NCMOUSEMOVE
: Here the button will be told when the mouse hovers over it.
WM_SETTEXT
: This message must be handled, otherwise the caption will change in a way I don't like ;-)
Credits
- If you want to know more about messaging, you should read the excellent essay from Joseph M. Newcomer
- Paul Di Lascia. I reused his code in
isWindowActive(...)
.
- For a Win32 implementation, look here.
UPDATES:
- 04.06.2004
TRACKMOUSEEVENT
structure added
TrackMouseEvent(TRACKMOUSEEVENT lpEventTrack)
- function added
- Message-handling for
WM_NCMOUSELEAVE
added
- changed some values in
COLORADJUSTMENT
structure
- 11.06.2004
- solved Problem for disappearing buttons on right-mouseclick in NC-area
- solved problem for delay of drawing the buttons when dialog is re-activated by a
left-mouseclick in the NC-area