Home | API | MFC | C++ | C | Previous | Next

Programming Windows with MFC

Working with the Mouse

Windows is capable of handling a mouse with up to 3 buttons and a mouse wheel. Mouse messages are generated when the user moves the mouse, presses the mouse buttons or scrolls the mouse wheel, within the client area and non-client area. The non-client area is the area of a window consisting of borders, title bar, menu bar, minimize/maximize button and exit button etc.

Client Area Mouse Messages

Listed below are some of the more common windows client area mouse together with their MFC macro names and associated message map macro handling function

Description Message map macro Handling function
Left mouse button pressed. ON_WM_LBUTTONDOWN OnLButtonDown
Left mouse button released. ON_WM_LBUTTONUP OnLButtonUp
Left mouse button double-clicked. ON_WM_LBUTTONDBLCLK OnLButtonDblClk
Middle mouse button pressed. ON_WM_MBUTTONDOWN OnMButtonDown
Middle mouse button released. ON_WM_MBUTTONUP OnMButtonUp
Middle mouse button double-clicked. ON_WM_MBUTTONDBLCLK OnMButtonDblClk
Right mouse button pressed. ON_WM_RBUTTONDOWN OnRButtonDown
Right mouse button released. ON_WM_RBUTTONUP OnRButtonUp
Right mouse button double-clicked. ON_WM_RBUTTONDBLCLK OnRButtonDblClk
Cursor moved over client area. ON_WM_MOUSEMOVE OnMouseMove

The message map handler function will be of the following format

afx_msg void OnMsgName (UINT nFlags, CPoint point)

Where
point – contains the location of the cursor a which is reported in device coordinates relative to the upper left corner of the window’s client area.
nFlags – contains additional information about the mouse state and Shift and Ctrl as detailed below.

    MK_LBUTTON – The left mouse button is pressed.
    MK_MBUTTON – The middle mouse button is pressed.
    MK_RBUTTON – The right mouse button is pressed.
    MK_CONTROL – The Ctrl key is pressed.
    MK_SHIFT – The Shift key is pressed.

Nonclient-Area Mouse Messages

When the mouse is clicked inside or moved over a window’s nonclient area, a nonclient-area mouse message is generated. The table lists below details these nonclient-area mouse messages.

Message Message-Map Macro Handling Function
WM_NCLBUTTONDOWN ON_WM_NCLBUTTONDOWN OnNcLButtonDown
WM_NCLBUTTONUP ON_WM_NCLBUTTONUP OnNcLButtonUp
WM_NCLBUTTONDBLCLK ON_WM_NCLBUTTONDBLCLK OnNcLButtonDblClk
WM_NCMBUTTONDOWN ON_WM_NCMBUTTONDOWN OnNcMButtonDown
WM_NCMBUTTONUP ON_WM_NCMBUTTONUP OnNcMButtonUp
WM_NCMBUTTONDBLCLK ON_WM_NCMBUTTONDBLCLK OnNcMButtonDblClk
WM_NCRBUTTONDOWN ON_WM_NCRBUTTONDOWN OnNcRButtonDown
WM_NCRBUTTONUP ON_WM_NCRBUTTONUP OnNcRButtonUp
WM_NCRBUTTONDBLCLK ON_WM_NCRBUTTONDBLCLK OnNcRButtonDblClk
WM_NCMOUSEMOVE ON_WM_NCMOUSEMOVE OnNcMouseMove

The message map handler function will be of the following format

afx_msg void OnMsgName (UINT nHitTest, CPoint point)

where

nHitTest – contains a hit-test code that identifies where in the window’s nonclient area the event occurred. A selection of these hit-test codes are shown in the list below.

Value Corresponding Location
HTCAPTION The title bar
HTCLOSE The close button
HTGROWBOX The restore button (same as HTSIZE)
HTHSCROLL The window’s horizontal scroll bar
HTMENU The menu bar
HTREDUCE The minimize button
HTSIZE The restore button (same as HTGROWBOX)
HTSYSMENU The system menu box
HTVSCROLL The window’s verticalscroll bar
HTZOOM The maximize button


Point
– specifies the location in the window at which the event occurred however for nonclient-area mouse messages, point.x and point.y contain screen coordinates as oppose to client coordinates. Screen can be converted to client coordinates to client coordinates with function the CWnd member function ScreenToClient().

