Monday, November 23, 2009

Working with Mapping Modes

Working with Mapping Modes

This section describes windows and viewports, then presents mapping modes, origins, and extents.

All drawing functions called by a Windows-based application draw to logical space. Windows maps — or transforms — the drawing to physical space. As a result, the application has the advantage of being able to "size" the logical space in a way that is convenient for the problem domain at hand, and to have Windows map the drawing to the physical space. For example, for some applications it might be convenient to think of the logical space as if it is dimensioned in terms of miles or kilometers.

This section includes the following topics:

Windows and Viewports

The drawing area in logical space is called the "window."

The drawing area in physical space is called a "viewport." It typically corresponds to a view in your application that represents a given output device, such as a monitor screen or a printer. Physical coordinates are often called device coordinates or device units.

You can use CDC "viewport" and "window" functions and mapping modes to control how the drawing in logical space will be mapped (or transformed) into the physical space of the viewport.

To see an overview of how to initialize the view for isotropic drawing, click this icon.

® Mapping Modes

Because your application can change the mapping mode, it can control how the drawing in logical space will be transformed to physical space. The mapping mode defines the unit of measure that is used to convert logical units to physical units; it also defines the orientation of the device's x- and y-axes.

Windows supports eight mapping modes. Six are absolute; two are proportional. An absolute mapping mode equates a logical unit to a fixed unit of measure; a proportional mapping mode varies the display with the relative size of the output window.

The following table describes the eight mapping modes, how they transform a drawing from logical space to physical space, and notes on usage or outcome.

Mapping mode Mapping rules Notes


Absolute None Absolute modes equate a logical unit to a fixed unit of measure.

MM_TEXT Each logical unit is converted to 1 device pixel. Positive x is to the right; positive y is down. The default mode. It follows the conventions of text display for many languages: left to right, top to bottom.

MM_LOENGLISH Each logical unit is converted to 0.01 inch. Positive x is to the right; positive y is up. Useful in applications that must draw in physically meaningful units of measure (such as inches or millimeters).

MM_HIENGLISH Each logical unit is converted to 0.001 inch. Positive x is to the right; positive y is up. (same as above)

MM_LOMETRIC Each logical unit is converted to 0.1 millimeter. Positive x is to the right; positive y is up. (same as above)

MM_HIMETRIC Each logical unit is converted to 0.01 millimeter. Positive x is to the right; positive y is up. (same as above)

MM_TWIPS Each logical unit is converted to 1/20 of a point. (Because a point is 1/72 inch, a twip is 1/1440 inch.) Positive x is to the right; positive y is up. (same as above)

Proportional None Display varies with the relative size of the output window.

MM_ANISOTROPIC Allows the x- and y-coordinates to be adjusted independently. The exact shape of the image is not preserved.

MM_ISOTROPIC Ensures a 1:1 aspect ratio. The exact shape of an image is preserved.



The functions CDC::SetMapMode and CDC::GetMapMode manage the current mapping mode. Each application can change the mapping mode for its client area; one view can display concurrently graphical objects that are created with different mapping modes.

® Origins and Extents
The default origin (location for the coordinates 0,0) is the upper-left corner of the view. The default window and viewport extents establish a ratio or scaling factor of 1:1, so one unit in logical space is equivalent to one pixel in physical space.


Note that it is the ratio between extents of the window and the viewport that is important, not the actual values for each.

To see sample code that shows how to set origins and extents in order to display a soccer field in the viewport, click this icon.

// FootView.CPP
void CFootballView::SetMappingMode(CDC *pDC)
{
// Preserve aspect ratio
pDC->SetMapMode(MM_ISOTROPIC);
// A soccer field is 110 x 70 meters, but the drawing
// needs a 10 meter border on all 4 sides.
// Constants: LENGTH = 110, WIDTH = 70, BORDER = 10
pDC->SetWindowExt(LENGTH + 2 * BORDER, WIDTH + 2 * BORDER);
CRect rect;
GetClientRect(&rect); // Get size of viewport
// Make y negative to increase y
// in an upwards direction.
pDC->SetViewportExt(rect.Width(), -rect.Height());
// Set the origin in the center of the screen
// by dividing the height and width by 2.
pDC->SetViewportOrg(rect.Width() / 2, rect.Height() / 2);
}

You can use the following functions to get information about or change the current settings.

Logical space Physical space


Origin CDC::GetWindowOrg, CDC::SetWindowOrg CDC::GetViewportOrg, CDC::SetViewportOrg

Extent CDC::SetWindowExt, CDC::GetWindowExt CDC::SetViewportExt, CDC::GetViewportExt



You can obtain the capabilities of your output device through CDC:GetDeviceCaps.

DPtoLP and LPtoDP

Often there is a need to transform between logical and device coordinates. For example, mouse handlers receive information only in device units, but there may be a requirement to use the information in logical units. The CDC class provides two functions to perform these transformations, DPtoLP and LPtoDP.

The following example code shows how to transform device units to logical units:

void CMyView::OnMouseMove(UINT nFlags, CPoint point)

{
// Create a DC based upon this view.
CClient dc(this);
// convert the point
dc.DPtoLP(&point);
// point is now in logical units.
// do something with it...
}


Special Visual Effects

MFC provides several ways to create special visual effects. This section discusses three of these methods: using ROP2 codes to set the raster copying mode, using the CRectTracker class to handle OLE objects and to customize the borders of polygons, and using the BitBlt function to efficiently repaint data to the screen.

This section includes the following topics:

Using ROP2 Codes

ROP (raster operation) codes can be used to erase objects, emulate continuous movement, or create other visual effects. In a graphical application, it is common to draw with different graphical objects, and colors, in the same area of the screen. Windows uses the current setting of the ROP attribute of the DC to determine how the colors interact in these areas. For example, a black line can be made to stand out against a black-filled rectangle by drawing the line with an ROP code of R2_XORPEN selected.

The ROP attribute of the DC is managed with the CDC::SetRop2 and CDC::GetRop2 functions.

The complete code for a sample application that shows how different ROP2 codes achieve certain effects is in \Samples\Ch07.

For more information, search for "SetROP2" and "GetROP2" in Visual C++ Help.

® Using the CRectTracker Class

The CRectTracker class allows an item to be displayed, moved, and resized in different ways. Although the CRectTracker class is designed to allow the user to interact with OLE items by using a graphical interface, its use is not restricted to OLE-enabled applications.

Specific visual effects that CRectTracker provides include the following:

® CRectTracker borders can be solid or dotted lines.

® The item can be given a hatched border or overlaid with a hatched pattern to indicate different states of the item.

® You can place eight resize handles on either the outside or the inside border of the item. (For information about resize handles, search for "GetHandleMask" in Visual C++ Help.)

® You can change the orientation of an item during resizing.



For more information about the CRectTracker class, see Visual C++ Help.

Using BitBlt

Flicker can occur if the view is rapidly and repeatedly repainted. This can happen, for example, when a mouse-move event generates successive paint messages. This flicker is generated by a sequence of alternating view-erase and view-drawing code executing. If your application requires this functionality, then one way to remove the flicker is to perform all painting on an internal bitmap and then transfer the bitmap to the view using a BitBlt operation.

