ArtsAutosBooksBusinessEducationEntertainmentFamilyFashionFoodGamesGenderHealthHolidaysHomeHubPagesPersonal FinancePetsPoliticsReligionSportsTechnologyTravel
  • »
  • Technology»
  • Computers & Software»
  • Computer Science & Programming»
  • Programming Languages

Visual C++ MFC - Understanding Message Handling with an Example

Updated on December 28, 2016

1. What is Message Handling?

The user interacts with the application through the basic external peripherals like Keyboard and Mouse. In MFC, hardware events from these devices are seen as "Windows Messages". As an MFC application programmer responding to these windows messages are called "Message Handling".

For Example, Let us say; a user clicks Left Mouse button in the "Windows Client Area" of an MFC SDI Application, and after that, a message box appears greeting "Hello there". Here, the "Mouse Click" is sent to the application as Windows Message, and a "Handler Function" is displaying the message box by responding to the received message.

In this example, we will see how to respond to the Left and Right mouse button clicks.

2. Create an SDI MFC Application

Create a "Single Document Interface" MFC Application project and Name it as MessageHndl. Creating the application is shown in the below video (Use Full Screen Mode):


Creating SDI Application for Message Handling Example

By following the steps shown in the above video, you now have a "Do nothing" MFC SDI Application. We will use this application to add code and learn message handling.

3. Add Handler Functions For WM_LBUTTONUP, WM_RBUTTONUP

The mouse click is a combination of two events. They are pressing down the button and then releasing it. Here, we are going to respond to the mouse button release action as it looks like a mouse click. In MFC, BUTTONUP is the best way to track the mouse clicks.

The below video shows adding the handler code for the window messages "WM_LBUTTONUP", "WM_LBUTTONDOWN". Once handler code is in hand, we can start writing the code. Now follow the steps explained in the video below:


Providing MFC Message Handler

4. Message Handlers added by the IDE

By following the steps shown in the above video, our application is modified by the Visual Studio IDE (Interactive Development Environment) to provide the skeleton code to handle the Left and Right mouse button clicks. Let us look at these pieces code before adding our own one.

4.1 DECLARE_MESSAGE_MAP()

This macro is added to the header file and it will do the declaration of internal functions and structures written as part of Microsoft Foundation Classes (MFC). These declarations together provide support for handling the windows messages.

4.2 BEGIN_MESSAGE_MAP, END_MESSAGE_MAP

The Visual Studio IDE adds these macros to the CPP file. The window messages which are handled in the CPP file is wrapped between these macros. The "BEGIN_MESSAGE_MAP" first provides the implementation for the functions declared by the macro "DECLARE_MESSAGE_MAP". Then, it populates the array of type "AFX_MSGMAP_ENTRY". The population of the array is done by the message map entries added in between Begin and End message map pair.

The macro "END_MESSAGE_MAP" closes the MessageEntry initialization. This macro also populates one more structure of type "AFX_MSGMAP". This structure has two entries. One is message map entries initialized by the message map pair and the another entry is a function pointer which when called returns the "AFX_MSGMAP" for the base class.

Now, when a message handler is not found in the current class implementation, the function pointer is used to search the handler in the base class. Likewise, the message handler is searched till the very base class. When a handler for the message is found, the handler gets called.

4.3 Handler function prototype added by the IDE

The below screenshot shows the handler function signature added by the IDE:

Handler Functions Prototype
Handler Functions Prototype | Source

Here, the "Flags" parameter is used to check button status. For example one can check weather shift button is down when a mouse button is released. Look at the Table1 below to know the flag constant used for the check:

Table1: Flags for OnLButtonUp, OnRButtonUp

Flag Constant
Used to Test
MK_CONTROL
Set if the CTRL key is down.
MK_MBUTTON
Set if the middle mouse button is down.
MK_RBUTTON
Set if the right mouse button is down.
MK_SHIFT
Set if the SHIFT key is down.

4.4 Mouse Handler Dummy Implementation

The IDE also added the dummy implementation functions in the "View CPP File" for you. The below screenshot shows the implementation added by the class property:

Mouse handler dummy implementation
Mouse handler dummy implementation | Source

5 Code for the Mouse button handlers

In the previous section, we reviewed the code added by the Visual Studio. Now, let us add our code and experiment the mouse handler. Follow steps listed below.

5.1 Declare variables in the MessageHndlView.h

Declare the variable m_bluerect of type bool. We will use this variable to alternate the colour of the rectangle between blue and green. Next, add a CRect type variable called m_rctUpdateArea. This is used for defining the Update Area of the client window. We will see about this in more detail later. Below is the code snippet:

Variable Declarations
Variable Declarations | Source

5.2 Initialize Data Members

Have a look at the screenshot below.

First, the boolean variable m_bluerect is initialized to true (Marked as 1). The draw function will check this variable and draws the rectangle in blue colour when the variable is holding the true. Next, a bounding box for updating the client area is defined by initializing the data member m_rctUpdateArea (Marked as 2). This bounding box is supplied to the draw function so that it only erases and redraws the content defined by it (Or enclosed by it). The bounding box is shown as green below and we are going to draw the rectangle inside this bounding box. In MFC, we usually call this bounding box as "Clipping Region" which marks portion of area in client to be redrawn.

Defining the Redraw clipping region
Defining the Redraw clipping region | Source

5.3 Draw Rectangle in CMessageHndlView::OnDraw(CDC* pDC)

The function "OnDraw()" gets called by the Windows OS whenever the application requires a redraw. Some well-known examples are given below:

  1. Let us say a user minimized our application and then restored it. In this case, redraw of the client area is required.
  2. User stacked a notepad application on top our application and then closed the notepad application. The area, hidden by the Notepad requires a redraw.
  3. The user moved our window one location to other location. Here also a redraw is required.

Now, look at the code snippet below:

MFC OnDraw() and drawing a rectangle
MFC OnDraw() and drawing a rectangle | Source

In the above code snippet, first, the Rectangle that we are going to draw is defined in terms of Top, Left, Bottom, Right (Marked as 1) just like how we defined the Clipping region. After defining the rectangle, the Draw function checks the Boolean flag m_bluerect. When this boolean flag is true, a blue rectangle is drawn and green rectangle is drawn otherwise (Marked as 2 and 3).

When we compile and run the application at this stage we always see a blue rectangle. Now, let us start writing some code in the mouse button handler so that we can change the boolean flag.

5.4 Implement OnLButtonUp, OnRButtonUp Handlers

All Set. Now we will set the boolean member m_bluerect to true in the OnLButtonUp() handler and we set the same boolean variable as false in the OnRButtonUp() handler (Marked as 3 and 4). Now, when the user clicks the left mouse button, the boolean is set to true and when right mouse button click will set is as false.

In both the handler, we are making a call to "InvalidateRect()" function by supplying the bounding box (Marked as 1 and 2). This means that we are asking windows to redraw the portion of the "ClientArea" marked by the passed-in rectangle. And the windows will call the OnDraw() function and the OnDraw first clears the background of the clipping region and then draws either Blue and Green Rectangle. The screenshot below shows the code snippet:

Left and Right mouse button handler
Left and Right mouse button handler | Source

Note that when we call Invalidate, the entire client area gets redrawn. Watch the video below which explains the InvalidateRect and how it works by running the application with some sligh code changes:

WM_LBUTTONUP, WM_RBUTTONUP, InvalidateRect Explained

6. Code Listing and Sample application Download

Below are the code modified for the Application.

MessageHndlView.h

// MessageHndlView.h : interface of the CMessageHndlView class
//
#pragma once

class CMessageHndlView : public CView
{
protected: // create from serialization only
	CMessageHndlView();
	DECLARE_DYNCREATE(CMessageHndlView)

// Attributes
public:
	CMessageHndlDoc* GetDocument() const;

// Operations
public:

// Overrides
public:
	virtual void OnDraw(CDC* pDC);  // overridden to draw this view
	virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
	virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
	virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
	virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);