Miscellaneous Mouse Messages

WM_NCHITTEST

Before a window receives a client-area or nonclient-area mouse message, it receives a WM_NCHITTEST message accompanied by the cursor’s screen coordinates. Windows uses this message to determine whether to send a client area or nonclient area mouse message.

The Mouse Wheel

The WM_MOUSEWHEEL message is sent when the mouse wheel is rotated.

MFC’s ON_WM_MOUSEWHEEL macro maps WM_MOUSEWHEEL messages to the message handler OnMouseWheel.

The prototype of OnMouseWheel is:

BOOL OnMouseWheel (UINT nFlags, short zDelta, CPoint point)

Where
The nFlags and point parameters are identical to those passed to OnLButtonDown.
zDelta is the distance the wheel was rotated. The zDelta value is calibrated in multiples or divisions of the constant WHEEL_DELTA, which is 120. A value less than zero indicates rotating while a value greater than zero indicates rotating forward (away from the user).

Double Dlicks

In order for a window to register a double click the window must be set up to be notified of a double click event by including the WNDCLASS style CS_DBLCLKS during windows registration . This is set by default in a frame windows declaration. The MFC Message-Map Macro and associated Handling Function for dealing with a double click are

ON_WM_LBUTTONDBLCLK – OnLButtonDblClk(UINT, CPoint)
ON_WM_RBUTTONDBLCLK – OnRButtonDblClk(UINT, CPoint)
ON_WM_MBUTTONDBLCLK – OnMButtonDblClk(UINT, CPoint)

Capturing the Mouse

A window procedure normally receives mouse messages only when the mouse cursor is positioned over the client or nonclient area of the window however a program might need to receive mouse messages when the mouse is outside the window. For example if a mouse button is clicked inside a windows but the mouse moves outside the window’s client area before releasing that button then the windows will not receive the button up event. To remedy this problem, windows allows the application to ‘capture’ the mouse and continue to receive mouse messages when a cursor moves outside the applications windows. Windows will then continue to receive messages until the button is released or the capture is canceled. The mouse is captured with CWnd member function SetCapture() and released with CWnd member function ReleaseCapture(). These functions are normally executed in the button-down and button-up handlers

The Hourglass Cursor

When an application undertakes a lengthy processing task the usual procedure is to display an hourglass to indicate that the application is “busy.” The CWaitCursor class allows any application to display a wait cursor. To display a WaitCursor define the CWaitCursor object variable before the code that performs the lengthy operation; this will cause the object’s constructor to be displayed automatically. When the object goes out scope its destructor will set the cursor to the previous cursor.

Changing the mouse icon

The CButton member function SetCursor allows an application to change the mouse cursor. The syntax for this function is

HCURSOR SetCursor (HCURSOR hCursor);

Where hCursor is handle to the cursor. The cursor can be created by the CreateCursor() function or loaded by the LoadCursor or LoadImage function. If this parameter is NULL, the cursor is removed from the screen.
The return value is the handle to the previous cursor or NULL if there was no previous cursor.

For further information about setting the cursor icon

The following short program demonstrates how windows handles messages from both the client and non client area of the screen, together with the ALT and CTRL keys. Output describing the area clicked and the coordinate of the area click is displayed in the main window.


#include <afxwin.h>
class CSimpleApp : public CWinApp
{
public:
BOOL InitInstance();
};

class CMainFrame : public CFrameWnd
{
public:
CMainFrame();
afx_msg void OnLButtonDown( UINT, CPoint );
afx_msg void OnLButtonUp( UINT, CPoint );
afx_msg void OnLButtonDblClk( UINT, CPoint );
afx_msg void OnRButtonDown( UINT, CPoint );
afx_msg void OnRButtonUp( UINT, CPoint );
afx_msg void OnRButtonDblClk( UINT, CPoint );
afx_msg void OnNcLButtonDown( UINT, CPoint );
afx_msg void CMainFrame::textoutput(CString,int,int);
int tpy;

DECLARE_MESSAGE_MAP()
};


BOOL CSimpleApp::InitInstance(){
m_pMainWnd = new CMainFrame();
m_pMainWnd->ShowWindow(m_nCmdShow);
return TRUE;
}