Much of this functionality can be hidden from the application drawing code by overriding the view's OnPaint handler. The OnPaint handler normally sets up CPaintDC and then calls the view's OnDraw function. The intention is to create a memory device context and pass this new context to the OnDraw function. The drawing code uses the new DC; the drawing is simply stored in memory and not sent directly out to the view. When the OnDraw function returns, the OnPaint handler performs a rapid bitmap transfer from the memory DC to the view's DC.

To override a view's OnPaint handler

1. Create a memory DC object, compatible with the view.
2. Create a new bitmap object, compatible with the view.
3. Select the bitmap into the CDC object.
4. Call the view's OnDraw function.
5. Select the bitmap into the view's DC.
6. Call the view DC's BitBlt function using the memory DC as the source.
7. Delete the memory DC object and the bitmap object.

The following example code shows you how to implement this functionality:

void CMyView::OnPaint()
{
// Generate a paint DC based upon the view.
CPaintDC dc(this); // device context for painting

// Create a compatible memory dc

pMemDC= new CDC;
pMemDC->CreateCompatibleDC(&dc);

// Get the view's dimensions

CRect rect;
GetClientRect(&rect);

// Create compatible bitmap.

pBitmap = new CBitmap;
pBitmap->CreateCompatibleBitmap( &dc, rect.Width(), rect.Height());
pMemDC->SelectObject(pBitmap);
// Call the view's OnDraw, passing it the memory DC.
OnDraw(pMemDC);
// do the bitblt to the view from the memory DC.
dc.SelectObject(pBitmap);
dc.BitBlt(
0,0,rect.Width(), rect.Height(), pMemDC, 0, 0, SRCCOPY);
// clean up memory DC and bitmap
pBitmap->DeleteObject();
delete pBitmap;
delete pMemDC;
}

Self-Check Questions

1. Which one of the following is not a responsibility of a device context?

f A. A DC gives permission to a program to write to an output device.
f B. A DC maintains a clipping region for the associated window.
f C. A DC gives permission to a program to read from an input device.
f D. A DC maintains current information about how to draw to a window.

2. If you called CDC::SetTextColor with a value of RGB(255, 0, 0) and CDC::SetBkColor with a value of RGB(0, 0, 0), and subsequently called CDC::TextOut to display text to the client area, what would the text look like?

ff A. None would be displayed because no font was selected into the DC.
f B. Blue text with a white background.
f C. Red text with a white background.
f D. Red text on either a black background or window background color.
3. Which one of the following is not a GDI object?

f A. Rectangle
f B. Pen
f C. Font
f D. Bitmap

4. What is the default mapping mode in Windows?

f A. MM_TEXT
f B. MM_WINDOWS
f C. MM_TWIPS
ff D. MM_ISOTROPIC

Using GDI Objects

Using GDI Objects

This section describes the graphics user interface (GDI) objects that are used to draw output, their purpose, and how to use them. It also discusses creating your own GDI objects and shows how to use stock objects.

The tools used by the DC to draw output — regions, pens, brushes, fonts, bitmaps, and palettes — are encapsulated by MFC in classes that are derived from the CGdiObject base class. To see an illustration that shows some of the available GDI objects, click this icon.





The following table outlines the purpose and default values for each object.

Object Purpose Default value

CPen Used to draw lines and the border of all shapes, such as rectangles, polygons, and regions Solid black pen, one pixel wide


CBrush Used to fill in the interior of shapes such as polygons and regions Solid white brush
CFont Used to manipulate font characteristics System font
CBitmap Used to install and manipulate bitmaps None
CRgn Used to create, alter, and retrieve information about regions None
CPalette Used to create and manipulate palettes None

This section includes the following topics:

Creating Your Own GDI Objects

MFC provides classes for creating custom GDI objects. To define a GDI object, you simply create an object using the class constructor. If you use a constructor with no arguments, then one of the create functions should be called to initialize it. For example, the following example code shows two ways to define a pen object:

// Method number 1
CPen myPen1(PS_SOLID, m_penThickness, RGB(255,0,0));
// Method number 2
CPen myPen2;
myPen.CreatePen(PS_SOLID, 0, RGB(255,0,0));

The advantage of the latter method is that a constructor using no arguments will never throw an exception. However, each of the create functions returns a Boolean value that should be checked to determine whether the object was created successfully.

Default Attributes and Objects

When a CDC object is created, it gains a set of default attributes and GDI objects. These all reflect their system default values.

To manage these attributes, the CDC class contains a number of member functions. For example, CDC::GetTextColor and CDC::SetTextColor can be used to manage the color of text as it is drawn.

Overriding the Defaults

To change a GDI object from the default, you must select a new object into the DC. The CDC::SelectObject function is used to perform this selection. SelectObject selects the new object into the context and then returns the old object. Typically, this returned object is saved. When the drawing operation is complete, the original object is reselected into the DC.
CDC::SelectObject Functions

The following example code illustrates how various overloads of CDC::SelectObject support the GDI objects:

class CDC : public CObject
{ ...
CPen* SelectObject( CPen* pPen);
CBrush* SelectObject( CBrush* pBrush);
CFont* SelectObject( CFont* pFont);
CBitmap* SelectObject( CBitmap* pBitmap);
CRgn* SelectObject( CRgn* pRegion);
...
};

Note Palettes are selected into the DC using CDC::SelectPalette. Saving the Deselected Object

Save the deselected object so that it can be restored later. Three important reasons to do this are:
® It is good programming etiquette to restore the DC to its original state.
® To ensure that the data in the DC is valid, it must not be left pointing to a local GDI object (such as myBrush in the following example).
® Often a DC is shared between functions. Restoring the DC ensures that the next function to receive the DC will receive it in a known state.

The following example code shows how to create and use a GDI CBrush object:

void CMyView::OnDraw(CDC* pDC)
{
CBrush myBrush;
myBrush.CreateSolidBrush(RGB(0, 255, 0));
// Save the original brush.
CBrush *pOldBrush;
pOldBrush= pDC->SelectObject(&myBrush);
// Call drawing functions as required.
// ...
// Restore the old pen before exiting.
pDC->SelectObject(pOldBrush);
}

Using Stock Objects

Windows maintains a set of standard GDI objects for system and program use called stock objects. Stock objects include commonly used pens, brushes, and fonts. There are several advantages to using stock objects:

® Because they are built in, these stock objects do not need to be constructed or created.
® Multiple applications can use stock objects concurrently.
® Stock objects do not increase the number of resources used by the application.


CDC::SelectStockObject

Use CDC::SelectStockObject to request predefined fonts, pens, or brushes (or a default palette).

Note Remember that you are using an existing object, not creating a new one. The following table summarizes the types of stock objects available.


GDI category Stock object values

Fonts ANSI_FIXED_FONT

ANSI_VAR_FONT
DEVICE_DEFAULT_FONT
OEM_FIXED_FONT
SYSTEM_FONT
Pens BLACK_PEN
WHITE_PEN
NULL_PEN
Brushes BLACK_BRUSH
DKGRAY_BRUSH
GRAY_BRUSH
HOLLOW_BRUSH
LTGRAY_BRUSH
NULL_BRUSH
WHITE_BRUSH

