Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

CCaptionButton (buttons for the titlebar)

0.00/5 (No votes)
13 Jun 2004 1  
Bitmap - buttons for the Titlebar

Sample Image - CCaptionButton.gif

Here are some more images with different button states:

DeActive Image - DeActive.gif

Mouse hover state:

Highlight Image - Highlight.gif

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!

  1. Add the files CaptionButton.h and CaptionButton.cpp to your project and include the header.
    #include "CaptionButton.h"
  2. Create a member variable of class CCaptionButton.
    i.e.   CCaptionButton *m_pCapBtn // a pointer to CCaptionButton
  3. 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.
  4. 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.

  5. 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)
            //{{AFX_MSG(CTitleBarButtonDlg)
    
            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
    
            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)
    // announce the registered message:
    
    
    DECLARE_USER_MESSAGE(WMU_HITCAPBUTTON)
    
    BEGIN_MESSAGE_MAP(CTitleBarButtonDlg, CDialog)
            //{{AFX_MSG_MAP(CTitleBarButtonDlg)
    
            ON_WM_PAINT()
            ON_WM_QUERYDRAGICON()
            ON_WM_NCLBUTTONDOWN()
            ON_WM_NCPAINT()
            ON_WM_DESTROY()
            ON_WM_NCACTIVATE()
            ON_WM_NCMOUSEMOVE()
            //}}AFX_MSG_MAP
    
            ON_REGISTERED_MESSAGE(WMU_HITCAPBUTTON, OnWMU_Hitcapbutton)
    END_MESSAGE_MAP()

    Please notice that the entries in the message map are written outside the //}}AFX_MSG - MFC-Tags!

    • Now, when your class receives the WMU_HITCAPBUTTON message, it will call the handler for this message which should look like this:
    /***********************************************************************
    *   Message Name:            WMU_HITCAPBUTTON
    *   Inputs:
    *     WPARAM: (UINT)wParam = Nomba of Button
    *     LPARAM: (BOOL)lParam = Button state (Pressed=TRUE/Released=FALSE)
    *
    *   Result: LRESULT
    *                       Logically void, 0, always
    *
    *   Effect: represents the # of the Button (if there is more than one)
    *               and the button state (pressed/released)
    ***********************************************************************/
    LRESULT CTitleBarButtonDlg::OnWMU_Hitcapbutton(WPARAM wParam,
                                                      LPARAM lParam)
    {
            // Button #1 has been hit:
    
            if((UINT)wParam == 1)
            {
                // button No.1 has been clicked, now do something
    
    
            }
    
    return 0; // Allways return 0(LRESULT->void)
    
    }

That's it! Now your button should work.


Additional member functions:

  • A function to enable/disable a button (this routine is implemented but not working on my NT4 even though it works on my XP-Home):
    BOOL CCaptionButton::EnableButton(BOOL bEnable)

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

    License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here