Teach Yourself Visual C++® 5 in 24 Hours

Previous chapterNext chapterContents


- Hour 12 -
Using Pens and Brushes

In this hour you will look at pens and brushes. Specifically, you will learn

You also modify the DCTest sample program from Hour 11, "Device Contexts," to draw a variety of figures using pens and brushes.

What Is a Pen?

New Term: A pen is a Windows GDI object used to draw lines and figures.

Think of a Windows pen as being like an ink pen at your desk. A Windows pen object has three attributes:

Programs written for Windows use two types of pens:

You use a cosmetic pen when you must always draw lines with a fixed size. For example, rulers and grid lines are often drawn using cosmetic pens. You use geometric pen lines to reflect the scaling provided by the current mapping mode.


Just a Minute: A pen is perfect in situations in which you must draw a geometric shape or line. Although you can use a bitmap for complicated images, you easily can draw squares, rectangles, circles, and other basic shapes using GDI objects.

You can create and use pens with a variety of styles. Cosmetic pens are extremely quick and are mapped directly into device units. This makes them useful for drawing things like frames, borders, grid lines, and other screen objects that should not be affected by the current device context-mapping mode. Geometric pens require more CPU power but offer more styles. You can manipulate geometric pens using any of the available mapping modes.

Pens are also useful for drawing three-dimensional highlighting or other effects. It's not uncommon for pens and other GDI objects to be used to simulate controls in Windows; before Windows 95 was released, early versions of property pages used pens to draw simulated "tabs."

MFC Support for Pens

Like other GDI objects, you normally use a pen by creating an MFC object. Use the CPen class to create and manage both cosmetic and geometric pens. When creating a pen, you must specify at least three things:


Time Saver: The number of styles available for geometric pens is much larger than for cosmetic pens. However, cosmetic pens have much less overhead. You should use cosmetic pens whenever possible. The next few sections discuss the various options available for cosmetic and geometric pens.

Using Cosmetic Pens

Cosmetic pens are not affected by the current mapping mode's scaling factor because they are always drawn in device units. Therefore, they are useful where a line must overlay another view that may be scaled. These basic styles are available for cosmetic pens:

Figure 12.1 shows examples of each of the pen styles.

Figure 12.1.
Examples of the styles available for pens.

Using Geometric Pens

Geometric pens can use all the styles available for cosmetic pens except for the PS_ALTERNATE style, and they also have access to four additional attributes:

Using the CPen Class

The CPen class is simple because there really are only a few things that can be done to a pen object; most of the fun occurs when the pen object is selected into a device context. The CPen class provides three constructors: two simple constructors primarily for cosmetic pens and another extremely flexible constructor primarily for geometric pens.

The first constructor has no arguments:

CPen    aGreenPen;
aGreenPen.Create( PS_SOLID, 1, RGB(0,255,0);

If you use this constructor, use the Create member function to actually create the pen and make it ready for use.

The second constructor provided for CPen also is used for cosmetic pens:

CPen    penDottedAndRed( PS_DOT, 1, RGB(255,0,0) );

This version of the constructor accepts three parameters: the pen style, width, and color. In this case, the CPen instance is a dotted red pen.

The third constructor used for CPen objects enables any type of pen to be created. It also uses more parameters, as shown in Listing 12.1.

TYPE: Listing 12.1. Creating a brush using a LOGBRUSH structure.

LOGBRUSH    lbrGrnHatch;

lbrGrnHatch.lbStyle = BS_HATCHED;
lbrGrnHatch.lbColor = RGB(0,255,0);
lbrGrnHatch.lbHatch = HS_DIAGCROSS;

CPen    penGeometric( PS_DOT | PS_GEOMETRIC | PS_ENDCAPROUND,
                      50,
                      &lbrGrnHatch,
                      0,
                      NULL );

The constructor's first parameter is the pen's style, with the C++ OR operator, |, used to combine all styles that are applied to the pen. The second parameter for the constructor is the width; if the pen is cosmetic, it must be set to 1. The third parameter is a pointer to a LOGBRUSH structure. In Listing 12.1, lbrGrnHatch is defined as a diagonally cross-hatched green brush.

The last two parameters are rarely used; they define a user-supplied pattern for the pen. These two parameters are used only if the pen is created with the PS_USERSTYLE attribute. The fourth parameter is the number of elements in the style array, whereas the fifth parameter is an array of DWORD values, each used to define the length of a dash or space in the pen's pattern.

Using Stock Pens

The simplest pens to use are known as stock objects. Stock objects were discussed in Hour 11; they are GDI objects that belong to the operating system. Windows provides three stock pens:

Each of these pens is exactly one unit wide. If you need a wider pen, you must create one using the CPen class. These pens are used through a CDC object by calling the SelectStockObject function, passing the stock object as a parameter, as follows:

CPen* pOldPen = pDC->SelectStockObject( BLACK_PEN );

An Example that Draws with Pens

After a pen has been selected into a device context, several different drawing functions can be performed with the device context. The CDC class used to represent device contexts, as you learned in Hour 11, includes these drawing functions often used with pens:

As an example using pens, you can modify the DCTest program you created in Hour 11. The changes use three pens to draw three figures in the view window in a variety of styles and colors. Modifying the existing project also gives you a chance to see how different mapping modes affect the figures drawn using pens. Although some of the listings might look long, most of them require only a few changes in the source code that is already present.

Modifying the Mapping Mode Dialog Box

As the first step in the sample program, modify the Mapping Mode dialog box and the CMapModeDlg class to support extra options used when drawing with pens. The new version of the IDD_MAP_MODE dialog box is shown in Figure 12.2.

Figure 12.2.
The new version of the Mapping Mode dialog box.

Use the values from Table 12.1 for the new controls you add to the Mapping Mode dialog box, using ClassWizard to add member variables to the CMapModeDlg class. All existing controls should remain as they are.

Table 12.1. Values for new edit controls you add to the Mapping Mode dialog box.

Edit Control Resource ID Variable Name Type
Pen Width IDC_WIDTH m_nPenWidth int
Ellipse Width IDC_CXELLIPSE m_cxEllipse int
Ellipse Height IDC_CYELLIPSE m_cyEllipse int

Modifying the CDCTestView Class

You must modify the CDCTestView class slightly to add three new member variables that store the pen height and the ellipse variables you just added to the dialog box class. Add three new member variables to the attributes section of the CDCTestView class declaration, as shown in Listing 12.2.

TYPE: Listing 12.2. New member variables added to the CDCTestView class.

// Attributes
private:
    // Variables added for Hour 11
    CMap< int, int, CString, CString > m_map;
    int     m_nMapMode;
    // Variables added for Hour 12 - pens
    int     m_cxEllipse;
    int     m_cyEllipse;
    int     m_nPenWidth;

Do not modify the declarations for existing member variables.

Modifying the CDCTestView Member Functions

You must modify three CDCTestView member functions:

Each of these views is modified in the following sections. None of these member functions are new; all are used in the current DCTest project.

Add three new lines to the CDCTestView constructor to initialize the new variables added to the CDCTestView class. Listing 12.3 is the new version of the CDCTestView constructor. Most of the function should already be entered; you must add only the last three lines before the closing parenthesis.

TYPE: Listing 12.3. The new version of the CDCTestView constructor.

CDCTestView::CDCTestView()
{
    m_nMapMode = MM_TEXT;
    m_map.SetAt( MM_ANISOTROPIC, "MM_ANISOTROPIC" );
    m_map.SetAt( MM_HIENGLISH, "MM_HIENGLISH" );
    m_map.SetAt( MM_HIMETRIC, "MM_HIMETRIC" );
    m_map.SetAt( MM_ISOTROPIC, "MM_ISOTROPIC" );
    m_map.SetAt( MM_LOENGLISH, "MM_LOENGLISH" );
    m_map.SetAt( MM_LOMETRIC, "MM_LOMETRIC" );
    m_map.SetAt( MM_TEXT, "MM_TEXT" );
    m_map.SetAt( MM_TWIPS, "MM_TWIPS" );
    m_nPenWidth = 1;
    m_cxEllipse = 100;
    m_cyEllipse = 200;
}

Modify the CDCTestView::OnViewMapMode function to handle the changes in the Mapping Mode dialog box. Listing 12.4 provides the source code for the new version of OnViewMapMode. There are a total of six new source code lines, each marked with a comment. You should need to add only these six lines; the rest of the function should have been created already.

Listing 12.4. The new version of the CDCTestView::OnViewMapMode function.

void CDCTestView::OnViewMapMode()
{
    CMapModeDlg dlg;
    // The next three lines are added for Hour 12 - pens
    dlg.m_nPenWidth = m_nPenWidth;  // 1
    dlg.m_cxEllipse = m_cxEllipse;  // 2
    dlg.m_cyEllipse = m_cyEllipse;  // 3
    if( dlg.DoModal() == IDOK )
    {
        // The next three lines are added for Hour 12 - pens
        m_nPenWidth = dlg.m_nPenWidth;  // 4
        m_cxEllipse = dlg.m_cxEllipse;  // 5
        m_cyEllipse = dlg.m_cyEllipse;  // 6
        POSITION    pos;
        pos = m_map.GetStartPosition();
        while( pos != NULL )
        {
            CString szMapMode;
            int     nMapMode;
            m_map.GetNextAssoc( pos, nMapMode, szMapMode  );
            if( szMapMode == dlg.m_szCombo )
            {
                m_nMapMode = nMapMode;
                break;
            }
        }
        InvalidateRect( NULL );
    }
}

Last but not least, you must create a new version of the OnDraw function. Most of this version of OnDraw is new because you are now drawing with pens instead of listing device attributes. Use the source code provided in Listing 12.5 for the new version of CDCTestView::OnDraw.

Listing 12.5. The new version of CDCTestView::OnDraw.

void CDCTestView::OnDraw(CDC* pDC)
{
    pDC->SetMapMode( m_nMapMode );
    // Draw an ellipse based on the current map-mode and values
    // supplied by the user.
    CRect   rcClient;
    GetClientRect( rcClient );
    pDC->DPtoLP( rcClient );    // Covert device units to logical
    CPoint  ptCenter( rcClient.Width()/2, rcClient.Height()/2 );
    CRect   rcEllipse( ptCenter.x - ( m_cxEllipse/2 ),
                       ptCenter.y - ( m_cyEllipse/2 ),
                       ptCenter.x + ( m_cxEllipse/2 ),
                       ptCenter.y + ( m_cyEllipse/2 ) );
    CPen    penRed( PS_SOLID, m_nPenWidth, RGB(255,0,0) );
    CPen*   pOldPen = pDC->SelectObject( &penRed );
    pDC->Ellipse( rcEllipse );
    // Draw a black box around the ellipse, using one of the stock
    // pens.
    pDC->SelectStockObject( BLACK_PEN );
    pDC->MoveTo( rcEllipse.TopLeft() );
    pDC->LineTo( rcEllipse.right, rcEllipse.top );
    pDC->LineTo( rcEllipse.BottomRight() );
    pDC->LineTo( rcEllipse.left, rcEllipse.bottom );
    pDC->LineTo( rcEllipse.left, rcEllipse.top );
    // Draw an arc using the client area as a bounding rectangle.
    // Clip the arc so that only the lower-left half is displayed.
    CPen    penDottedAndGreen( PS_DOT, 1, RGB(0,255,0) );
    pDC->SelectObject( &penDottedAndGreen );
    pDC->Arc(rcClient, rcClient.TopLeft(),rcClient.BottomRight());
    pDC->SelectObject( &pOldPen );
}

Compile and run the DCTest project, and experiment by changing the values in the Mapping Mode dialog box. Figure 12.3 shows the DCTest project running with the mapping mode set to MM_TEXT.

Figure 12.3.
The DCTest example after adding pen GDI objects.

What Are Brushes?

A brush is a GDI object used to fill a control, window, or other area when programming for Windows. A brush is much like a pen; both are selected the same way, some of the attributes are similar, and you can use a series of stock objects without much overhead. However, you use a brush to fill an area rather than draw a line or a figure. A common use for brushes is to color windows, controls, or dialog boxes.

Every brush has several attributes:

By choosing different values for attributes given to a brush, you can achieve a wide variety of effects.

MFC Support for Brushes

In a program you write with Visual C++, you normally create and use CBrush objects the way you create and use CPen objects. Every brush has a set of attributes that define how it appears and behaves. Just as with pens, Windows has several stock brushes that are available by calling the SelectStockObject function.

Your Windows program can create four basic types of brushes:

Figure 12.4.
Examples of styles available for brushes.

You create each of these brush types using a different function call. For example, a solid brush is created using CreateSolidBrush, whereas a hatched brush is created using CreateHatchBrush. When using the CBrush class, it's also possible to call a specialized CBrush constructor to construct the CBrush object in the desired style. You will use the CBrush class later in this hour.

You can create solid and hatch brushes with a color attribute that specifies the color used when the brush fills an area. In the case of a hatched brush, the color specifies the color of the hatching lines.

Hatch Styles for Brushes

Six hatch styles are available for hatch brushes. You create a hatch style by passing one of the following styles as a parameter when you create the brush:

Figure 12.5 shows examples of these six hatching styles.

Figure 12.5.
Examples of brush hatching styles.

Using the CBrush Class

To use a brush in an MFC program, create a CBrush object and select it into a device context. You can create the brush using single-step construction, like this:

CBrush    brBlack( RGB(0,0,0) );

Alternatively, use two-step construction, where the brush object is constructed and then explicitly created, like this:

CBrush    brBlack();
brBlack.CreateSolidBrush( RGB(0,0,0) );

The advantage of using two-step construction is that the function used to create a brush returns FALSE if the function fails.

Unlike pens, which use style bits to determine the type of pen to be created, separate functions are used for different brush types. In two-step construction, you can use three functions to create a brush after you construct the CBrush object:

Four different constructors are provided for CBrush. In addition to the default constructor, you can use three constructors to create a specific type of brush in one step. The second constructor is used to create a solid brush and takes one COLORREF parameter, indicating the color used for the brush.

CBrush    brGreen( RGB(0,0,255) );

Using this brush is equivalent to using the default constructor and then calling CreateSolidBrush.

The third form of the CBrush constructor is used to create a hatched brush, and takes the hatching style and hatch color as parameters:

CBrush   brGray( HS_CROSS, RGB(192,192,192) );

This constructor is equivalent to using the default constructor and then calling CreateHatchBrush.

Use the fourth and final constructor for CBrush to create brushes that have bitmap patterns. You will learn more about bitmaps in Hour 15, "Using Bitmaps"; for now, just remember that a bitmap can be used as a pattern for a brush. The constructor takes a pointer to a CBitmap object as a parameter:

CBrush    brArrow( &bmpArrow );

The CBitmap object can be up to 8x8 pixels. If the bitmap is larger, only the upper-left eight pixel squares are used for the brush pattern.

Logical Brushes

Logical brushes are defined using the LOGBRUSH structure. A logical brush often is used when specifying how a brush should be constructed. For example, earlier in this hour you used a LOGBRUSH structure to specify the characteristics of a geometric pen. Think of a LOGBRUSH as a recipe for a brush that might be created: It's not a brush yet, but it might help build a brush in the future.

The LOGBRUSH structure has three data members:

Each of the three LOGBRUSH data members corresponds to one of the style attributes available for brushes, discussed earlier in this hour. To create a logical brush, just assign values to the three data members, as with any structure. Listing 12.6 uses a logical brush to create a red hatched brush.

TYPE: Listing 12.6. Filling a LOGBRUSH structure.

LOGBRUSH    lbrRed;
lbrRed.lbrStyle = BS_HATCH;
lbrRed.lbrColor = RGB(255,0,0);
lbrRed.lbrHatch = HS_CROSS;

CBrush      theRedBrush;
theRedBrush.CreateBrushIndirect( &lbrRed );

Using Stock Brushes

Just like stock pens discussed earlier in this hour, Windows maintains a set of stock brushes. Windows provides seven stock brushes:

As with other stock objects, these brushes are used through a CDC object by calling the SelectStockObject function, passing the stock object as a parameter, as follows:

CPen* pOldBrush = pDC->SelectStockObject( BLACK_BRUSH );

Using the Common Color Dialog Box

The Windows operating system includes a series of dialog boxes as part of the operating system. These dialog boxes are guaranteed to be present, and using them requires just a few lines of code. Use these dialog boxes for common operations where it's beneficial for all Windows programs to have a similar look and feel. The common dialog boxes shipped with Windows can help you

To use the Common Color dialog box, just create a CColorDialog object and call DoModal, just as with any other dialog box:

CColorDialog    dlgColor;
if( dlgColor.DoModal() )
{ //....

If IDOK is returned from the dialog box, the CColorDialog::GetColor function gets the selected color value. The example in the next section uses the Common Color dialog box to choose a brush color. You will use other common dialog boxes in later hours. (For example, the font-selection dialog box is used in Hour 13, "Fonts.")

Changing the Mapping Mode Dialog Box and CMapModeDlg Class

As an example of how to use brushes, continue to modify the DCTest project that you worked with earlier this hour. The new version of the project displays a colored ellipse on a gray view background. Both the ellipse and background color are filled using CBrush objects. You can change the color of the ellipse using the Common Color dialog box; as a bonus, the Mapping Mode dialog box color changes to match the ellipse.

Modify the Mapping Mode dialog box to allow the user to choose a color for the dialog box and a brush used for the view. The CMapModeDlg class needs two new variables: a COLORREF for the currently selected color, and a CBrush object that has been created using the current color. Listing 12.7 contains the changes to the CMapModeDlg class declaration. Add the new code in the Dialog Data section, just after the AFX_DATA comments.

TYPE: Listing 12.7. Changes to the CMapModeDlg class declaration.

// Dialog Data
    //{{AFX_DATA(CMapModeDlg)
    enum { IDD = IDD_MAP_MODE };
    CString   m_szCombo;
    int       m_cyEllipse;
    int       m_cxEllipse;
    int       m_nPenWidth;
    //}}AFX_DATA
    // Variable added in Hour 12
public:
    COLORREF m_clrChoice;
private:
    CBrush   m_brControl;

You must change the Mapping Mode dialog box slightly for this example. Remove the pen-width edit control and add a pushbutton control, as shown in Figure 12.6. Use ClassWizard to remove the m_nPenWidth member variable from the CMapModeDlg class.

Figure 12.6.
The new version of the Mapping Mode dialog box.

Use the values from Table 12.2 for the new button control.

Table 12.2. Values for the new Color button.

Resource ID Caption Function
IDC_COLOR &Color... CMapModeDlg::OnColor
Using ClassWizard, add a new message-handling function to the CMapModeDlg class named CMapModeDlg::OnColor. The source code for OnColor is provided in Listing 12.8.

TYPE: Listing 12.8. The CMapModeDlg::OnColor member function.

void CMapModeDlg::OnColor()
{
    CColorDialog    dlgColor;
    if( dlgColor.DoModal() == IDOK )
    {
        m_clrChoice = dlgColor.GetColor();
        // If the brush already exists, delete the current
        // GDI object before calling CreateSolidBrush
        if( m_brControl.Detach() )
            m_brControl.DeleteObject();
        m_brControl.CreateSolidBrush( m_clrChoice );
        InvalidateRect( NULL );
    }
}

The OnColor function creates a Common Color dialog box and displays it using DoModal. If the user selects a new color, the color is collected and the brush is updated. If the brush has previously been created, the Detach and DeleteObject functions must be called to destroy the current brush before creating a new brush.

Handling the WM_CTLCOLOR Message

Before displaying any control or dialog box, Windows asks for the control's color by sending a WM_CTLCOLOR message to the owner of the control. To specify a color to be used for the control or dialog box, return a solid brush containing the color in response to this message, as shown in Listing 12.9. The m_brControl brush is a class member variable because it must survive for the life of the control.

TYPE: Listing 12.9. Changing the color of a dialog box by handling WM_CTLCOLOR.

HBRUSH CMapModeDlg::OnCtlColor(CDC* pDC,CWnd* pWnd,UINT nCtlColor)
{
    if( nCtlColor == CTLCOLOR_DLG || nCtlColor == CTLCOLOR_STATIC )
    {
        pDC->SetBkMode( TRANSPARENT );
        return (HBRUSH)m_brControl.GetSafeHandle();
    }
    else
        return CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
}


Time Saver: The easiest way to deal with colored dialog boxes is shown in Listing 12.9, where the text-drawing mode is set to transparent by calling SetBkMode. If this line is commented out, you will see that the static text has colored areas around each color. By setting the drawing mode to transparent, the text is drawn without including the text background color, allowing the dialog box color to show through.

The GetSafeHandle function is used with all GDI objects to return a handle to the underlying object. A CBrush object returns an HBRUSH handle; a CPen object returns an HPEN, and so on.

The WM_CTLCOLOR message is sent for every control type found in the dialog box. It's possible to set different colors for each control type by testing for the values found in Table 12.3. If a brush is not returned, determine the return value by calling CDialog::OnCtlColor.

Table 12.3. Control-type message return value.

Control Message Value Control Type
CTLCOLOR_BTN Button control
CTLCOLOR_DLG Dialog box
CTLCOLOR_EDIT Edit control
CTLCOLOR_LISTBOX List box control
CTLCOLOR_MSGBOX Message box
CTLCOLOR_SCROLLBAR Scrollbar control
CTLCOLOR_STATIC Static control

Updating the CDCTestView Class

The CDCTestView class must store the color and brush selected by the user to color the ellipse and Mapping Mode dialog box. One new variable is added to the attributes section of the CDCTestView class, as shown in Listing 12.10: The m_clrChoice variable stores the currently selected color for the ellipse.

TYPE: Listing 12.10. Changes to the CDCTestView class declaration.

// Attributes
private:
    // Variables added for Hour 11
    CMap< int, int, CString, CString > m_map;
    int     m_nMapMode;
    // Variables added for Hour 12 - pens
    int     m_cxEllipse;
    int     m_cyEllipse;
    // Variable added for Hour 12 - brushes
    COLORREF m_clrChoice;

Changes to CDCTestView Member Functions

To update the DCTest project to use brushes instead of pens, you must make four basic changes to the CDCTestView member functions:

Edit the constructor for CDCTestView so it looks like the source code provided in Listing 12.11. The m_nPenWidth variable has been removed, and one line has been added to initialize the m_clrChoice variable.

TYPE: Listing 12.11. The CDCTestView constructor.

CDCTestView::CDCTestView()
{
    m_nMapMode = MM_TEXT;
    m_map.SetAt( MM_ANISOTROPIC, "MM_ANISOTROPIC" );
    m_map.SetAt( MM_HIENGLISH, "MM_HIENGLISH" );
    m_map.SetAt( MM_HIMETRIC, "MM_HIMETRIC" );
    m_map.SetAt( MM_ISOTROPIC, "MM_ISOTROPIC" );
    m_map.SetAt( MM_LOENGLISH, "MM_LOENGLISH" );
    m_map.SetAt( MM_LOMETRIC, "MM_LOMETRIC" );
    m_map.SetAt( MM_TEXT, "MM_TEXT" );
    m_map.SetAt( MM_TWIPS, "MM_TWIPS" );
    // The next two lines are added for Hour 12 - pens
    m_cxEllipse = 100;
    m_cyEllipse = 200;
    // The next line is added for Hour 12 - brushes
    m_clrChoice = RGB(0,0,0);
}

Modify the CDCTestView::OnViewMapMode function as shown in Listing 12.12. The code in this listing removes all references to the m_nPenWidth variable, and the function now tracks the color selected by the user. A total of two lines have been removed and one line added to the existing function.

TYPE: Listing 12.12. The OnViewMapMode function.

void CDCTestView::OnViewMapMode()
{
    CMapModeDlg dlg;
    // The next two lines are added for Hour 12 - pens
    dlg.m_cxEllipse = m_cxEllipse;
    dlg.m_cyEllipse = m_cyEllipse;
    // The next line is added for Hour 12 - brushes
    dlg.m_clrChoice = m_clrChoice;
    if( dlg.DoModal() == IDOK )
    {
        // The next two lines are added for Hour 12 - pens
        m_cxEllipse = dlg.m_cxEllipse;
        m_cyEllipse = dlg.m_cyEllipse;
        // The next line is added for Hour 12 - brushes
        m_clrChoice = dlg.m_clrChoice;
        POSITION    pos;
        pos = m_map.GetStartPosition();
        while( pos != NULL )
        {
            CString szMapMode;
            int     nMapMode;
            m_map.GetNextAssoc( pos, nMapMode, szMapMode );
            if( szMapMode == dlg.m_szCombo )
            {
                m_nMapMode = nMapMode;
                break;
            }
        }
        InvalidateRect( NULL );
    }
}

Modify the CDCTestView::OnDraw function as shown in Listing 12.13. The new version of OnDraw uses a CBrush object to fill the view window with a red brush. Another CBrush object is used to draw an ellipse in the center of the view using a user-defined color to fill the figure.

TYPE: Listing 12.13. The OnDraw member function modified to use brushes.

void CDCTestView::OnDraw(CDC* pDC)
{
    CRect rcClient;
    GetClientRect( rcClient );
    pDC->DPtoLP( rcClient );
    CBrush  brBackground( RGB( 255, 255, 100 ) );
    pDC->FillRect( rcClient, &brBackground );
    CPoint  ptCenter( rcClient.Width()/2, rcClient.Height()/2 );
    CRect   rcEllipse( ptCenter.x - ( m_cxEllipse/2 ),
                       ptCenter.y - ( m_cyEllipse/2 ),
                       ptCenter.x + ( m_cxEllipse/2 ),
                       ptCenter.y + ( m_cyEllipse/2 ) );
    CBrush  brEllipse( m_clrChoice );
    CBrush* pOldBrush = pDC->SelectObject( &brEllipse );
    pDC->Ellipse( rcEllipse );
    pDC->SelectObject( &pOldBrush );
}

Compile and run the DCTest project, and experiment by changing the values in the Mapping Mode dialog box. Also experiment with different colors for the dialog box and ellipse by clicking the Color button. Figure 12.7 shows an example of the DCTest project running.

Figure 12.7.
The DCTest example after adding brush GDI objects.

Summary

In this hour you learned about two GDI objects--pens and brushes--and how they are used to draw figures and fill shapes in Windows programs. You also learned about the MFC classes CPen and CBrush that are used to manage pen and brush objects. Finally, you modified the DCTest program to use the MFC CPen and CBrush classes.

Q&A

Q Can a pen and a brush be used at the same time?

A Yes, in fact, you are always drawing with both a pen and a brush. Earlier in this hour the ellipse and rectangle were drawn with specific pens; the shapes weren't filled in because the default brush for a device context is the hollow, or null brush. Because this brush has no effect, it seems as though no brush is being used.

Q Why aren't pushbuttons affected by handling WM_CTLCOLORBTN?

A The WM_CTLCOLORBTN message will affect radio buttons and check boxes, but not pushbuttons. To change the color of a pushbutton you must make it an owner-drawn pushbutton and take over responsibility for drawing every aspect of the button.

Workshop

The Workshop is designed to help you anticipate possible questions, review what you've learned, and begin thinking ahead to putting your knowledge into practice. The answers to the quiz are in Appendix B, "Quiz Answers."

Quiz

1. What are the three attributes of a pen?

2. What are the two types of pens?

3. What MFC class is used to manage pens?

4. What stock pens are maintained by Windows?

5. What styles are available for cosmetic pens?

6. What styles are available for geometric pens?

7. What are the four types of brushes?

8. What stock brushes are maintained by Windows?

9. What MFC class is used to manage brushes?

10. What function is used to draw a circle?

Exercises

1. Modify the DCTest example, and change the edit control color to red.

2. Modify the DCTest example so that the ellipse is drawn with a red outline.


Previous chapterNext chapterContents


Macmillan Computer Publishing USA

© Copyright, Macmillan Computer Publishing. All rights reserved.