Note Be sure to explicitly cast the CGdiObject pointer returned by SelectStockObject to a pointer of the proper type. Technically, a stock object does not have to be released because it is never really owned by your application. However, good programming etiquette dictates that you restore the previous GDI object, which releases the stock object.


To use a stock object
1. Create a pointer to the current object in the device context and select into the device context the object that your application will use to draw.

void CmyView::OnDraw(CDC* pDC)
{ //...
// save pointer to old Pen
CPen *pOldPen;
pOldPen = (CPen*)pDC->SelectStockObject(WHITE_PEN);
// Call drawing functions as required
// ...
2. Restore the old object before exiting.
// ...
pDC->SelectObject(pOldPen);
}

Using Pens

Pens, encapsulated in the CPen class, are used to draw lines, curves, and shapes. Pens have three characteristics: style, width, and color.

Styles

There are seven pen styles:

® PS_SOLID
® PS_DOT
® PS_DASH
® PS_DASHDOT
® PS_DASHDOTDOT
® PS_NULL
® PS_INSIDEFRAME



To see an illustration that shows examples of the first five pen styles, click this icon.



Use a PS_NULL pen style to draw a shape without a border. If you draw a line with a null pen, it will be invisible.

Use a PS_INSIDEFRAME pen style to draw inside the frame of closed shapes.

Width

The width of pen output is described in pixels. Use 0 to guarantee the narrowest possible width (1 pixel), regardless of the mapping mode. For more information about mapping modes, see Mapping Modes in this chapter. For values greater than 1, the pen style will always be PS_SOLID. If a different style is specified, it will be ignored.

Color

Use the RGB macro to create a COLORREF value for the color. For more information about COLORREF, see Setting Colors in this chapter.

Using Brushes

Brushes, encapsulated in the CBrush class, are used to fill areas. You can choose from three types, depending upon the kind of fill pattern you want — solid, a standard hatched pattern, or a bitmap pattern.

Unlike CFont and CPen, CBrush has multiple versions of the constructors and creation functions. This reflects the fact that brushes come in three main types.
Solid Brushes
Solid brushes require only a COLORREF argument.
Hatched Brushes
Hatched brushes require a hatch-style index and a COLORREF argument.
The standard hatch style indexes are:
® HS_BDIAGONAL
® HS_CROSS
® HS_DIAGCROSS
® HS_FDIAGONAL
® HS_HORIZONTAL
® HS_VERTICAL

To see an illustration that shows examples of the hatch styles, click this icon.


The following code example shows you how to create a blue hatched brush to fill a polygon. The hatch is aligned to the first point of the polygon by using the SetBrushOrg function.

// Construct and create the brush.
CBrush brush;
brush.CreateHatchBrush( HS_CROSS, RGB(0,0,255));
// set the bitmap origin.
pDC->SetBrushOrg(m_pts[0].x % 8, m_pts[0].y % 8);
CBrush* pOldBrush= pDC->SelectObject(&brush);
pDC->Polygon(m_pts, GetSize());
pDC->SelectObject(pOldBrush);

Bitmapped Brushes


A bitmapped brush requires a pointer to a bitmap; 8 x 8 pixels is the minimum size. This bitmap is tiled to fill the required area.

Using Fonts

MFC encapsulates fonts in the CFont class. A font is a set of characters of the same typeface (such as Courier), stroke weight (such as bold), and size (such as 10-point). To see an illustration that shows the various characteristics of a font,

click this icon.


Note The cell (or box) that surrounds each character determines the character spacing of a font.


A font family is made up of several fonts that may vary widely, but that have some common characteristic. The following table lists and describes the six font families defined by Windows.

Font family name Description

Decorative Novelty font; for example, Old English

Don't Care Generic family name

Modern Monospace font with or without serifs; for example, Pica, Elite, and Courier New

Roman Proportional font with serifs; for example, Times New Roman

Script Font designed to look like handwriting; for example, Script and Cursive

Swiss Proportional font without serifs; for example, Arial

Fixed-Pitch Fonts

In a fixed-pitch (monospace) font of a given size, each cell has the same dimensions, regardless of the width of the character that occupies it.

In the following example, note that every character in the second line falls exactly beneath a character in the previous line:

Example of a fixed-pitch font.

This is the TrueType font Courier New.



Software applications, such as source code editors, usually rely on fixed-pitch fonts because they allow easy vertical alignment of program constructs.

Proportional Fonts

In a proportional font, the cells vary in width, depending upon the relative width of the letter (l versus m, for example). The space between characters is designed to be optically pleasing, rather than uniform, and thus easier to read. This paragraph is set in Arial, a proportional font.

In addition to readability, proportional fonts offer the advantage of occupying less physical space.

Vector Fonts vs. Raster Fonts

Fonts can be stored and drawn by using one of two techniques:

® Vector fonts

Each font is stored as a mathematical description of the curves that compose each character. These descriptions are then used to draw a font of virtually any size. Microsoft TrueType and Adobe PostScript are the two best-known vector font technologies.

® Raster fonts

Each font character is stored as a bitmap pattern exactly as it would be displayed on an output device. Raster fonts are designed as sets for a specific point size, such as 6-point or 12-point font.
Though displaying vector fonts requires more computation, they are a more compact, flexible method for manipulating font information. In addition, vector fonts can also be rotated and stretched.
Because raster-font characters are stored and displayed as is, they can be quickly realized and output.

TrueType Fonts

A TrueType font character is a collection of line and curve commands, as well as a collection of hints. Windows uses the line and curve commands to determine the outline for a character or symbol. It uses the hints to adjust the length of lines and the shapes of curves so that a character will look as good as possible on a particular display.

TrueType fonts are easily resized, rotated, and stretched, but maintain a high quality of appearance that is comparable with a raster font.

Font Metrics

Because fonts are complicated entities, descriptions of them are also complicated. The standard data structure in Windows that is used to describe fonts is TEXTMETRIC, a data structure that contains 20 fields to describe the font.

You can use the CDC::GetTextMetrics function to retrieve information about the metrics for the currently selected font. This function places the information in a TEXTMETRIC data structure.

For TrueType fonts, a similar function, CDC::GetOutlineTextMetrics, can be used to fill in an OUTLINETEXTMETRIC structure.

The following code example shows how to use the TEXTMETRIC structure to set the range for a CScrollView class:

TEXTMETRIC tm;

pDC->GetTextMetrics(&tm);

CSize scrollRange;

scrollRange.cx = 100;

scrollRange.cy = tm.tmHeight * GetDocument()->GetSize();

SetScrollSizes( MM_TEXT, scrollRange);

Note All of the fonts that are installed on the system can be enumerated by using the ::EnumFonts and ::EnumFontFamiliesEx Windows SDK functions. However, these functions require the use of a callback function.

Logical Fonts vs. Physical Fonts

A logical font is a description of an ideal font. This ideal font may or may not actually exist on the system. A LOGFONT data type represents a logical font. (A LOGFONT data type is actually a typedef-defined structure to hold font information.)

A physical font is a font that is actually installed on the target system; therefore, it can be displayed. The TEXTMETRIC (or OUTLINETEXTMETRIC) data type represents it.

From the perspective of an application, a logical font represents the desired font, while the physical fonts represent the fonts available for actual use.

Using a Non-Stock Font

Using fonts other than the stock fonts can be very helpful when you need a specialized look.