//Sample 01: Variable to store current rectangle colour
private:
	bool m_bluerect;
	CRect m_rctUpdateArea;

// Implementation
public:
	virtual ~CMessageHndlView();
#ifdef _DEBUG
	virtual void AssertValid() const;
	virtual void Dump(CDumpContext& dc) const;
#endif

protected:

// Generated message map functions
protected:
	DECLARE_MESSAGE_MAP()
public:
	afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
	afx_msg void OnRButtonUp(UINT nFlags, CPoint point);
};

#ifndef _DEBUG  // debug version in MessageHndlView.cpp
inline CMessageHndlDoc* CMessageHndlView::GetDocument() const
   { return reinterpret_cast<CMessageHndlDoc*>(m_pDocument); }
#endif

MessageHndlView.cpp

// MessageHndlView.cpp : implementation of the CMessageHndlView class
//

#include "stdafx.h"
#include "MessageHndl.h"

#include "MessageHndlDoc.h"
#include "MessageHndlView.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CMessageHndlView

IMPLEMENT_DYNCREATE(CMessageHndlView, CView)

BEGIN_MESSAGE_MAP(CMessageHndlView, CView)
	// Standard printing commands
	ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CView::OnFilePrintPreview)
	ON_WM_LBUTTONUP()
	ON_WM_RBUTTONUP()
END_MESSAGE_MAP()

// CMessageHndlView construction/destruction

CMessageHndlView::CMessageHndlView()
{
	//Sample 02a: Initialize the Mode
	m_bluerect = true;

	//Sample 02b: Initialize the Update Rectangle
	m_rctUpdateArea.top = 5;
	m_rctUpdateArea.left = 5;
	m_rctUpdateArea.bottom = 170;
	m_rctUpdateArea.right = 170;
}

CMessageHndlView::~CMessageHndlView()
{
}

BOOL CMessageHndlView::PreCreateWindow(CREATESTRUCT& cs)
{
	// TODO: Modify the Window class or styles here by modifying
	//  the CREATESTRUCT cs

	return CView::PreCreateWindow(cs);
}

// CMessageHndlView drawing

void CMessageHndlView::OnDraw(CDC* pDC)
{
	CMessageHndlDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	// TODO: add draw code for native data here
	//Sample 03: Draw the Rectangle
	CRect rct(10,10,160,160);
	if (m_bluerect == true)
	{
		pDC->FillSolidRect(rct, RGB(0,0,255));
	}
	else
	{
		pDC->FillSolidRect(rct, RGB(0,255,0));
	}

}


// CMessageHndlView printing

BOOL CMessageHndlView::OnPreparePrinting(CPrintInfo* pInfo)
{
	// default preparation
	return DoPreparePrinting(pInfo);
}

void CMessageHndlView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
	// TODO: add extra initialization before printing
}

void CMessageHndlView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
	// TODO: add cleanup after printing
}


// CMessageHndlView diagnostics

#ifdef _DEBUG
void CMessageHndlView::AssertValid() const
{
	CView::AssertValid();
}

void CMessageHndlView::Dump(CDumpContext& dc) const
{
	CView::Dump(dc);
}

CMessageHndlDoc* CMessageHndlView::GetDocument() const // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMessageHndlDoc)));
	return (CMessageHndlDoc*)m_pDocument;
}
#endif //_DEBUG


// CMessageHndlView message handlers

void CMessageHndlView::OnLButtonUp(UINT nFlags, CPoint point)
{
	//Sample 04: Set the Rct colour to Blue
	m_bluerect = true;
	InvalidateRect(m_rctUpdateArea);

	CView::OnLButtonUp(nFlags, point);
}

void CMessageHndlView::OnRButtonUp(UINT nFlags, CPoint point)
{
	//Sample 05: Set the blue rectangle to false
	m_bluerect = false;
	InvalidateRect(m_rctUpdateArea);

	CView::OnRButtonUp(nFlags, point);
}

Download SourceCode

Sample Application Download: Link

Comments

    0 of 8192 characters used
    Post Comment

    No comments yet.