CMainFrame::CMainFrame()
{
CRect rect(12,12,500,400);
Create(NULL, "MFC Mouse Demo",WS_CAPTION| WS_SYSMENU| WS_MAXIMIZEBOX| WS_MINIMIZEBOX);//,rect);
tpy=0;
}
BEGIN_MESSAGE_MAP(CMainFrame,CFrameWnd)
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_LBUTTONDBLCLK()
ON_WM_RBUTTONDOWN()
ON_WM_RBUTTONUP()
ON_WM_RBUTTONDBLCLK()
ON_WM_NCLBUTTONDOWN()
ON_WM_NCRBUTTONDOWN()
END_MESSAGE_MAP()
CSimpleApp MFCApp1;

//deals with left mouse down and ctrl and shift keys
afx_msg void CMainFrame::OnLButtonDown(UINT flags,CPoint pt)
{
CClientDC dc(this);
CString action;
action="Left Mouse button down";
if (flags & MK_CONTROL)
action=action+" with control key ";
if (flags & MK_SHIFT)
action=action+" with shift key ";
textoutput(action,pt.x,pt.y);
}
//deals with left mouse up
afx_msg void CMainFrame::OnLButtonUp(UINT flags,CPoint pt)
{
CClientDC dc(this);
CString action;
action="Left Mouse Button Up ";
textoutput(action,pt.x,pt.y);
}
//deals with left mouse double click
afx_msg void CMainFrame::OnLButtonDblClk(UINT flags,CPoint pt)
{
CClientDC dc(this);
CString action;
action="Left Mouse Double click ";
textoutput(action,pt.x,pt.y);
}
//deals with right mouse down
afx_msg void CMainFrame::OnRButtonDown(UINT flags,CPoint pt)
{
CClientDC dc(this);
CString action;
action="Right Mouse Down ";
textoutput(action,pt.x,pt.y);
}
//deals with right mouse up
afx_msg void CMainFrame::OnRButtonUp(UINT flags,CPoint pt)
{
CClientDC dc(this);
CString action;
action="Right Mouse Up ";
textoutput(action,pt.x,pt.y);
}
//deals with right button double click
afx_msg void CMainFrame::OnRButtonDblClk(UINT flags,CPoint pt)
{
CClientDC dc(this);
CString action;
action="Right Mouse Double click ";
textoutput(action,pt.x,pt.y);
}
//deals with client area clicks
afx_msg void CMainFrame::OnNcLButtonDown(UINT flags,CPoint pt)
{
CClientDC dc(this);
CString action;
switch(flags)
{
case HTCLOSE:
action="Close ";
PostQuitMessage(0 );
break;
case HTCAPTION:
action="Title Bar ";
break;
case HTREDUCE:
action="Minimise Button ";
break;
case HTZOOM:
action="Maximise Button ";
break;
case HTSYSMENU:
action="System Menu ";
break;
}
textoutput(action,pt.x,pt.y);
}
afx_msg void CMainFrame::textoutput(CString action, int x,int y)
{
CClientDC dc(this);
CString locstr;
locstr.Format("%d", x);
action=action+" x="+locstr;
locstr.Format("%d", y);
action=action+" y="+locstr;
CRect clientRect;
ASSERT( AfxGetMainWnd()!=NULL );
AfxGetMainWnd()->GetClientRect(&clientRect);
int heightclient;
heightclient=clientRect.bottom-clientRect.left;

if (tpy>=heightclient-15)
{
AfxGetMainWnd()->ScrollWindow(0, -15, NULL, &clientRect);
AfxGetMainWnd()->UpdateWindow();
dc.TextOut(0,tpy-25,action);
}
else
{
dc.TextOut(0,tpy,action);
tpy=tpy+15;
}
}

Download Code


Home | API | MFC | C++ | C | Previous | Next
Creating a Simple Window | Processing Messages | Device Context | Working with Graphics | Mapping Modes | Text Output | Working with the Mouse | Dealing with Keyboard Input | Drawing Lines and Shapes | Adding Menus | Child Windows | Dialog Windows | Common Dialog Box | Working with Bitmaps | Common Controls | Toolbars | Document View Architecture | Multi Document Interface | Timers | MFC Collections Classes |

Last Updated: 16 October 2022