To use non-stock fonts

1. Construct the font object.

2. Initialize the font using CFont::CreateFont, CFont::CreateFontIndirect, CFont::CreatePointFont, or CFont::CreateFontIndirect.

3. Select the font into the DC.



The following example code illustrates the creation of a font object using CFont::CreatePointFont:
CFont font;
font.CreatePointFont( 120 , ""); // 12 point, default typeface
LOGFONT logfont;
font.GetLogFont(&logfont); // retrieve specifics about the font.
CFont* pOldFont = pDC->SelectObject(&font);
// use font...



When an application requests the selection of a font into a DC, the font mapper portion of the Windows GDI is responsible for producing the closest match between the requested font and the available fonts. This process is called "font realization."

Using Bitmaps
A bitmap is an array of bits that contain data that describes the colors found in a rectangular region on the screen (or a rectangular region found on a page of printed paper).


MFC provides support for working with bitmaps through the CBitmap class and the CDC bitmap functions.

The two types of bitmaps are device-dependent bitmaps (DDBs) and device-independent bitmaps (DIBs). DDBs were common in versions before Windows 3.0. In fact, they were the only bitmaps available to developers. However, as display technology improved and as the variety of display devices increased among Windows users, certain inherent problems surfaced. For example, because there was no method of storing (or retrieving) the resolution of the display type on which a bitmap was created, a drawing application could not quickly determine whether a bitmap was suitable for the type of video display device on which the application was running. To solve this problem, beginning with Windows version 3.1, Microsoft started to use DIBs.
Device-Independent Bitmaps

A DIB contains the following color and dimension information:

® The color format of the device on which the rectangular image was created.

® The resolution of the device on which the rectangular image was created.

® The palette for the device on which the image was created.

® An array of bits that maps red, green, blue (RGB) triplets to pixels in the rectangular image.

® A data-compression identifier that indicates the data-compression scheme (if any) that is used to reduce the size of the array of bits.

The following example code creates a bitmap compatible with a specific DC:

CRect rect;
GetClientRect(&rect); // get the dimensions of the view
m_pBitmap = new CBitmap;
// create a bitmap compatible with the DC.
m_pBitmap->CreateCompatibleBitmap(
pDC, rect.Width(), rect.Height());

You can also load a bitmap file into your application, although this requires more work. For information, search for "LoadImage" in Visual C++ Help.

Using Regions
The CRgn class encapsulates a Windows GDI region. A region is an area built up from one or more elliptical or polygonal shapes. To use regions, use the member functions of the CRgn class with the clipping functions defined as members of the CDC class.


The following example code shows how to clip a view area to show only a complex elliptical subset of the rectangle:

// Create the complex, elliptic region.
CRgn rgn1, rgn2;
rgn1.CreateEllipticRgn(50,50,200,100);
rgn2.CreateEllipticRgn(100,25,50,300);
rgn2.CombineRgn(&rgn1, &rgn2, RGN_OR);
// Select it as the clip region
pDC->SelectClipRgn(&rgn2);
// Now draw the black rectangle.
CRect rect(0,0,400,400);
pDC->SelectStockObject(BLACK_BRUSH);
pDC->Rectangle(&rect);

For more information, search for "CRgn" in Visual C++ Help.

Using Palettes
The CPalette class encapsulates a Windows color palette. A palette provides an interface between an application and a color output device (such as a display device). The interface allows the application to take full advantage of the color capabilities of the output device without severely interfering with the colors that are displayed by other applications. Windows uses the application's logical palette (a list of needed colors), and the system palette (definitions of available colors) to determine the colors that are used.


A CPalette object provides member functions for manipulating the palette referred to by the object. You can construct a CPalette object and use its member functions to create the actual palette, a GDI object, and to manipulate its entries and other properties.

For more information, search for "CPalette" in Visual C++ Help

Using the CDC Class

Using the CDC Class

The CDC class contains a large amount of functionality. This section introduces you to the CDC class and several of its important text and graphics functions. Classes derived from the CDC class are also discussed.

The CDC class defines a class of device context objects. The CDC class provides member functions for device context operations, working with drawing tools, graphics device interface (GDI) object selection, and working with colors and palettes. Member functions are also provided for drawing text.

This section includes the following topics:

Setting Colors

In Windows, colors for both text and graphics are represented by a 32-bit value. Because MFC does not contain a class that encapsulates color information, MFC and SDK developers use the COLORREF data type. To see an illustration that shows COLORREF using eight bits, or a range of 0 – 255, for each of the red, green, and blue color components, click this icon.




The high-order eight bits of a COLORREF should not be changed. These bits must contain zeros in order for a COLORREF variable to function correctly.

Windows provides the following macros to manipulate colors:

® RGB, which returns the COLORREF value based on the component values.

® GetRValue, which returns the red component of a COLORREF value.

® GetGValue, which returns the green component of a COLORREF value.

® GetBValue, which returns the blue component of a COLORREF value.

The following example code illustrates the use of these macros:

COLORREF color = RGB(12,34,56);
CBrush brush(color);

TRACE("The Red component of the brush is %d\n", GetRValue(color));

TRACE("The Green component of the brush is %d\n", GetGValue(color));

TRACE("The Blue component of the brush is %d\n", GetBValue(color));

Simple Graphics Functions

The CDC class provides the basic drawing shapes from which you can create more complex shapes. You can, for example, manipulate individual pixels, draw lines, or create polygons.

Pixel Manipulation

Pixel manipulation (CDC::SetPixel and CDC::GetPixel) enables you to have the finest control of drawing shapes. For example, the following example code uses OnDraw to set individual pixels at startup:

void CMyView::OnDraw(CDC* pDC)
{
// Draw a red dot at location 100, 100
pDC->SetPixel(CPoint(100,100), RGB(255, 0, 0));
}


Line Drawing Functions
Several functions are available for drawing line output.
You can draw simple line segments using the CDC::MoveTo and CDC::LineTo functions. The CDC::MoveTo function is used to set the starting position for a pen. The CDC::LineTo function then draws a line from the starting position to the new position. This new position then becomes the current position and further CDC::LineTo functions can be called.

CDC::Polyline can be used to draw a set of line segments connecting the points that are specified in an array of POINT structures or CPoint objects. The lines are drawn from the first point through subsequent points by using the current pen. Unlike the LineTo member function, the Polyline function neither uses nor updates the current position.

The following example code draws a simple X out to the device:

pDC->MoveTo(0,0);
pDC->LineTo(100,100);
pDC->MoveTo(0,100);
pDC->LineTo(100,0);


Other Drawing Functions

CDC::Rectangle draws a rectangle by using the current pen for the border and the current brush for the interior of the rectangle.

CDC::Ellipse draws an ellipse by using the current pen for the border and the current brush for the interior of the rectangle.

CDC::Polygon draws a polygon from an array of points.
CDC::FrameRect draws a rectangular border with a supplied brush.
CDC::FillRect fills a rectangular area with a supplied brush.
The following example code draws a circle embedded within a square:

CRect rect(0,0,100,100);
pDC->Rectangle(&rect);
pDC->Ellipse(&rect);

Simple Text Functions

The CDC class contains functions for drawing font-independent text. It also contains functions to set and get basic attributes of the text, such as color and alignment. Logically, drawing text consists of two operations: first, setting the attributes of the text, and then writing the text out to the device.

Text Output Functions

Several CDC functions are commonly used for text. The following table lists four functions that control text output.

Function Description


TextOut Displays a text string at the given location by using the currently selected font, colors, and alignment.

TabbedTextOut Similar to TextOut, but supports expansion of tab stops.

DrawText Displays formatted text within a bounding rectangle.

ExtTextOut Similar to TextOut, but allows specification of clipping options.

Text Display Attributes

These functions can be used to affect text display attributes. The following table lists four sets of functions that control text display attributes.

Function Description

SetTextColor and GetTextColor Control the color of the text displayed.


SetBkMode and GetBkMode Control whether or not the cells that surround each letter are filled before the corresponding characters are displayed. In Opaque mode, the cells are filled with the current background color. In Transparent mode, no fill takes place.

SetBkColor and GetBkColor Control the color of the cells that contain the displayed text. The background mode must be Opaque before this attribute is used in a fill.

SetTextAlign and GetTextAlign Control the alignment of text relative to the window position specified in one of the text output functions.

Note The background mode and color are also used for pen and brush operations. The following example code shows how to display simple text in the view:


void CSimpleTextView::OnLButtonDown(UINT nFlags, CPoint point)
{
CClientDC dc(this);
dc.SetTextcolor(RGB(0, 0, 255));
dc.TextOut(point.x, point.y, "Hello");
CView::OnLButtonDown(nFlags, point);
}

CDC-Derived Classes

There are four CDC-derived classes that provide specialized device contexts.

CDC-derived class Drawing area affected

CClientDC Client area of a window

CPaintDC Invalid region of a client area
CWindowDC Client and nonclient areas
CMetafileDC A metafile

Note You can also use a CDC object to create a memory device context, which is compatible with an existing DC. This would be useful for preparing images in memory before you transfer them to the device. A simple example of painting text to the screen uses the derived class CPaintDC, called from the OnPaint member function. The following example code shows you how to paint a single line of text to the center of the screen, using the DrawText member function:


void CMyView::OnPaint()
{
CPaintDC dc(this);

CRect rect;

GetClientRect(&rect);
dc.DrawText("Hello world", -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
}

Note The – 1 tells Windows that the string is null-terminated; the three flags tell it how to draw the text. The DrawText function uses the default system font. If you wanted to use another font, you would use the CFont class. For more information about CFont, see Using Fonts in this chapter.

Outputting Text and Graphics

Chapter 7: Outputting Text and Graphics

In Windows 95 and Windows NT, text and graphics output is handled by the graphics device interface (GDI) dynamic-link library. The GDI ensures that Windows-based applications work with a wide variety of different printers, plotters, and displays.

This capability to work with a wide variety of output devices, often referred to as device independence, means that the developer does not have to deal with the differences between the types of output devices. Access to the GDI is provided primarily through the CDC class and its derivatives.

This chapter introduces you to some of the fundamental concepts of the GDI. You will learn how to output text and graphics to a device context (DC) as well as use a variety of drawing objects and drawing primitives that are provided by MFC.

Objectives

After completing this chapter, you will be able to:

® Describe a device context (DC).

® Explain the purpose of CDC, CPaintDC, and CClientDC.

® Output text to the view window.

® List and describe standard graphics device interface (GDI) objects.

® Output simple graphics objects, such as lines and rectangles.

® Use stock objects effectively in an application.

® Describe mapping modes.

® Describe ROP2 codes.

® Use the BitBlt function.

In order to ease the burden of outputting text and graphics to a large variety of different devices, Windows uses a construct called a device context. A device context is an abstract layer that your application writes to when generating graphics and text. Device contexts relieve you of the burden of having to write code for every output device available in the marketplace.

This section describes device contexts and how they work, and the MFC classes that you can use to draw to an output device.

This section includes the following topics:

Introducing Device Contexts

A device context (DC) is a data structure containing fields that describe the information that the GDI needs to know about the display surface as well as the context in which it is being used. In Windows, a DC does the following:

® Gives permission to an application to use an output device.

® Provides a link between a Windows-based application, a device driver, and an output device, such as a monitor or printer.

® Maintains current information about how to draw or paint to a window, such as the colors, brush or pen patterns, pen widths, and so on.

® Maintains a clipping region for a window, which limits the program output to the areas of the output device covered by the window.

Once you have a DC, you can use a variety of GDI objects to draw lines, geometric shapes, and text to a view in your application.

Device Contexts and MFC:
MFC encapsulates device context capabilities through the CDC class. The core of this class is the data member m_hDC, which represents a handle to a Windows DC.

The CDC class is a large one. It contains creation and initialization functions for a device context, as well as many general GDI operations, including drawing simple graphic objects such as lines, rectangles, and ellipses, as well as more sophisticated functions that operate on regions, bitmaps, and clipping areas.

To see an illustration that shows the object hierarchy for the CDC base class and its four derived classes, click this icon.




The OnDraw Function

The first place that a developer typically uses a device context is within the view's OnDraw function. AppWizard includes the OnDraw function when it creates the application. The OnDraw function looks similar to the following:

void CMyView::OnDraw(CDC* pDC)
{
CMyDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);

// TODO: add draw code for native data here

}

The OnDraw function is called when the view becomes stale and needs to be refreshed. Whenever a window or portion of a window needs to be repainted, the operating system sends it a WM_PAINT message with information on what portion of the window needs to be repainted. In the MFC framework, this information is given to CWnd::OnPaint, which creates a DC of the appropriate type and calls the OnDraw function for the view.

To see an illustration that shows how OnDraw represents a factoring of the common code that is used in the display (paint) operation (CWnd::OnPaint) and the print and print-preview operations (CView::OnPrint), click this icon.




Using OnDraw for the bulk of output coding ensures a high degree of consistency across print and display operations. A close similarity between what users see on the screen and what they get when printing is referred to as “what you see is what you get,” or WYSIWYG.

Accessing a DC in MFC


Two common methods are used to access a device context (DC) in an MFC application:

® Receiving a pointer to a CDC object as a function parameter.

® Creating a temporary, local CDC object.



Some message handlers receive a pointer to a CDC object that is used to perform display operations. If the handler receives just a pointer to a CDC object, then the handler does not have to release the DC or clean up the object.

For example, the following example code illustrates using only a pointer to a CDC object:

void CMyView::OnDraw(CDC* pDC)

{

// ...

// Draw a black dot at location 10,10

pDC->SetPixel(CPoint(10,10), RGB(0,0,0));



}



You can create a local DC object by using any of the CDC-derived classes. In the following example code, the handler draws a black dot at the location of a left-mouse-button-down event in the view:

void CMyView::OnLButtonDown(UINT nFlags, CPoint point)

{
CClientDC dc(this);
dc.SetPixel(point, RGB(0,0,0));
CView::OnLButtonDown(nFlags, point);
}

Note Because the DC was created on the stack, it is released by the DC-object destructor when the local object dc goes out of scope. This returns the resource and memory to the system.

what is oracle? video tutorial??

Create a MessageMap

Create a MessageMap

To get you started using message maps, this section walks you through the process of declaring and implementing a message map and adding the corresponding message handler. Most of the work required to create a message map is done automatically by AppWizard. Your responsibility in working with messages is limited to making message map connections between messages and their handler functions.

This section includes the following topics:

® Declaring a Message Map

® Implementing a Message Map

® Adding a Message Handler

For detailed information about message map construction, see "Technical Note 6: Message Maps," available in the Microsoft Foundation Class Reference section in the Visual C++ online documentation.

Declaring a Message Map

To declare a message map, add a DECLARE_MESSAGE_MAP macro to the end of a class declaration in the corresponding header file, xxx.h. If you use AppWizard to create the starter files for your application, a declare statement for each class is added automatically to the header file. The DECLARE_MESSAGE_MAP statement simply creates a message map table in which each of the message map entries will be stored. ClassWizard maintains the declarations for message handlers.

For example, the OnLButtonDblClk message handler and message map associated with the CMsgView class are declared with the following statements in the header file, xxx.h:

// Generated message map functions
protected:
//{{AFX_MSG(CMsgView)
afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()

The afx_msg prefix is a visual reminder that OnLButtonDblClk is a message handler. You can omit afx_msg from your code because it evaluates to nothing when the code is compiled. The term "afx_msg" implies that a function behaves as a virtual function, but does not require a vtable entry.

The DECLARE_MESSAGE_MAP statement is the final statement in the class declaration because it uses C++ access specifiers to specify the visibility of its member functions. You can declare member functions of your own following the DECLARE_MESSAGE_MAP statement, but if you do, you should start off with a public, protected, or private keyword to ensure that you get the accessibility you want for these member functions.

Implementing a Message Map

Once the message map is declared, you can use ClassWizard to implement the message map and add the appropriate entries to the class implementation file, xxx.cpp. The BEGIN_MESSAGE_MAP and END_MESSAGE_MAP macros bracket the message map. The following example code shows a message map for the derived view class, CMsgView, in the implementation file, Msg.cpp.

// MSG.CPP
BEGIN_MESSAGE_MAP(CMsgView, CView)
//{{AFX_MSG_MAP(CMsgView)
ON_WM_LBUTTONDBLCLK()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

The BEGIN_MESSAGE_MAP macro begins the message map and identifies both the class to which the message map belongs and the base class. Message maps are passed by inheritance just as other class members are, and the base class is required so the framework can identify the message map associated with the base class. If the message is not handled, the framework determines to which base class to pass a message by looking at the parameters to BEGIN_MESSAGE_MAP. The END_MESSAGE_MAP macro ends the message map.

ClassWizard maintains the message map entries between the //{{AFX_MSGMAP and //{{AFX_MSGMAP comments. Anything placed in this area should be placed there by ClassWizard. ON_WM_LBUTTONDBLCLK is a macro defined in the header file (.h), which adds an entry for WM_LBUTTONDBLCLK messages to the message map. The macro accepts no parameters because it is hard coded to link WM_LBUTTONDBLCLK messages to the OnLButtonDblClk message handler.

You can also process a message for which MFC does not provide a message map macro. To do this, you create an entry for the message using the ON_MESSAGE macro, which accepts two parameters: the message ID and the address of the corresponding message handler. The second statement of the following example code maps WM_SETTEXT messages to a message handler called OnSetText:

BEGIN_MESSAGE_MAP(DerivedClass, BaseClass)
ON_MESSAGE (WM_SETTEXT, OnSetText)
END_MESSAGE_MAP()

For information about using ClassWizard, see Using Wizards to Handle Messages later in this chapter.

Adding a Message Handler

After declaring and creating a message map for a class, you can add a corresponding message handler to the class implementation file, xxx.cpp. Later in this chapter, you will learn to use ClassWizard or the WizardBar to add message handlers as needed for your classes. When you use ClassWizard to create a new class, it provides a message map for the class. Alternatively, you can create a message map manually using the source code editor; this is only recommended for experienced MFC developers.

The following example code shows a message map in the CMsgView class for the ON_WM_LBUTTONDBLCLK Windows message followed by its associated handler, OnLButtonDblClk. This code is located in the implementation file, Msg.cpp.

// MSG.CPP
BEGIN_MESSAGE_MAP(CMsgView, CView)
//{{AFX_MSG_MAP(CMsgView)
ON_WM_LBUTTONDBLCLK()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
void CMsgView::OnLButtonDblClk(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
MessageBox("You have just double-clicked the left mouse button");
CView::OnLButtonDblClk(nFlags, point);
}

If you manually add message handlers to your message map, you should add the prototypes for those handlers outside the message map area bounded by the //{{AFX_MSG_MAP and //}}AFX_MSG_MAP comments. This area is maintained by ClassWizard and any code placed in this area should be placed there by ClassWizard.
a Use ClassWizard to create message-map entries. If you add message-map entries manually, add them outside the //{AFX_MSG section. Otherwise, you may not be able to use ClassWizard later to edit the message map.

To see a demonstration showing how the message map and message handler for the OnRButtonDown function are implemented in an application's source files, click this icon.

How MFC Processes Messages

In previous sections, you've seen how the message map is built and where its various pieces are located in the application's source files. In this section, we'll look at how MFC routes messages through the framework and calls the appropriate message handlers to respond to the messages.

Both Windows and command messages are usually sent to the main frame window of the application. The MFC window procedure gets the messages and routes them differently, depending on the type of message received.

The best way to understand message handling in MFC is to look at the path messages usually follow as they are routed through the framework. Since Windows and command messages are handled differently, first we'll look at Windows messages and how they are handled, and then we'll look at command messages.

This section includes the following topics:

How Windows Messages Are Handled

Windows messages are routed through the framework in a three-step process. Briefly, this process is as follows:

1. The message is sent to a window — more specifically, to a window's window procedure.

2. Using the window object's message map, the MFC window procedure searches for a message handler that pertains to the message.

3. If the window procedure finds the correct message handler, it calls the handler and passes relevant message parameters, such as where the event took place.

To view an animation that shows how a Windows message is routed, click this icon.

The process of routing Windows messages is described in more detail in the following paragraphs.

Step 1: Windows Sends a Message to a Window

To start processing a Windows message, the Windows operating system sends a message to a window object. The message is usually handled by the window to which it is sent. The window object might be a main frame window, a child window, a standard control, a dialog box, or a view. MFC supports Windows messages through the CWnd class and CWnd-based classes, such as CView and CFrameWnd. You can add a handler to any of the CWnd classes to support Windows messages.

Step 2: Window Procedure Searches for Message Handler

After the window receives the message, the window procedure CWnd::WindowProc is called to find a message handler. The WindowProc function searches for the pertinent message map entry by iterating through the array of message map structures associated with the window class. The message map entry defines the messages a class will handle and correlates these messages to their message handlers.

Step 3: Window Procedure Calls Message Handler

Finally, if the window procedure finds a message handler for the targeted window class, it calls the handler; otherwise, the base class will be checked for a handler. If no handler exists there, then its base class will be examined for a handler. This process continues up through the class hierarchy until either a handler is found or until the CWnd class is reached. If no handler in the derived or base class is found, the default window procedure, CWnd::DefWindowProc, is called, which provides default behavior for all Windows events (moving, sizing, and so on).

How Command Messages Are Handled

Unlike Windows messages, command messages can be handled by a wide variety of objects — the application, documents, document templates, windows, and views. A command message originates from a menu item, command button, or accelerator key.

Any class derived from the CCmdTarget class is eligible to become a destination for a command message. Key classes that are derived from CCmdTarget are CWnd, CView, CWinApp, CDocument, CWnd, and CFrameWnd. Note that CWnd classes can receive both command messages and Windows messages.

When a command affects a particular object, it makes sense to have that object handle the command. For example, the Open command on the File menu is logically associated with the application, so the handler for the Open command is a member function of the application class.

The following paragraphs first examine how the framework routes command messages, and then briefly review how command messages are sent and received in an MFC application.

Routing Command Messages

There is more flexibility in routing a command message than there is with a Windows message. The framework routes command messages through a standard sequence of command-target objects, one of which is expected to have a handler for the command. Each command-target object checks its message map to see if it can handle the incoming message. The following illustration shows the routing sequence for command messages.



The various command-target objects check their own message maps at different times. Typically, a class routes the command to certain other objects to give them first chance at the command. If none of those objects handle the command, the original class checks its own message map. Then, if the class can't supply a handler, it may route the command message to yet more command targets. The following table shows the standard command routing sequence for the various classes.

When an object of this type receives a command It gives itself and other command-target objects a chance to handle the command in this order


MDI frame window

(CMDIFrameWnd) 1. Active CMDIChildWnd

2. This frame window

3. Application (CWinApp object)

Document frame window

(CFrameWnd,

CMDIChildWnd) 1. Active view

2. This frame window

3. Application (CWinApp object)



View 1. This view

2. Document attached to the view


Document 1. This document

2. Document template attached to the document



Dialog box 1. This dialog box

2. Window that owns the dialog box

3. Application (CWinApp object)


When entries in the second column of the table refer to other objects, go to the referred object in the first column to follow the routing of the command further. For example, when you read in the second column that the view forwards a command message to its document, see the "Document" entry in the first column and follow the command routing shown in the second column.

Sending Command Messages

Most messages result from user interaction with the program. Command messages are generated by mouse clicks in menu items or toolbar buttons, or by accelerator keystrokes.

The CWinApp::Run member function retrieves messages and dispatches them to the appropriate window. Most command messages are sent to the main frame window of the application. The WindowProc function gets the messages and routes them appropriately based on the type of message received.

Receiving Command Messages

The initial receiver of a message must be a window object. Command messages, usually originating in the application's main frame window, get routed by the framework through the command-target chain described previously under "Routing Command Messages."

Each object capable of receiving messages or commands has its own message map that pairs a command message with its handler function. When a command-target object receives a command message, it searches its message map for a match. If it finds a handler for the message, it calls the handler. Otherwise the message is handled by the default window procedure, DefWindowProc.


Using Wizards to Handle Messages


In this section, you will learn how to use wizards to make message-map connections between messages and message handlers. ClassWizard and WizardBar are the tools that are most often used to manage these message-handling tasks in your applications.

When you use AppWizard to create the starter files for your application, it automatically adds common message handlers. However, it is often necessary to add, modify, or delete message handlers in your application, a task for which you use ClassWizard.

This section includes the following topics:



  1. Adding Handlers with ClassWizard


ClassWizard is a tool designed specifically to connect messages to message handlers. Some possible scenarios where ClassWizard can be used to help create your application are listed below:

® You determine that one of your classes must handle a certain Windows message, so you run ClassWizard to make the connection.

® You create a menu or accelerator resource, then invoke ClassWizard to connect the command associated with that object to a handler.



As you develop MFC applications, you'll find that ClassWizard greatly simplifies your message-management tasks. ClassWizard writes the following information to your source files:

® A declaration of the handler as a member function of the class in the header file, xxx.h

® The appropriate message map entry for the connection in the implementation file, xxx.cpp

® An empty function template for you to fill in with the handler's code in the implementation file, xxx.cpp



ClassWizard does not make changes to code that you have written.

a Use ClassWizard to create and edit all message-map entries. If you add them manually, you may not be able to edit them with ClassWizard later. If you add them outside the bracketing comments as recommended, //{{AFX_MSG_MAP(classname) and //}}AFX_MSG_MAP, ClassWizard cannot edit them at all. By the same token, ClassWizard will not touch any entries you add outside the comments, so feel free to add messages outside the comments if you do not want them to be modified.

To see a demonstration showing how to add a message handler using ClassWizard, click this icon.

Deleting Handlers with ClassWizard

You can delete a message handler with ClassWizard, provided the handler you want to delete was created using ClassWizard.

When you delete a handler, ClassWizard deletes the message map entry and the prototype for the message handler. However, it does not remove the actual handler code — you must do this manually. This protects you against accidentally deleting important code that you may have written.

To see a demonstration showing how to delete a message handler using ClassWizard, click this icon.

a

For more information about using ClassWizard to manage message maps, see the Visual C++online documentation.



  1. Handling Messages with the WizardBar


Most of the message-handling tasks that you can accomplish with ClassWizard can also be performed with the WizardBar. To view the WizardBar, right-click in an unused portion of the Developer Studio menu bar. On the shortcut menu that appears, click WizardBar. The following illustration shows a sample display of the WizardBar.


Note The WizardBar remains inactive unless you have a project open.


To see a demonstration showing how to use the WizardBar to add or edit message handlers, click this icon.

a

WizardBar Combo Boxes

The WizardBar contains three combo boxes, as shown in the previous illustration. These combo boxes are drop-down lists of the classes, filters, and members related to the active project. The three combo boxes have a hierarchical relationship: the class that you select determines available filters, and the filter that you select determines what is displayed in the Members list.

The WizardBar Class list always displays classes in the active project. If your workspace contains projects written in more than one language, or a subproject, you can change the active project. The Filters list provides useful filters related to the current class. You can select the All Class Members filter, or you can select specific resource IDs. The Members list displays the result of the filter selected — for example, the specified class members, or the Windows message handlers defined for the selected resource ID.

WizardBar Action Control

The WizardBar Action control provides a direct way to perform common tasks such as jumping to a function or method definition. The Action control consists of two parts: the Action button on the left, and the drop-down Action menu on the right.

The task performed when you click the Action button, or the default action, is displayed in the ToolTip for the button. (It is also the item listed in bold on the Action menu.) The default action changes depending on the current selection in the WizardBar combo lists.

The Action button has three possible states:

® Tracking

® Active

® Disabled (no project open)



The Tracking state indicates that the WizardBar is currently tracking your context. The Active state indicates that the WizardBar itself is active—that is, there is a project open in the workspace. The Disabled state indicates that there is no open project.

The WizardBar Action menu appears when you click the arrow next to the Action button, or when you click the right mouse button when the focus is on a WizardBar combo control.


Self-Check Questions


1. The MFC message map is:

a A. A tool provided in Developer Studio to add message handlers to an application.

a B. A system that connects a message to the class member functions handling the message.

a C. An MFC class that defines how messages should be routed.

a D. A file generated by AppWizard used to maintain message-handling data structures.



2. Which one of the following is the responsibility of the function CWnd::WindowProc?

aa A. Retrieves messages from the application’s message queue.

a B. Contains a large switch statement that has the code to handle the specific messages.

a C. Simply calls CWnd::DefWindowProc to handle all messages.

a D. Searches the message map in the current CWnd-derived class for a handler for the current message.



3. In an MFC application, what is the message map macro name and handler name associated with the WM_RBUTTONDOWN Windows message?

a A. The macro is WM_RBUTTON_MESSAGE and the handler is OnRButtonMessage.

a B. The macro is OnRButtonDown and the handler is ON_WM_RBUTTONDOWN.

a C. The macro is ON_WM_RBUTTONDOWN and the handler is OnRButtonDown.

a D. Since general window messages are handled by the MFC framework, they never need message map entries.



4. Which tools inside Developer Studio are typically used to manage message map entries?

a A. ClassWizard only

a B. The WizardBar and ClassWizard

a C. ClassWizard and the Dialog editor

a D. AppWizard and ClassWizard



5. Which of the following MFC classes is most crucial to the message mapping system?

a A. CCmdTarget

a B. CDocument

a C. CObject

a D. CMenu



6. There is more flexibility in routing a command message than a Windows message since command messages can be handled by a wide variety of objects.

a A. True

aaaa B. False


Lab 6.1: Messages with MFC


In this lab, you will create a simple MDI application in which you examine Windows messages and add message handlers for mouse messages.

To see a demonstration that shows what you will accomplish during the lab, click this icon.

a

Estimated time to complete this lab: 45 minutes

To complete the exercises in this lab, you must have the required software. For detailed information about the labs and setup for the labs, see Labs in this course.

Objectives

After completing this lab, you will be able to:

® Create the framework for a simple MDI application.

® Add message handlers to an application using ClassWizard.

Prerequisites
There are no prerequisites for this lab.
Exercise
The following exercise provides practice with the concepts and techniques covered in this chapter.
® Exercise 1: Adding Message Handlers
In this exercise, you will build and run a simple MDI application using AppWizard. Then you will use ClassWizard to add message handlers and implement message boxes for the MDI application.

Exercise 1: Adding Message Handlers In this exercise, you will create a simple MDI application using AppWizard. You will add message handlers for several mouse-generated Windows messages to the MDI application you create. You will modify two of these message handlers, OnLButtonDown and OnRButtonDown, to display messages that provide information to the end user.


Create a simple MDI application

1. In Developer Studio, use AppWizard to create an MDI (exe) application named Msgr.
2. In Step 1 of AppWizard, click Multiple Documents.
3. In Steps 2 through 6, accept the defaults presented by AppWizard to create the starter files for the Msgr application.
After AppWizard creates the starter files for the application, you will be returned to the Developer Studio environment.

Add message handlers for mouse messages
1. On the View menu, click ClassWizard or press CTRL+W.
The MFC ClassWizard property page appears. The Message Maps tab is displayed by default.
2. In the combo boxes provided on the property page, click the Msgr project, the CMsgrView class, and the CMsgrView Object ID.
3. In the Messages combo box, scroll down and click the WM_LBUTTONDOWN message. Click Add Function.
4. Repeat Step 3 for the WM_RBUTTONDOWN message.
Display the position of the mouse clicks in a message box
1. In the Member functions combo box (at the bottom of the MFC ClassWizard property page), select the OnLButtonDown message handler, and then click Edit Code.

This places you in the empty function template for the OnLButtonDown message handler in the implementation file, MsgrView.cpp.

2. Note that the button-click message handlers are called with two parameters as shown by the following statement in the code:

void CMsgrView::OnLButtonDown(UINT nFlags, CPoint point)

® nFlags is a set of flags that indicate the possible virtual keys:
— MK_CONTROL — Set if the CTRL key is down.
— MK_LBUTTON — Set if the left mouse button 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.
® CPoint is a pointer to the location of the click relative to the origin, or upper-left corner of the window.
3. Display selected flags as strings that the user can understand. Create two strings, one for the state of MK_CONTROL and the other for MK_SHIFT, as follows:
CString CtrlPressed, ShiftPressed;
4. Mask the nFlags parameter to produce "Yes" or "No" to specify whether or not the SHIFT key is pressed and store the string in ShiftPressed, as follows:
ShiftPressed = (nFlags & MK_SHIFT)?"Yes":"No";
5. Repeat Step 4 for the CtrlPressed string, as follows:
CtrlPressed = (nFlags & MK_CONTROL)?"Yes":"No";
6. Declare a CString variable to hold a string copy of the point passed into the function and convert the point passed value into a string as follows:
CString MousePosition;
MousePosition.Format("[%d, %d]", point.x, point.y);
7. Since you are working in an MDI application, you can have multiple active views and documents. To see the view that was clicked, get the title of the document as follows:
CString DocName = GetDocument()->GetTitle();
8. Concatenate all of the strings, and add a few line breaks to make it easier to read, as follows:

CString Position =
"Left mouse button pressed in:\nDocument: \t" + DocName +
"\nMouse Position: \t" + MousePosition +
"\nControl: \t\t" + CtrlPressed +
"\nShift: \t\t" + ShiftPressed;
9. Display the resulting string in a message box, as follows:
AfxMessageBox(Position);
10. Save MsgrView.cpp. The completed code for the OnLButtonDown function should look like the following:

void CMsgrView::OnLButtonDown(UINT nFlags, CPoint point)
{
//unpack nFlags to show shift and control keys
CString CtrlPressed, ShiftPressed;

ShiftPressed = (nFlags & MK_SHIFT)?"Yes":"No";

CtrlPressed = (nFlags & MK_CONTROL)?"Yes":"No";
//convert position to string
CString MousePosition;
MousePosition.Format("[%d, %d]", point.x, point.y);
CString DocName = GetDocument()->GetTitle();
CString Position =
"Left mouse button pressed in:\nDocument: \t" + DocName +
"\nMouse Position: \t" + MousePosition +
"\nControl: \t\t" + CtrlPressed +
"\nShift: \t\t" + ShiftPressed;
AfxMessageBox(Position);
CView::OnLButtonDown(nFlags, point);
}
11. Implement the same functionality for the right button message handler, OnRButtonDown. To do this, you can copy the OnLButtonDown function and change the message from "Left mouse button..." to "Right mouse button..." If this were more than a simple exercise, you would put this interpretive code in a single function and call it from both handlers. The completed code for the OnRButtonDown function should look like the following:

void CMsgrView::OnRButtonDown(UINT nFlags, CPoint point)
{
//unpack nFlags to show shift and control keys
CString CtrlPressed, ShiftPressed;

ShiftPressed = (nFlags & MK_SHIFT)?"Yes":"No";

CtrlPressed = (nFlags & MK_CONTROL)?"Yes":"No";
//convert position to string
CString MousePosition;
MousePosition.Format("[%d, %d]", point.x, point.y);
CString DocName = GetDocument()->GetTitle();
CString Position =
"Right mouse button pressed in:\nDocument: \t" + DocName +
"\nMouse Position: \t" + MousePosition +
"\nControl: \t\t" + CtrlPressed +
"\nShift: \t\t" + ShiftPressed;
AfxMessageBox(Position);
CView::OnRButtonDown(nFlags, point);

}
12. Save the implementation file, MsgrView.cpp.
13. Build and run the application, Msgr.exe.

You can find the code for this completed exercise in \Labs\Ch06\Lab01\Ex01