0% found this document useful (0 votes)
694 views1,377 pages

Comprehensive Guide to MFC Programming

Uploaded by

andres lopez
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
694 views1,377 pages

Comprehensive Guide to MFC Programming

Uploaded by

andres lopez
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 1377

Introduction

Foreword
About the Author

A Brief History of MFC


The MFC Class Hierarchy
CObject
CCmdTarget
CWinThread
CWinApp
CWnd
CFrameWnd
CView
CDocument
Summary
Creating an Application
Starting and Using MFC AppWizard
The AppWizard-Generated Code
Modifying the Application
Adding Dialog Controls
Adding Initialization
Using Dialog Controls
ToolTips
Enabling ToolTips
Displaying Text
Dialog Data Exchange
Standard DDX
UpdateData
Using Standard Dialog Boxes
File Open/Save
Color Selector
Font Selector
Print Dialog
Summary

Initializing and Using the Common Controls


Notifications for Windows Common Controls

The Notification Message Structure


Overview of the Notification Process
A Better Notification Handling Scheme
Specifying Notification Ranges with ON_NOTIFY_RANGE

Hot Key Controls: Class CHotKeyCtrl

CHotKeyCtrl Class Methods


Creating and Initializing a CHotKeyCtrl Object
Using a Hot Key Control
Spin Controls: Class CSpinButtonCtrl

Spin Control Styles


CSpinButtonCtrl Messages
CSpinButtonCtrl Class Methods
Creating and Initializing a Spin Control
Sample Program: SPIN1

Slider Controls: Class CSliderCtrl

Slider Control Styles


CSliderCtrl Messages
CSliderCtrl Class Methods
Creating and Initializing a Slider Control
Sample Program: Slider Controls (SLIDER1)
Sample Program: SLIDER1

Progress Bar Controls: Class CProgressCtrl

CProgressCtrl Class Methods


Creating and Initializing a CProgressCtrl Object
Using a Progress Control

Image Lists: Class CImageList

CImageList Class Methods


Creating and Initializing a CImageList Control

List View Controls: Class CListCtrl

List View Control Styles


Image Lists and the List View Control

List View Items and Subitems

List View Notification Messages


Creating and Initializing a CListCtrl Object
Using the List View Control

Tree View Controls: Class CTreeCtrl

Tree View Control Styles


Tree View Notification Messages
CTreeCtrl Class Methods
Creating and Initializing a Tree View Control
Using a CTreeCtrl Object
Sample Program: TREELIST.EXE

Tab Controls: Class CTabCtrl

Tab Control Styles


Tab Control Notification Messages
CTabCtrl Class Methods
The Tab Item Structure (TC_ITEM)
Creating and Initializing a Tab Control
Using a Tab Control

Animate Controls: Class CAnimateCtrl

Animate Control Styles


Animate Control Notification Messages
CAnimateCtrl Class Methods
Creating and Initializing an Animate Control
Using an Animate Control

Rich Edit Controls: Class CRichEditCtrl

Rich Edit Control Window Styles


The Character Format Structure (CHARFORMAT)
The Paragraph Format Structure (PARAFORMAT)
CRichEditCtrl Class Methods
CRichEditCtrl Line-Related Methods
CRichEditCtrl Text-Selection Methods
CRichEditCtrl Formatting Methods
CRichEditCtrl Editing Methods
CRichEditCtrl Clipboard Methods
CRichEditCtrl General-Purpose Methods
Creating and Initializing a Rich Edit Control
Using a Rich Edit Control

Summary
Device Contexts
The Graphics Device Interface

MFC Wrapping

MFC Device Context Classes

The Base Class: CDC


Painting with Class CPaintDC
Managing Client Areas with Class CClientDC
Managing Frame Windows with Class CWindowDC
Windows Graphic Objects

Pens: Class CPen


Brushes: Class CBrush
Fonts: Class CFont
Bitmaps: Class CBitmap
Palettes: Class CPalette
Regions: Class CRgn

GDI Coordinate Systems

Logical Mapping Modes

Vector Graphics

Drawing Modes
Lines and Polylines
Rectangles
Regions
Polygons
Ellipses
Fonts and Text
Font Characteristics
The TEXTMETRIC Structure
The LOGFONT Structure
Font Creation
Drawing Text
Sample Program: Vector Graphics and Text Methods (VECTEXT1.EXE)
Raster Graphics
Named Raster Operations (ROPs)
Bitmaps
Device-Dependent Bitmaps
Device-Independent Bitmaps (DIBs)
The CBitmap Class
Transferring and Contorting Bitmaps
Bitmap Resources
Tacking Resources onto an Executable File
Getting Image Resources out of an Executable File
Sample Program: Exploring Bitmap Resources (BITMAP1)
Summary

A Validating Edit Control


The Clock Static Control

Control Metrics
Painting the Face
Locating the Hands
Painting the Hands
Setting the Time
Pitfalls of Subclassing Standard Controls

The Hyperlink Control

Implementation Strategy
Font Processing
Painting the Window
Controlling the Cursor
Mouse Input
Keyboard Input
Launching the Link

Advanced Custom Control Topics

Subclassing Limitations
Notifications
Using the Resource Editor with Custom Classes

Summary
The MFC Application Object

CWinApp and Application Lifetime


The CWinApp Data Members
The CWinApp Member Functions
Functionality in InitInstance
OLE Container Support
3D Look for Windows NT 3.5x
Registry Usage
Most Recently Used Files List
SDI and MDI Document/View
Main Frame Window Creation
Automation Support
Rich Edit Control Support
Command-Line Handling

Message Routing, Message Maps, and Message Categories

Message Routing
PreTranslateMessage
Message Maps

Idle Processing

OnIdle
Idle Processing for Dialogs
The Splash Screen Component
Summary

Documents, Frames, and Views


Document Templates
Creating New Documents
Opening New Files
Single Versus Multiple Document Templates
Views
The CView Class
The CScrollView Class
The CFormView Class
The Database View Classes
The Control Views
Changing Views in an SDI
Using the MDI
Summary

Responding to the User

Keyboard Messaging
Handling Keyboard Messages
Mouse Messaging
Handling Mouse Messages

User Interfaces and AppWizard


Extending Menus

Obtaining Menus and Pop-up Menus


Adding Menu Items
Using Floating Pop-up Menus

Putting Control Bars to Use

Using Toolbars and Rebars


Using Status Bars
Showing and Hiding Control Bars
Supporting ToolTips

Updating the User Interface


Property Sheets and Wizards

Creating a Simple Wizard

Splitting a View
Summary
Printing Fundamentals
Printing with MFC
Printing in the View
The PrintInfo Object
Printing Menu Commands

Printing and GDI Mapping Modes


WYSIWYG Printing

Application Resources
The Application Class
The View Class

Pagination

Printing with a Known Page Count


Printing with an Unknown Page Count
Printing Page Numbers

Stopping and Aborting Print Jobs

Halting a Print Job in OnPrepareDC()


Halting a Print Job in OnPrint()

Summary

A Little History
Interfaces, Objects, and Methods
Some Terminology
A Real-World View
The IUnknown Interface
Servers, Clients, and Classes
The COM Runtime Environment
Defining the Class Factory
How Are COM Objects Reused?
Marshaling and Threading
Marshaling
Threading
COM, OLE, and Automation
IDispatch
Automation Servers, Objects, and Controllers
Supporting Both IDispatch and IUnknown
Persisting COM Data
Structured Storage
Identifying COM Data (Monikers)
Transferring Data
Uniform Data Transfer
Connectable Objects
DCOM
Object Creation
Invoking Methods
Security Issues
Some Important Information
BSTR
SAFEARRAY
HRESULT
VARIANT
Further Reading
Summary

Understanding the Afx Global Functions

Application Lifetime Control


Client Control Management
Connection Point Management
Control Registration
Exceptions
Initialization
Licensing
Type Information

Reviewing the OLE Macros

Class Factories
Client/Container Common Commands
Control Property Persistence
Dialog Data Exchange
Dispatch Maps
Event Maps
Property Page Data Mapping
Property Pages
Type Library Access

MFC and the OLE Class Categories

Active Document
Automation
Common Dialogs for OLE
Container
Control
Drag and Drop (Universal Data Transfer)
Document Servers
Support

Summary
Document Servers
Server Types
Full Server
Active Document
Container/Server
Mini-Server

Document Server Design

OLE Documents
Active Documents

Building an Active Document Server

Persistence
Rendering the View

Automation Servers
IDispatch

GetIDsOfNames
GetTypeInfo
GetTypeInfoCount
Invoke

IDispatch-Derived Interface in ODL


Calling Methods Through IDispatch

GetIDsOfNames
Type Information Methods
Invoke

Dispinterfaces Differ from Interfaces


Dual Interfaces
The Variant
An Automation Server Using MFC

Server Type
Declaring and Defining Additional Dispinterfaces
Adding Methods and Properties

Summary
IDispatch and Its Place in Automation
Interface Definition for Automation Servers

IDL and ATL


ODL and MFC
Dual Interfaces
MFC and Automation

Controller
Connecting to a Server
Server Review

Building a Controller

Using COleDispatchDriver
Remote Automation

Summary
Development Strategy

MFC
ATL
MFC and ATL

Control Development
Two Faces of a Control

Runtime
Design Time

Subclassing a Control

MFC
ATL

Component Categories

ICatRegister
ICatInformation

Methods, Properties, and Events

Properties
Methods
Events

Property Pages

Property Pages in ATL


Property Pages in MFC

Component Registration
Registration Scripts
Registration and Controls

COM Object Subkeys


Building an MFC Control
Interface Definition
A Quick ATL Port
Summary
Active Document Container

Storage
Site Objects
In-Place Activation
Document Extensions
Building the Simplest Active Document Container

OLE Controls

Adding Containment to an Existing Project


ActiveX Container
Control Containment and Events

Summary
What Is ATL and Why Is It Important for Programming in MFC?
Helpful ATL COM Support for MFC Applications

COM Pointers the Smart Way


Other ATL COM Support

Advanced ATL Support for MFC Applications

Begin with Your MFC Application


Add the Required ATL Support

Summary
Scripting Basics

Scripting Interfaces
Dual Interfaces

Object Models
Implementing a Scripted Application

Object Implementation

Summary
Relational Database Concepts
Tables
Columns
Records
Cursors
Transactions
Storing and Retrieving Data
SELECT
INSERT
DELETE
UPDATE
Database Communication Mechanisms
ODBC
DAO
Which Methodology Should I Use?
ODBC/MFC
CDatabase
CRecordset
DAO
CDaoWorkspace
CDaoDatabase
CDaoRecordset
CDaoTableDef
CDaoQueryDef
Summary

The COM Approach

OLE DB
ADO
Which One Should I Use?

OLE DB Consumers Using the ATL Wrapper Classes

CDataSource
CSession
Accessors
Rowsets
Accessing Datasource Data

ADOConnection
ADORecordset
ADOCommand
ADOField
ADOProperty
ADOParameter
ADOError
Summary
Strings and String Classes
Inside the CString Class
Practical CString Usage
CString Summary
Collections
Inside Collection Classes
Templated Collections
The UNL_MultiEd Application
Overview
An STL Approach
Summary

The CFile Class

Processing Files with CFile

Inside the CFile Class

The CStdioFile Class


The CMemFile Class
The CSharedFile Class

The CFileDialog Class


The User-Defined CFileDialog Class
Practical Usage of CFile and CFileDialog

Opening a File
Reading Data from a File

A Classical Approach

What Are Streams?

Summary
Types of Exceptions
Structured Exception Handlers
Nesting of Structured Exception Handlers
Raising Structured Exceptions
Cleaning Up After an Exception
MFC Exceptions

MFC CException-Derived Classes


CMemoryException
CNotSupportedException
CArchiveException
CFileException
CResourceException
COleException
CDbException
COleDispatchException
CUserException
CDaoException
CInternetException

Deriving Your Own MFC-Compliant Exception Objects


Deleting Exceptions
Using MFC Exception Macros
Mixing Exceptions
Summary

DHTML, MSIE, and the Internet


Using the Internet Explorer Web ActiveX Control
Internet Explorer ActiveX Control Basics
CHtmlView
CHtmlView and the Document/View Relationship
CHtmlView and COM
Using the Internet Explorer ActiveX Control in a Dialog Box
Using DHTML
The DHTML Object Model
The Document Object
Other DHTML Objects
Collection Objects
DHTML COM Interfaces
Obtaining the Document DHTML Interface
Obtaining the DHTML Collections Interfaces
Using the DHTML Interface
The DHTML Element Interface Methods
Document Navigation
Summary

How Do Network Communications Work?

Sockets, Ports, and Addresses

Winsock and MFC

Initializing the Winsock Environment


Creating a Socket
Making a Connection
Sending and Receiving Messages
Closing the Connection
Socket Events
Controlling Event Triggering
Detecting Errors
Getting Socket Information
Sockets and I/O Serialization
Building a Networked Application

Creating the Application Shell


Window Layout and Startup Functionality
Inheriting from the CAsyncSocket Class
Connecting the Application
Sending and Receiving
Ending the Connection

Summary
Web Application Protocols and WinInet

Hypertext Transfer Protocol


File Transfer Protocol
Gopher Protocol
WinInet API and MFC

Internet Session Basics

CInternetSession
CInternetFile
CInternetException
CInternetConnection
Building a Simple WinInet Application

Application-Level Functionality

HTTP Classes
FTP Classes
Gopher Classes
Building a Simple FTP Client

Summary
The Foundation
From There to Here
From GET to POST
Adding Some Form Elements

Change the Form


Change the Parse Map
Declare and Use the Handler Function
Add a Radio Button

Other Form Input Elements

Change the Form


Change the Parse Map
Change the Handler Function

Summary
The Messaging Application Programming Interface

Client Applications

Two Higher-Level Alternatives: Simple MAPI and CMC

Simple MAPI
Common Messaging Calls
The MapiMessage Structure
The MapiFileDesc Structure

Limited MAPI Functionality in MFC


The CDocument Connection

Doing MAPI the MFC Way


The CDocument::OnFileSendMail() Method

Sample Program: MAPI1.EXE


Summary
Overview

Assisted Telephony
Basic Telephony Service
Supplemental Telephony Service
Extended Telephony Service

History of TAPI

TAPI 1.3
TAPI 1.4
TAPI 1.5
TAPI 2
TAPI 2.1
TAPI 2.2
TAPI 3

Using Assisted Telephony


Using Basic Telephony

Configuring TAPI
Connecting with TAPI
Transmitting Data with TAPI
Disconnection with TAPI
Terminating a TAPI Session

Summary

What Is OpenGL?
Graphics Boot Camp
Core Terms and Key Concepts
The Basic Mathematics of Graphics Programming
OpenGL Boot Camp
OpenGL Datatypes
OpenGL Function Naming Conventions
OpenGL Render Function Scoping
The OpenGL Matrix Stack
The OpenGL Rendering Context
Minimal OpenGL Program and a Custom View
Creating Your Basic MFC Application
Adding the Custom OpenGL Base Class
Integrate the Custom View Class into Your Application
Build Your 3D Scene
2D and 3D Models
Basic Model Development
Model Transformations
Display Lists
Light
The Basics of Light in OpenGL
Types of Light
Object Material Properties
Texture Mapping
OpenGL Images and Windows Device-Independent Bitmaps
Applying Texture
Special Effects
Blending
Fog
Summary
What Is DirectX?
DirectX in a Nutshell
DirectX Components
DirectX Basics
Differences in COM Programming Style and DirectX
DirectX Component Startup Sequence
DirectDraw Basics
Initializing DirectDraw
DirectDraw and Palettes
DirectDraw Surfaces
DirectDraw Page Flipping
DirectDraw from a Window
Using DirectDraw in Practice
Surfaces and Bitmaps
Windowed Rendering
Error Handling
DirectDraw Shutdown
Summary

Multimedia Fundamentals
The DirectX Media Player Control

Media Formats Supported by the Media Player Control


Inside the Media Player Control
The CMediaPlayer Class

Using the Media Player Control


Playing Sound

Working with Waves


Revisiting DirectSound
Using DirectSound to Play Sound Effects

Summary

Registry Usage
Configuration
Services Information
State
User Preferences
The Registry Structure
Programmatic Control of the Registry
The Registry API
The Registry Sample Application
The Registry Key Editor Dialog
Creating a New Key
Deleting a Key
Deleting a Name/Value Pair
A Word About Wrapping the Registry Functions
A Word About Installation
Summary

Advantages of DLLs

Code Elimination
Modularity and Packaging
Extensibility

Inside an AFX DLL


Exporting Classes, Functions, and Data

The Big Deal About Exports


Mangled Names
Exporting Classes
What Goes Around, Comes Around
Exporting Explicit Functions
Exporting Data
Exporting MFC Data
Exporting the Destructor
Export Toolkit include Files
What to Export

Other DLL Issues

AfxLoadLibrary and AfxFreeLibrary


Designing for Extensibility and Reuse
Resource Location
Multiple Module Definition Files
Load Addresses and the Linker

Summary
Property Sheets and Property Pages

The CPropertySheet and CPropertyPage Classes


The Wizard Walk and the Property Sheet Connection

Creating a Wizard

Setting the Wizard Mode


Enabling the Wizard Buttons
Displaying the Wizard
Wizard Notification Messages

Sample Program: Off to See the Wizard (WIZARD1.EXE)

Creating Wizard Page Dialog Template Resources


Create a Dialog Class for Each Dialog Resource
Exploring the Welcome Page: Class CIntroPage
Exploring the About You Page: Class CPage1
Updating Wizard Information for CPage2 and CPage3
Creating and Displaying the Wizard

Summary
Index
Introduction

If you shop like I do, you’ve just noticed this book on the shelf and picked it up to see what
interesting topics it covers. You’ve read the table of contents. Aggressive. There is obviously a lot of
detailed MFC information, but there is also a lot of ATL and COM information. Interesting.

So you turn to the introduction to see what it has to say about the wealth of information and hard-
won experience contained in this (very large) volume. Will it convince you this book is the best MFC
programming book on the market today?

Absolutely not. If by now you aren’t convinced that this is the best MFC programming book on the
market, this introduction won’t sway you. However, this is the best MFC programming book on the
market today. Why? Simply because the information contained within these pages represents
decades of collective MFC programming experience—not the hobby-shop kind of programming, but
rather the hard-core, schedule-pressure, “we need it now” kind of programming. From the basic to
the intricate, every page contains more experience per page than any other MFC book out there
today.

Programmers of all levels will benefit from the information you find here. You don’t understand the
Document/View architecture? It’s explained here. You want to extend and go beyond the Document/
View architecture? That’s here too, as well as all other aspects of MFC programming. In a nutshell,
here is what you’ll find:

• MFC’s architecture and class layout


• MFC as C++
• MFC as a Windows application programming tool
• How to create compelling MFC applications
• Using Developer Studio to create more compelling MFC applications
• Using the Windows Common Controls from MFC applications
• Implementing your own custom controls using MFC
• MFC and the GDI
• All about MFC’s message routing scheme
• Documents and views, and how to supercharge their use
• MFC application user interface programming
• MFC and printing
• MFC as a COM server programming platform, including ActiveX programming
• MFC as a COM client programming platform
• Adding scripting to your MFC applications
• MFC and databases
• MFC’s utility classes, including exception processing
• MFC and the Web, including DHTML, WinInet, and ISAPI programming
• MFC and hot graphics programming using both OpenGL and DirectX
• How to add help access to your MFC applications

It is true that many of these topics can be found in some of the other books you see sitting on the
shelf alongside this book. But none of them cover this much information in one place, and each
topic here is covered in the deepest detail. After all, that is why the book is titled MFC Programming
with Visual C++ 6 Unleashed.

How This Book Is Organized


This book is designed to be the most comprehensive MFC programming resource available
anywhere today. As you work from chapter to chapter, you’ll not only learn what makes MFC tick,
but you’ll also learn how to supercharge MFC, customize it, and take the architecture and make it
work the way you want it to work. You’ll go way beyond the wizard-generated code and see how
experienced MFC programmers do their magic. Soon, it’ll be your magic, too:

• Part I, “Core MFC,” covers the core MFC concepts. Here, you see how MFC was crafted
to be a tremendous general-purpose application framework. When you understand the
basic mechanics, you’ll see how to tailor MFC to better fit your programming requirements.
Chapter 1 shows you the MFC architecture and introduces you to the major MFC classes
you need to understand to get the most from the tool. Chapter 2 initiates your journey
through MFC application development by showing you how MFC interacts with data and
windows through the use of dialog boxes. Chapter 3 provides the details you need to get
the most from the Windows common controls, especially the useful list and tree controls.
Chapter 4 shows you how to manage GDI resources using MFC, as well as how you work
with fonts, brushes, and so on. After you know how to deal with standard controls and paint
screen items, you have what you need to tackle Chapter 5, which shows you how to create
custom controls specific to your application. Finally, Chapter 6 dispels the myths
surrounding MFC’s idle processing, allowing you to use seemingly idle application
processing time to your advantage.
• Part II, “Documents, Views, and Applications That Use Them,” details the MFC
architecture and how you can both optimize it and modify it to suit your personal needs.
Chapter 7 describes the Document/View architecture. Chapter 8 shows you how to extend
your application’s user interface. Finally, Chapter 9 blows the cover off using the printer
from your MFC application, allowing you to move past the basic wizard-based code to really
use the printer.
• Part III, “MFC and COM Programming,” describes MFC and COM programming, as most
of today’s Windows applications use COM to provide at least some of their functionality.
Chapter 10 introduces you to COM, and Chapter 11 provides an explanation of MFC’s
implementation of COM. Chapters 12 and 13 discuss OLE servers and clients, and Chapter
14 shows you how to write feature-packed ActiveX controls that the containers you’ll
develop in Chapter 15 can use to their advantage. Chapter 16 gives you the facts about
mixing MFC and ATL, a templated COM framework. Chapter 17 shows you how to add
scripting capabilities to your MFC applications.
• Part IV, “MFC Database Programming,” shows you how MFC and databases work
together. Chapter 18 looks at MFC and various database programming concepts and
technologies. Chapter 19 moves past the basics and shows you the current technologies in
MFC (and COM) database processing.
• Part V, “MFC Utility Classes,” nails down the details regarding the MFC utility classes.
Chapter 20 covers MFC string and collection management. Chapter 21 shows you how to
work with files using MFC. Chapter 22 provides the critical details you need to process
exceptional runtime conditions.
• Part VI, “MFC and the Web,” marries MFC and the Web. Chapter 23 introduces you to
Dynamic HTML (DHTML) and how you can access DHTML from your MFC application.
Chapter 24 shows you the basics of MFC network programming. Chapter 25 moves to
advanced MFC network programming. Chapter 26 describes MFC ISAPI programming.
Need to add telephonic or electronic mail support to your MFC application? Then Chapters
27 and 28 are here for you.
• Part VII, “MFC and Graphics Programming,” introduces you to the fascinating area of
MFC graphics programming. Chapter 29 describes OpenGL and how you can exploit that
technology in your MFC applications. Chapter 30 similarly describes MFC and DirectX.
Chapter 31 goes a step further by adding full-fledged multimedia support to your MFC
applications.
• Part VIII, “Advanced MFC,” gives you a wealth of insight born of experience. Chapter 32
shows you how to work with the Windows Registry database. Chapter 33 describes using
MFC from within DLLs. And the final chapter, Chapter 34breaks open the secrets of
Developer Studio AppWizard programming.
Who Should Read This Book?

Actually, this book is perfect for just about anyone interested in programming Windows applications
using MFC. Whether you are just beginning or have weathered MFC programming from its
inception, there are valuable nuggets for you here:

Beginners—The basic concepts are clearly and concisely covered. More important than
that, however, is the valuable coverage you’ll find regarding pitfalls and potential
optimizations. You’ll learn the basics, but you’ll also learn how to avoid common beginning
MFC programmer mistakes.
Casual and accomplished MFC programmers—Use this book as a reference to delve into
topics of specific interest. You will be able to reuse much of the code you find here to
enhance your ongoing Windows programming tasks. You’ll also find detailed coverage of
the latest Microsoft technologies with specific use from within MFC-based applications.
Experts—For you, this book provides the detailed programmer-to-programmer information
you won’t find elsewhere. Our goal was to provide you with the deepest details and provide
alternative methods of accomplishing intricate programming tasks. If you need it, it’s here
for you.

We have worked exceptionally hard to provide you with the definitive work to date regarding MFC
programming. Enjoy!
Foreword

The Microsoft Foundation Classes are a excellent example of how an object-oriented approach to
packaging software functionality can lead to code reuse, reduced application complexity, and in the
end, a more efficient software development environment.

MFC has been around for about seven years now. The first version of MFC was released with
version 7 of Microsoft’s 16-bit C/C++ compiler, and it represented little more than a wrapper around
the Window GDI calls. (Visual C++ version 1 followed Microsoft C/C++ version 7.)

For those who had been developing applications using the Windows API, MFC promised an
immense gain in programming productivity. Rather than write Petzold-style WinMain procedures,
message loops, and switch statements to dispatch messages, you could create an instance of a
class that would take care of much of the drudgery. Rather than create GDI objects, write many
lines of code to initialize and use the objects, carefully track their lifetimes, and be sure to release
them properly, you could instantiate an MFC class, often simply use the default values, and then let
the destructor worry about cleaning up system resources.

Indeed, to truly comprehend the advantages of using MFC, you should write a significant graphics
application first using MFC and then using strictly the Windows API. Then compare war stories.

But although MFC made the programmer’s life easier, early versions were not without cost. MFC
applications were much larger than a non-MFC equivalent. They tended to run slower as well
(probably because they were so much larger...). And whenever it was necessary to stretch the MFC
envelope a bit, a working knowledge of the Windows API was still a prerequisite.

MFC has gone through many iterations since version 1 was released. The current revision of MFC
is version 6. MFC added the Document/View architecture in version 2, ODBC and drag-and-drop
support in version 2.5, and multithreading and Unicode support in version 3. Subsequent releases
have added support for socket classes, Internet classes, OLE automation, and thread
synchronization. MFC has constantly evolved in its support of new graphic interface features and
now offers a rich collection of support classes such as arrays, lists, and strings, to name a few.

Over the years, I’ve used MFC on many projects. One of the most rewarding of those endeavors
was being part of the software development team for Qualcomm’s Eudora versions 3 and 4. We
used MFC extensively in the creation of the Windows version of that product.

The real world still holds challenges for programmers! MFC has not changed that; MFC is still
supplied with source code for good reason. But without MFC, I believe developing Eudora would
have been a much more difficult and time-consuming task. MFC allowed us to build a product that
conformed to Windows GUI standards, incorporated new and improved user interface metaphors,
and ran with a high degree of independence on many versions of operating systems and hardware
platforms. MFC also saved time when constructing non-GUI data structures and objects used by the
internals of Eudora.

This book is an authoritative reference document that describes MFC: what it is and how to get the
most out of it. This book is written by programmers for programmers. It is filled with sample
applications and code snippets to clarify and demonstrate the use of MFC 6. If you are new to MFC,
this book will get you up to speed quickly. If you are familiar with MFC but need to drill down into the
details of a specific MFC class, this book will be quite valuable as well. This book can also be used
as a companion to the Microsoft documentation, which is typically quite good but perhaps a bit terse
in spots.

I hope you find this book both educational and enjoyable.

Keith McIntyre
Vice President—Engineering
Internet Systems Division
Stellcom, Inc.
About the Authors

Eugene Olafsen has been working with Microsoft Windows since the 3.0 release. At that time, he
was attempting to build a product using Digital Research’s GEM (Graphics Environment Manager),
decided to port to Windows, and has never looked back. He has worked with MFC/OLE since its
humble beginnings. Gene has worked to develop applications ranging from engine test simulators
for Boeing to search and publishing systems for Reuters. He is an independent consultant and a
principal partner in Stratton & Associates, Ltd. (www.strattonassociates.com), a software-
engineering firm. His current projects include application development that leverages COM and
Web technologies.

Kenn Scribner actually began his career in computers in 1980 as a pizzeria manager. After he
realized he didn’t want to make pizzas for a living, he joined the Air Force as a computer operator.
Two degrees and a commission later, he found himself designing silicon-based Fast Fourier
Transform processors and running a research flight simulation facility. As the Air Force downsized,
he retired and began writing Windows applications. (He’s delighted to earn a living with his previous
hobby!) Kenn has written several commercial Windows applications, and he now finds himself
writing all the client user interface code and major ATL-based COM components for a large-scale n-
tier client/server application. Kenn also started his own company, the EnduraSoft Corporation (http://
www.endurasoft.com), writes the “Windows Programming” column for “TechTalk”—the newsletter of
the Small Computer Book Club (http://www.booksonline.com)—and acts as a Windows
programming technical reviewer for several publishers. In his dwindling spare time, Kenn enjoys the
company of his lovely wife and two wonderful children, works with his horse, remodels parts of his
house, and generally acts as EnduraSoft’s chief groundskeeper. Kenn can be reached at
[email protected].

K. David White has been developing software since 1981, using a wide variety of products,
platforms, and languages. During this time, Dave implemented several systems, ranging from a
facilities security system based on DEC MACRO-32 to several large supervisory control systems in
the oil industry on DEC VMS. Seeing the handwriting on the wall in 1993, Dave started developing
Windows-based software using Microsoft C and C++. Since that time, Dave has produced several
MFC-based products and is currently developing ATL COM objects. Dave also contributed to the
Sams book Sams Teach Yourself Database Programming with Visual C++ in 21 Days. In what little
spare time that Dave has, he thoroughly enjoys woodworking, golf, singing, and spending time with
his wife and three kids. Dave can be reached through email at [email protected].

Contributing Authors

Michael Morrison is a writer, developer, toy inventor, and author of a variety of books including
Sams Teach Yourself MFC in 24 Hours (Sams Publishing, 1999), Windows 95 Game Developer’s
Guide Using the Game SDK (Sams Publishing, 1997), Complete Idiot’s Guide to Java 1.2 (Que
Corporation, 1998), and Java 1.1 Unleashed (Sams Publishing, 1997). Michael is also the instructor
of several Web-based courses, including DigitalThink’s Win32 Programming series, the Introduction
to Java 2 series, and the JavaBeans for Programmers series. Michael cut his teeth on Windows
programming by codeveloping one of the first action games for Windows, Combat Tanks. When not
glued to his computer, risking life and limb on his skateboard, or watching movies with his wife,
Mahsheed, Michael enjoys hanging out by his koi pond.

Davis Chapman first began programming computers while working on his master’s degree in music
composition. Writing applications for computer music, he discovered that he enjoyed designing and
developing computer software. It wasn’t long before he came to the realization that he stood a much
better chance of eating if he stuck with his new-found skill and demoted his hard-earned status as a
“starving artist” to a part-time hobby. Since that time, Davis has focused on the art of software
design and development, with a strong emphasis on the practical application of client/server
technology. Davis is the lead author of Sams Teach Yourself Visual C++ 6 in 21 Days, Web
Development with Visual Basic 5, and Building Internet Applications with Delphi 2. Davis is also a
contributing author on Special Edition Using Active Server Pages and Running a Perfect Web Site,
Second Edition. He has been a consultant working and living in Dallas, Texas, for the past ten years
and can be reached at [email protected].

David Lowndes is a senior software developer at Boldon James Ltd. A Microsoft MVP since 1996,
Davis is a frequent contributor to the microsoft.public.vc newsgroups, helping with Visual C++ and
general Win32 issues. When not programming, he can be found playing guitar and trying to muscle
in on his son’s band. David can be contacted at [email protected].

Bill Heyman is an independent software consultant who specializes in systems and applications
software development using Microsoft Visual C++ and MFC Windows platforms. As a ten-year
software development veteran who has provided software engineering services for IBM and
Andersen Consulting, Bill has experience in DOS, OS/2, Windows, UNIX, and PalmOS. For more
information, including consultation for your own project, visit Bill at his Web site, http://www.
heymansoftware.com/~heyman/. His email address is [email protected].

Ed Harris is the lead engineer for tools and components development at NetPro Computing, Inc.
His group designs reusable frameworks for distributed network and directory service monitoring on
both Win32 and NetWare networks. Previous to that, Ed spent eight years as a Visual C, Windows,
and MFC consultant, specializing in the unlikely combination of custom control development and
mouse drivers. When not developing software, Ed enjoys spending time with his fiancée, Angela,
flying airplanes, and playing basketball. Ed can be contacted at [email protected].

Keith McIntyre has been developing software since the days of 4-bit processors. He has watched
and participated in the personal computer, local area network, client/server, and Internet evolutions.
During his years as a consultant/contract engineer, Keith has worked on a myriad of technologies,
including turbine engineer control systems, satellite communication systems, tape drive firmware,
UNIX and Windows device drivers, imaging and document management software, and custom
application development. Keith has contributed to many shrink-wrap software products including
those published by Optigraphics, Emerald Systems, Compton’s New Media, and Qualcomm. Keith
is currently vice president of engineering for Stellcom’s Internet Systems division. Keith McIntyre
holds a B.S. in Computer Science.

Rob McGregor began exploring computer programming in BASIC as a teenager in 1978. Since
then, Rob has worked as a programmer, software consultant, and 3D computer artist; he has written
a variety of programs for Microsoft and numerous other companies. Rob lives in Rockledge, Florida,
and in his free time, enjoys ray tracing, reading, writing, and playing guitar. You can contact Rob via
email at [email protected].

Acknowledgments

I would like to thank Chris Webb at Macmillan for his advice and support throughout my involvement
with this book. Additionally, I cannot praise the various editors and individuals involved in production
enough for their input and the value they add to this project. I would like to thank Hub and Simon at
TSCentral for their flexible working arrangements during the final push to complete this text. Thanks
to John Penrose of Progressive Technologies for his late-night calls, reminding me that I’m not the
only one up working. Thanks to my brother Stephen for getting my network running and answering
my NT Server administration questions. Thanks also to my friends at Reuters, especially Steve,
Dave, Ray, Joe, and Ed. Finally, I must acknowledge Gina for her tireless code reviews throughout
the years.
—Gene Olafsen

If there was a time not to write a book, it was while I was writing this one. My wife was studying for
medical school entrance, my current project at work wanted to consume all my time and then some,
and my children were very, very young (miss a day and you miss a lot). I couldn’t forget to thank
them for their sacrifice so that I could make this effort. I would also like to thank the terrific team at
MCP, especially Chris Webb, who put up with this cranky author. (I was, too; just ask him.) I would
like to extend most heartfelt thanks to Keith Davenport, Dana Lesh, and Margaret Berson, all at
MCP. It was they who had to read my raw material and fire it in the crucible of experience to
produce the fine product you hold in your hands. And thanks to the nameless (to me) crew, who
tirelessly set the type, adjusted the margins, and actually put my words to paper. From experience, I
know it’s not a small task. Finally, thanks to you for taking the time to look over my work and find it
worthy of your hard-earned dollar. I hope you enjoy reading it and find it tremendously useful in your
daily work.

—Kenn Scribner

There are so many people that have been supportive through the writing of this book. I want to
thank Chris Webb of Macmillan Publishing for being extremely patient during this ordeal. Writing a
book about a topic that has a plethora of coverage already is no easy task. Coupling that with the
demands of a job and family leads to very long nights and extended schedules. Even though the
publishing deadlines loomed large in the rear-view mirror, Chris remained supportive during the
entire effort. He was also responsible for tracking down contributing authors. Another person that
needs recognition is my mother. I would like to thank her for helping me through school all those
years ago. Her sacrifices to her family have paid off in dividends that cannot be described by mere
words. Thanks, Mom! I would also like to thank Kenn Scribner for his contribution to this effort and
his insight into helping me put together the table of contents. His daily support and encouragement
helped me cope with the daily pressures. If you need an ActiveX guru, Kenn is your man! But, most
of all, I want to thank the love of my life, my wife, for allowing me to put myself into this work. The
late nights and missed opportunities can never be regained, and I just want to tell my family how
much I love them!

—K. David White

Dedication

To my wife, Donna, for her love, encouragement, and understanding—and to my children, Ashley
and Eric, for the insight and wisdom they offer, beyond their years.

—Gene Olafsen

This, my first book, is lovingly dedicated to my wife, Judi, and to my children, Aaron and Katie.

—Kenn Scribner

Tell Us What You Think!

As the reader of this book, you are our most important critic and commentator. We value your
opinion and want to know what we’re doing right, what we could do better, what areas you’d like to
see us publish in, and any other words of wisdom you’re willing to pass our way.
As an associate publisher for Sams Publishing, I welcome your comments. You can fax, email, or
write me directly to let me know what you did or didn’t like about this book—as well as what we can
do to make our books stronger.

Please note that I cannot help you with technical problems related to the topic of this book, and that
due to the high volume of mail I receive, I might not be able to reply to every message.

When you write, please be sure to include this book’s title and author as well as your name and
phone or fax number. I will carefully review your comments and share them with the author and
editors who worked on the book.

Fax: (317)581-4770
Email: [email protected]
Mail: Bradley L. Jones
Associate Publisher
Sams Publishing
201 West 103rd Street
Indianapolis, IN 46290 USA
Part I
Core MFC

In This Part

• The MFC Architecture 7


• MFC Dialogs, Controls, and Data Interaction 49
• The Windows Common Controls 79
• Painting, Device Contexts, Bitmaps, and Fonts 147
• Custom Control Development 213
• The MFC Application Object, Message Routing, and Idle Processing 241

Chapter 1
The MFC Architecture

by Bill Heyman

In This Chapter

• A Brief History of MFC 8


• The MFC Class Hierarchy 13

The Microsoft Foundation Classes (MFC) allow you to develop C++ GUI applications for Windows
using its rich set of classes. This chapter discusses the evolution of MFC and the fundamental
classes used in almost every MFC-based application.

A Brief History of MFC

Since its beginnings in 1987, Windows has introduced legions of traditional DOS programmers to a
new way of programming: a device-independent, event-driven model. Although the Windows API
has grown to add much new functionality, it still retains the basic functions that existed in the early
versions of Windows (such as Windows/286 and Windows/386).

In the late 1980s, BASIC, 8088 assembler, and Pascal were the lingua francae for DOS software
development. At this time, the C language was starting to grow beyond its UNIX roots and become
a high-performance, systems development language on other platforms. Microsoft’s choice of using
C (combined with 8088 assembler) for the development of Windows allowed C to gain a foothold
among the PC developers.

The original Windows API (now sometimes called Win16) catered to using a C development
environment. The American National Standards Institute (ANSI) standardized the C language in
1989, thus solidifying C as a language for application and systems development. Armed with the
Windows Software Development Kit (SDK) and the Microsoft C compiler, developers started
developing GUI applications that took advantage of the Windows API.

The C language was procedural—it had no built-in support for the object-oriented features that are
commonly used today: encapsulation, inheritance, and polymorphism. The Windows API was
designed and delivered as a procedure-based interface and, hence, was perfect for the
development technology of the time. However, as object-oriented extensions to C were developed
and more widely accepted in a new language called C++, an object-oriented wrapper interface to
the Windows API seemed a natural next step. Microsoft developed this interface as its Application
Frameworks (AFX) product in 1992. This evolved into the Microsoft Foundation Classes (MFC)
product that exists today.

Note:

The Windows API is object-based. This means that you can programmatically
interact with the system objects (such as windows, semaphores, and pens) through
a handle and a defined interface. The actual implementation and data used by the
implementation is hidden from the view of the program. This “data hiding” is called
encapsulation.

The MFC Class Libraries are object-oriented. This means that in addition to the
encapsulation, the interfaces (packaged in a set of C++ classes) also provide
inheritance and polymorphism. Inheritance is the capability to share and extend the
functionality of an existing class. Polymorphism is the capability of objects to
support the same interface, but provide different implementations.

Note:

Although the Windows API is procedural and designed to be called from procedural
languages (like C), you can (and will) use the Windows API directly from your MFC
applications written in C++.

The concept of device independence was a boon for both software developers and hardware
manufacturers (well, at least the manufacturers that didn’t have a great amount of market share at
the time). Unlike the traditional DOS programs that required specific routines for different video and
print devices, programs coded for Windows could be written to a common interface and work across
a wide variety of video and print devices. The result is that developers could focus more on the
problem on hand, rather than the hardware used to solve the problem; and manufacturers could
focus more on creating device drivers for Windows and allow a much wider variety of software that
can use their devices.

Concomitant with the move to device independence, Windows GUI development forced a paradigm
shift on the traditional DOS programmers. At that time, most software was written in a procedural
manner: one function calling another, with the main program always being in control. The event-
driven model forced programs to give up their total control and, instead, wait and respond to
external events to provide their functionality to the end users.

The structure of Win16 (and now Win32) GUI programs remains the same today as it was in 1987.
Figure 1.1 shows the basic structure of a Windows GUI application. Observe that each program
consists of an entry point, the creation of a main window, a message loop, and the destruction of
the main window. In addition, there is a function associated with the main window, called a window
procedure, which contains the code that handles the system and application events (such as
keyboard entry, mouse movement and clicks, timer alarms, menu selections, and pushbutton clicks).
Figure 1.1 Structure of a Windows GUI application.

The entry point of a Windows GUI program is called WinMain. Named similarly to the entry point of C
programs, main, every Windows GUI application must provide a WinMain function. Unlike main, it is
passed different parameters and is declared as follows in Win32 programs:

int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,


LPSTR lpCmdLine, int nShowCmd);

The four parameters to the WinMain function include two handles (HINSTANCE), a pointer to a string
(LPSTR), and an integer (int). The instance handle represents a unique application identifier for the
program’s main executable file in memory. The previous instance handle is no longer used in Win32
and is always equal to zero. The pointer to a string contains the command-line arguments passed to
the program at start time. Finally, the integer contains an integer value that the program is supposed
to pass to the ShowWindow function that indicates whether the main window is to appear minimized,
maximized, or normal.

Note:

A handle is simply a 16- or 32-bit value that uniquely identifies a system object to an
application. By exposing only the handle of a system object, Windows can hide
(encapsulate) that object’s implementation and data and provide a controllable
interface to that system object. This allows Microsoft to add more functionality to
existing system objects, yet still support old applications—as long as they do not
change the behavior of the existing interfaces. (Occasionally some interface’s
behavior does change between releases. This often is the exception rather than the
rule.)

The most basic C++ classes in MFC wrap the handles to the Windows system
objects: windows, device contexts, pens, and brushes, to name a few.

Because it needs to perform a great deal of initialization at program startup and it provides an object-
oriented interface to the Windows API, MFC provides a WinMain function for your application. The
MFC-provided WinMain calls another function, AfxWinMain, which creates and manages your
CWinApp-derived application object. If you need to perform application-specific initialization, run
handling, and/or termination, refer to the more detailed discussion of the CWinApp class and
CWinApp::InitInstance, CWinApp::Run, and CWinApp::ExitInstance methods later in this chapter.

Without a main window, an application wouldn’t have a graphical user interface and would find it
hard to respond to window-related events. Consequently, almost all Windows GUI applications
create a main window as their first order of business when starting.

Using direct calls to the Win32 API, your application calls RegisterClassto register a window class
(basically associating a name with a window event callback function), followed by CreateWindowEx,
to create a main window (which is an instance of the registered window class). In MFC, this work is
done “automagically”when Visual Studio generates the code for you. Generally, in generated code,
your main frame’s application window is created in the CWinApp::InitInstancecode.

The next basic feature required for every Windows application is the message loop. Typically in
programs that call the Windows API directly, this is accomplished as shown following:

MSG msg;
while (GetMessage(&msg, 0, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

This loop allows the current application thread to get system events that are queued up for windows
created by that thread. The GetMessagefunction returns a Boolean value TRUEfor all messages,
except the WM_QUITmessage, which allows the whileloop to end, due to application termination.
After the call to GetMessagereturns with a message (event) for one of the thread’s windows, that
thread calls TranslateMessage, to process any accelerator keys and menu hot keys, followed by
DispatchMessage, to send the message to the window to which it belongs by calling that window’s
registered window procedure.

What Is a Thread

You are probably intimately familiar with the concept of a process. In many operating systems, a
processcorresponds to an instance of executing code in the computer’s memory. Processes
contain memory (usually some for code and some for data) and open resources, such as files,
pipes, and other system objects. Traditionally, many operating systems had the current execution
state (current machine registers, including the instruction pointer, and a stack) associated with the
process. As a result, each process was treated as a single unit of execution when the operating
system shared the CPU via multitasking.

Windows and many other modern operating systems allow multiple units of execution within the
same process. Each unit of execution is called a thread. Each thread within a process has the
same access to all the memory and resources owned by the process. However, each thread
maintains its own copy of the machine registers and call stack.

Threads are often used to perform some operation “concurrently”within the process and provide a
simpler and more efficient mechanism as compared to creating or “forking”a new process.
(Actually, on a uniprocessor system, threads cannot literally run at the same time; however, on a
multiprocessor system they actually could run simultaneously.)

Another advantage of threads is that they enable you to separate logical units of code and
maximize throughput in your programs. For example, if one thread is waiting for (blocking) a file I/
O request, another thread could perform some mathematical calculation, and yet another thread
can handle user interface events. The end result is that the program’s overall performance can be
improved because its utilization of the CPU(s) is maximized for a given amount of time.

For MFC applications, once again the message loop is automatically included as part of your
application. In the case of Single Document Interface (SDI) and Multiple Document Interface (MDI)
applications, this message loop is put into control through the CWinApp::Runmethod. (More
specifically, CWinApp::Runcalls the CWinThread::Runmethod, which is in its base class.) For dialog-
based applications, the message loop is instantiated during the CWinApp::initInstancethrough a call to
CDialog::doModal. In addition, when messages are dispatched from the thread’s message loop, they
are routed through the standard MFC-owned window procedures (AfxWndProcBaseand AfxWndProc)
and finally mapped to a function within your window or dialog class. (This process will be explained
in more detail later in this chapter.)

The final phase of a typical Windows program’s WinMainfunction is the destruction of the main
window via a call to the DestroyWindowfunction. Of course, this is also part of the MFC code that your
application uses. If you need to do termination processing in your application, you would override
the CWinApp::ExitInstancemethod in your application’s CWinApp-derived class.

At this point you should have a basic understanding of how some of the more basic features of a
standard Windows API application correlate to an MFC application. From this point forward, the
discussions will leave the Windows API behind and deal with the features of MFC. Of course,
specific Windows APIs might be mentioned from time to time, but not as a major part of any section
in this book.

The MFC Class Hierarchy

So far several MFC classes have been mentioned. This section covers some of the more important
classes that make up the MFC architecture.

Figure 1.2 shows the inheritance hierarchy of some of the most important application architectural
classes within MFC. You might immediately observe that these classes all ultimately derive from a
class named CObject.

Figure 1.2 MFC architecture class hierarchy.

CObject

The CObjectclass is the “mother of all MFC classes.”Well, actually not quite allMFC classes, but
quite a few of them. Its primary responsibilities are to support handle runtime type information and
object persistence (or serializationin MFC-speak), and to perform diagnostic output for derived
objects.

Classes that are derived from the CObjectclass can support its features in one of four ways:
1. (Most basic) General diagnostic support.
2. (Dynamic) All features described thus far plus runtime type identification.
3. (DynCreate) All features thus far plus the capability of unbound dynamic object creation.
That is, the class can be constructed by code that does not know the class’s name at the
time it was compiled and linked.
4. (Serial) All features thus far plus the capability of storing and restoring instances of the
object to and from a byte stream.

Diagnostic Support (All CObject-Derived Classes)

The diagnostic support within the CObjectclass is limited to two methods: AssertValidand Dump. Each
of these methods can be called at any time from your derived class.

AssertValid

The AssertValidmethod allows a class to perform a sanity check on itself before continuing. It is
declared as a public method in the CObjectclass as follows:

virtual void AssertValid() const;

If you choose to override this method, you will typically use the ASSERTmacro to perform sanity
checks on your object’s data. In addition, you should call your base class’s implementation of this
method so it can also validate its data. Because this method is const, you cannot change your
object’s state from within this method.

Caution:

Do not depend on AssertValidworking in release code. This is because the


ASSERTmacro is only defined when _DEBUGis defined at compilation time.

Because it is a public method, you can call it either from inside your class or from outside your class
at any time.

Dump

The Dumpmethod allows a class to put diagnostic information in the human-readable form of text
and/or numbers to a stream, typically through the OutputDebugStringAPI, which displays it in the
debugger’s output window (or any other program that traps debug strings). It is declared as a public
method in the CObjectclass as follows:

virtual void Dump(CDumpContext&dc) const;

If you override this method, first call your base class’s dumpmethod. Next, call the insertion operator
(<<) on the CDumpContextobject to output information about your object. As a C++programmer, this
is no different from how you would use the cerror coutobject to output diagnostic information about
your object. Finally, make sure that you do not output a trailing newline (\n) on your final line of
output.

Note:

If your CObject-derived class does not include runtime type information, CObject::
dumpdisplays only CObjectas the class name. Otherwise, it properly displays the
name of your derived class.

Runtime Type Information (Dynamic and DynCreate Classes)

When a class supports MFC’s Runtime Type Information (RTTI), it can respond to a request for its
name and its base class. To support the Dynamicform of RTTI, simply include a
DECLARE_DYNAMICmacro invocation within your class declaration and an
IMPLEMENT_DYNAMICmacro invocation near your class definition.

Caution:

Do not confuse MFC’s RTTI with the RTTI support built into the newer C+
+compilers (and activated using Visual C++’s /GRswitch). As of MFC 4.2, MFC’s
RTTI support does not use C++’s RTTI support in any way, shape, or form.

If you have a class named CMyClassthat is derived from CObjectand you want your class to support
RTTI, add the following to your header file:

class CMyClass : public CObject {


DECLARE_DYNAMIC(CMyClass)
// other class information
};

In your source file, add the following at file scope:

IMPLEMENT_DYNAMIC(CMyClass, CObject)

When your class is Dynamic, it has a static CRuntimeClassobject associated within it that contains the
runtime type information for the object. You can obtain a pointer to the runtime class object using
the RUNTIME_CLASSmacro, invoking it using the name of the class that you’d like. So, to obtain a
pointer to the CRuntimeClassobject associated with CMyClass(a Dynamicclass), simply use the
following code:

RUNTIME_CLASS(CMyClass)

An extension of the Dynamicclass is a DynCreateclass. When a class is DynCreate, a program can


construct an object of that class simply by knowing its name at runtime as opposed to compilation
time.

To support dynamic creation, your class must have a default constructor (that is, a constructor with
no parameters) that creates a stable object, and you must add macro invocations for
DECLARE_DYNCREATEand IMPLEMENT_DYNCREATE, as you did for dynamic objects previously.

Note:

Because dynamic creation is a superset of the dynamic support, do not add


invocations of the DECLARE_DYNAMIC/IMPLEMENT_DYNAMICmacros to your class
files.

The DynCreatemacros add a method named CreateObjectto your class. The implementation of this
method simply calls newon your specific object and returns it as a pointer to a CObject. If you have a
class named CMyClassthat supports dynamic creation, you can instantiate one of these objects by
using the RUNTIME_CLASSmacro to get the RTTI information and call the CreateObjectmethod on it
as follows:

CMyClass *myObj = DYNAMIC_DOWNCAST(CMyClass,


RUNTIME_CLASS(CMyClass)->CreateObject());

Note:

The DYNAMIC_DOWNCASTand STATIC_DOWNCASTmacros improve the type safety


for casting operations on MFC CObject-derived types. By using MFC’s RTTI, they
can check if the cast is valid at runtime.

You can use the C++dynamic_castand static_castkeywords to perform typesafe casts


on MFC objects as long as you compile your application with C++RTTI turned on
(Visual C++’s /GRcompiler switch). MFC is already built with this switch on;
however, the Visual Studio default is off.

Serialization (Serial Classes)

Serializationis the capability of an object to save its state to a byte stream and rebuild itself from that
stream. If an object supports serialization, it can be saved to a file, transmitted over a socket or
pipe, and later reconstituted either from that file or on the other end of the socket or pipe.

To create a CObject-derived class that supports serialization, you must add the
DECLARE_SERIALmacro inside its class declaration in the header file and the
IMPLEMENT_SERIALmacro in the source file containing the class’s method and data definitions, in
the same manner as was demonstrated for a Dynamicclass.

Note:
Because serialization is a superset of the dynamic and dynamic creation support,
do not add invocations of the DECLARE_DYNAMIC/IMPLEMENT_DYNAMICor the
DECLARE_DYNCREATE/IMPLEMENT_DYNCREATEmacros to your class files.

When a CObject-derived class supports serialization, there are two methods that are used:
IsSerializableand Serialize.

IsSerializable

The IsSerializablemethod allows another object to determine whether this CObject-derived class
supports serialization. It is declared as a public method in the CObjectclass as follows:

BOOL IsSerializable() const;

Because this function is not virtual, you cannot override it in any meaningful way. The MFC
CRuntimeClassobject determines whether serialization is supported from its m_wSchemafield. The
only valid values supported within this field are 0xffff(meaning not serializable) and
VERSIONABLE_SCHEMA(meaning supports standard MFC serialization).

Serialize

The Serializemethod is called to actually perform the saving and restoring of the object from a
serialization stream (within a CArchiveobject). It is declared as a public method in the CObjectclass as
follows:

virtual void Serialize(CArchive&ar);

If your class needs to save or restore its state when being serialized, you must override this method.

Of course, if you’re implementing this method so your class can support serialization, you first need
to know whether or not you need to save your object’s data to or restore your object’s data from the
CArchivestream. You can use either the CArchive::IsLoadingor CArchive::IsStoringmethods to determine
which direction you need to go.

Note:

You are guaranteed that if CArchive::IsLoadingreturns TRUE, CArchive::IsStoringreturns


FALSEand vice versa.

Next, if the archive object is in loading mode, you can use the CArchiveclass’s extraction operators
(>>) to retrieve data from the data stream. Likewise, if it is in storing mode, you can use its insertion
operators (<<) to add data to the data stream.
Serialization “Gotchas”

The serialization protocol requires that you call your base class’s Serializemethod before
performing your own serialization support.

If you detect an error while handling your Serializemethod, you can safely throw one of the
following exceptions: CMemoryException, CFileException, or CArchiveException.

You must store your data in exactly the same order that you load it.

All MFC lists support serialization. Be very careful when serializing lists of objects. If your list
contains a pointer to an object being serialized, it will attempt to “reserialize”the object and get
trapped in a recursive loop. This will cause a hang followed by a stack overflow in your
application.

A typical implementation of the Serializemethod for the fictitious CMyClassclass is shown here:

void CMyClass::Serialize(CArchive&ar)
{
CObject::Serialize(ar);

if (ar.IsStoring()) {
ar <<m_myData;
} else {
ar >>m_myData;
}
}

Serialization can be done either explicitly or implicitly by your application through the MFC
framework. When your MFC application is generated by Visual Studio, it implicitly creates a
CArchiveobject, associates it with a CFileobject, and calls the CArchive::ReadObjector CArchive::
WriteObjectmethods in response to the File menu bar, Open, Save, and Save As commands. These
methods call the Serializemethod on the appropriate objects.

Likewise, you can perform your own serialization explicitly within your application. First, create a
CFileor CFile-derived object (such as CSocketFile). Next, construct a CArchiveobject passing a pointer
to your CFileobject to its constructor. Finally, call either the CArchive::WriteObjector CArchive::
ReadObjectmethod to save or restore your object, respectively.

CCmdTarget

The CCmdTargetclass, derived from CObject, is responsible for managing the routing of system and
window events to the objects that can respond to these events. So, any class that expects to
receive one of these events derives from this class and overrides the CCmdTarget::
OnCmdMsgmethod.

Examples of classes that expect to receive system and window events are CWnd(the window class),
CView(the view class), CDocument(the document class), CWinThread(the user interface thread class),
and CWinApp(the application class). These classes are described in more detail later in this chapter.

The methods within the CCmdTargetclass can be organized into three categories: message routing,
wait cursor, and automation support.
Message Routing

In all Windows GUI applications, the application thread’s message loop processes all system and
window events for that thread’s windows. MFC is no exception. However, unlike the procedural
window procedure with a large switchstatement, the MFC architecture maps these events to object
methods for each object.

In fact, a C++programmer would immediately think that C++does provide a mechanism for doing just
this: virtual functions. Continuing on this logic, that programmer would think that to process and
dispatch the window messages, simply create a base Window object and add virtual functions for
each of the possible events that could arrive. Furthermore, you would derive from the base Window
object and simply override those events to which your window needs to respond.

Ah, if it were only that simple. MFC does not use the C++virtual function mechanism for handling the
various Windows events. Instead, it creates a static data structure for each CCmdTargetclass called
a message map. In essence, this structure maps an event to its corresponding handler in the object.

Why Doesn’t MFC Use the C++Virtual Function Mechanism?

One explanation suggests that there is a large amount of performance overhead carrying around
the virtual tables (vtables) for all the possible objects.

In analyzing this explanation, a few issues need to be considered: What does “carry”mean and
how large are these vtables anyway? Certainly most MFC objects do, in fact, have vtables and,
certainly, each object has an extra four bytes to store a pointer to the table. These four bytes
have to be “carried”around anyway.

However, for each class of object, there is only one vtable; that is, all CWndobjects share the
same vtable in memory. Also, each abstract class has a vtable that won’t ever be used. (Actually
MFC now uses the _declspec(novtable)keyword to prevent these from being linked into the
application.) So, the number of static vtables is less than or equal to the number of classes that
exist within the executable.

In addition, each vtable has one four-byte entry for each possible event that can occur. Assuming
that there are 256 events, each vtable would be approximately 1KB in size. So, if you have 100
different concrete CCmdTarget-derived classes in your application, this would add about 100KB to
your application size. Certainly on a system that has only 16MB of RAM, it could eat up 2% of
your physical RAM if you were running three of these programs (and all the vtables were not
paged out to disk).

In the context of 16MB machines, this might be unreasonable. Today, with 128MB to 256MB
standard on some machines, it might not be that bad. However, if the number of events is
increased, the cost increases linearly, of course. Overall, the explanation seems plausible, but
still not totally satisfying. (The term carrystill seems off base, however, unless used in the context
of the actual executable image.)

Another rarely espoused explanation, however, seems to indicate another possibility. C++’s
virtual table mechanism does not provide any sort of forward compatibility. As a result, as
Windows evolves and more events are possible, either reserved space would have to be
allocated in each object’s vtable beforehand or the application would have to be recompiled for
each release.
So, it would require early versions of MFC to determine up front what virtual table size is required
going forward. That would be a very difficult task, except perhaps for an oracle of some sorts (no
pun intended).

When MFC was originally developed, it did not have the advantage of the Component Object
Model (COM) to provide a more flexible, maintainable vtable mechanism. Consequently, the
designers created the message map as a slightly slower, but more memory-efficient and
maintainable custom “vtable”mechanism.

The method that MFC uses for mapping system and window events to objects is called message
mapping. Each class that is derived ultimately from CCmdTargetcontains a message map that allows
it to specify the events that it can handle and map those events to a method within the class.

To add a message map to your class, you must add an invocation of the
DECLARE_MESSAGE_MAPmacro within your object. For the fictitious CMyViewclass, this code is as
follows:

class CMyView : public CView { // CView is a subclass of CCmdTarget


DECLARE_MESSAGE_MAP()
// other class information
};

The DECLARE_MESSAGE_MAPmacro expands to declare two static members in your class, the
combination of which comprises your message mappings. In addition, two methods are added; one
internal (_GetBaseMessageMap), which returns the message map in the base class, and one external
(GetMessageMap), which returns a pointer to the message map mappings for this class.

Next, you need to add macro invocations within your source code at file scope to actually define the
message map for your class. An example of such code for the CMyViewclass is as follows:

BEGIN_MESSAGE_MAP(CMyView, CView)
ON_COMMAND(ID_FILE_OPEN, OnFileOpen)
ON_WM_SIZE()
END_MESSAGE_MAP()

The key elements are the macro invocations for BEGIN_MESSAGE_MAPand END_MESSAGE_MAPthat
provide the appropriate delimiting code. In between these macros, there are macros that describe
the exact events that can be handled by this class. In this case, using the ON_COMMANDmacro,
CMyViewmaps the menu item command identifier ID_FILE_OPENto the OnFileOpenmethod. In
addition, using the ON_WM_SIZEmacro maps the WM_SIZEwindow event to a method named OnSize
(which is actually defined in that specific macro). Because there are a large number of actual
events, there are a large number of macros that you can use in a message map. For more
information, please refer to header file AFXMSG_.H.

OnCmdMsg

There is one method that supports the dispatching of system and window events, OnCmdMsg.
Typically, you do not need to modify the handling of this method and, hence, you can just let the
event dispatching occur automatically. However, if you need to provide custom and/or dynamic
routing of events, you can override this virtual function in your CCmdTarget-derived class. The
declaration of this method is as follows:
virtual BOOL OnCmdMsg(UNIT nID, int nCode, void *pExtra,
AFX_CMDHANDLERINFO *pHandlerInfo);

Your override method must return TRUEif it handles the event, and FALSEotherwise.

Wait Cursor

The CCmdTargetclass defines three methods that applications can use to change the state of the
mouse pointer. These methods are BeginWaitCursor, EndWaitCursor, and RestoreWaitCursor.

BeginWaitCursor, EndWaitCursor, and RestoreWaitCursor

Use the BeginWaitCursormethod to change the mouse pointer to an hourglass, thus notifying the user
that the current operation might take some time. When your operation is complete, call
EndWaitCursorto change the pointer back to what it was at the BeginWaitCursorcall.

Use RestoreWaitCursorto change the pointer back to its original state after the pointer had been
changed by some external operation, such as displaying a message box.

These methods are declared as follows in the public access section of the CCmdTargetclass:

void BeginWaitCursor();
void EndWaitCursor();
void RestoreWaitCursor();

Automation Support

If your MFC application allows interaction through an IDispatchCOM interface, it supports


automation. The CCmdTargetclass not only can dispatch system and window events to objects of its
derived classes, but also it can translate automation interface methods in a similar way.

The methods within CCmdTargetthat support automation are: EnableAutomation, FromIDispatch,


GetIDispatch, IsResultExpected, and OnFinalRelease.

EnableAutomation

The EnableAutomationmethod is called from your CCmdTarget-derived class’s constructor to indicate


that it contains both a dispatch and an interface map. It is declared in the public section of the
CCmdTargetclass as follows:

void EnableAutomation();

The dispatch and interface maps are very similar in layout to the message map that was described
previously in this chapter. Like the message map, they require macro invocations in the class
declaration using the DECLARE_DISPATCH_MAPand DECLARE_INTERFACE_MAPmacros,
respectively. The following code demonstrates these macros for the sample CMyDocumentclass:

class CMyDocument : public CDocument {


DECLARE_DISPATCH_MAP()
DECLARE_INTERFACE_MAP()
// other class member declarations...
};
When declared in the class declaration, the dispatch map and interface maps must be defined in the
source file for the class. In the case of the CMyDocumentclass (located in the MyApp application),
these maps look as follows:

// {6C9C4209-9D58-11D2-8FAF-00400566CE21}
static const IID IID_IMyApp =
Ä{ 0x6c9c4209, 0x9d58, 0x11d2,
Ä{ 0x8f, 0xaf, 0x0, 0x40, 0x5, 0x66, 0xce, 0x21 } };

BEGIN_INTERFACE_MAP(CMyDocument, CDocument)
INTERFACE_PART(CMyDocument, IID_IMyApp, Dispatch)
END_INTERFACE_MAP()

BEGIN_DISPATCH_MAP(CMyDocument, CDocument)
DISP_FUNCTION(CMyDocument, “OpenFile”, OpenFile, VT_EMPTY, VTS_BSTR)
END_DISPATCH_MAP()

In this example, the COM automation interface for the MyApp application is mapped to be a
dispatch interface. Additionally, one automation (dispatch) function, OpenFile, is mapped to a
method of the same name.

FromIDispatch and GetIDispatch

The FromIDispatchand GetIDispatchmethods allow an application to get the CCmdTargetobject given an


IDispatchinterface pointer and vice versa. Of course, not all CCmdTargetobjects contain
IDispatchinterfaces and, hence, might return a NULLpointer. These methods are declared in the
public section of the CCmdTargetclass as follows:

static CCmdTarget * FromIDispatch(LPDISPATCH lpDispatch);


LPDISPATCH GetIDispatch(BOOL bAddRef);

IsResultExpected

The IsResultExpectedmethod returns TRUEif the automation client is waiting for a return value from
the function. Otherwise, if a result is not expected, the application can ignore calculating it
(particularly if it might take time to do so) and improve automation performance. This function is
declared as follows in the public section of the CCmdTargetclass:

BOOL IsResultExpected();

OnFinalRelease

The OnFinalReleasemethod is a virtual method that the CCmdTarget-derived class can choose to
override to perform any sort of special processing when the last COM interface reference to or from
the object is released. Otherwise, the CCmdTarget-derived object is simply deleted. This method is
declared in the public section of the CCmdTargetclass as shown here:

virtual void OnFinalRelease();

CWinThread

The CWinThreadclass is derived from the CCmdTargetclass and represents a thread of execution
within the MFC application. All MFC applications have at least one CWinThreadobject—the main
application’s CWinAppobject (which is derived from CWinThread). If you want to provide additional
asynchronous processing within your application, you can construct and run additional
CWinThreadobjects, as needed.

You can obtain a pointer to the current CWinThreadobject by calling AfxGetThread.

Within MFC, there are two different types of threads: worker threads and user interface threads.

Worker Threads

Worker threadsare threads that are created to do some additional processing, but do not require
any sort of system or window event processing. A worker thread would be useful to perform a time-
consuming calculation or to read data from a file. By creating a worker thread, you can do additional
work without interfering with the operation of the application’s user interface.

A worker thread is created using the AfxBeginThreadfunction. In its simplest form, you simply need a
callback function and a user-defined data pointer. After it has been created, a CWinThreadobject is
returned to the calling thread, and the new worker thread starts execution. This form of
AfxBeginThreadis declared as follows:

CWinThread *AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam,


int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0, DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );

In addition, your callback function, AFX_THREADPROC, must be declared as shown here:

typedef UINT (AFX_CDECL *AFX_THREADPROC)(LPVOID);

When the thread function is complete and wants to end itself, it simply returns or calls
AFXEndThread. If you want to end the created thread from another thread, you must set up your own
signaling mechanism, probably via the use of system event semaphores.

User Interface Threads

User interface threadsare threads that have their own message loop and can create, interact with,
and destroy user interface objects, such as modeless dialog windows that operate separately from
the main application thread’s user interface.

To use user interface threads, you must first derive a class from the CWinThreadclass that creates a
user interface element for which to handle events. You can choose to use the InitInstanceand
ExitInstancemethods to show and hide your user interface elements. When that is done, you can
choose one of two approaches to create and start the thread.

The first approach is to construct your derived CWinThreadfunction and then call the
CreateThreadmethod to start it. This method is declared as follows in the CWinThreadclass:

BOOL CreateThread(DWORD dwCreateFlags = 0, UINT nStackSize = 0,


LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);
Using the CreateThreadmethod is a fairly clean coding approach to creating and starting a new user
interface thread.

The second approach to creating a user interface thread is very similar to the technique for creating
a worker thread, except for using a different overload of the AfxBeginThreadfunction. This overload is
declared as follows:

CWinThread *AfxBeginThread(CRuntimeClass* pThreadClass,


int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0, DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);

You might note that the primary difference between the two overloads is in the initial parameter.
Instead of requiring a callback function and a user-defined pointer, the user interface
AFXBeginThreadoverload requires a pointer to the runtime class of the CWinThread-derived object that
you want to instantiate. Remember to use the RUNTIME_CLASSmacro to obtain a pointer to this
object for a specific class.

When creating CWinThreadobjects, you might find it necessary to initialize some members of the
CWinThreadobject beforethe thread starts executing. To do this, pass CREATE_SUSPENDEDto the
dwCreateFlagsparameter, which creates the thread in suspended mode. After the thread has been
created, set the members that you require and call the CWinThread::ResumeThreadmethod to allow
the thread to start executing.

When the thread needs to terminate, it can simply call AfxPostQuitMessageto end its message loop
and set an exit code for the thread. This function is declared thus:

void AFXAPI AfxPostQuitMessage(int nExitCode);

CWinThread Methods

The methods described in the following sections are contained in the CWinThreadclass. Remember
that because your CWinApp-derived application object has CWinThreadas a base class, you can use
these methods on that object.

InitInstance and ExitInstance

The InitInstanceand ExitInstancevirtual functions can be overridden by your application to provide pre-
message loop and post-message loop initialization and termination. If you override the Runmethod,
as you can do to create a worker thread, these virtual functions do not get called, unless you call
them directly. These public methods are declared as follows:

virtual BOOL InitInstance();


virtual int ExitInstance();

The CWinThread::ExitInstanceimplementation automatically deletes the CWinThreadobject if the


m_bAutoDeletemember is set to TRUE. Therefore, if you override ExitInstance, call your base class’s
ExitInstanceto maintain this behavior. Additionally, if the call to InitInstancefails, ExitInstanceis called.

Run

The Runmethod is where the actual thread operation occurs. The default implementation provides a
message loop for the thread and continues until a WM_QUITmessage is encountered. You can
create a CWinThread-derived worker thread if you override the Runmethod to perform your worker
thread processing. This method is declared as a public method as follows:

virtual int Run();

The return value from this function becomes the exit code from the thread. This value can be
obtained by calling the GetExitCodeThreadfunction in the Windows API.

SuspendThread and ResumeThread

Use the SuspendThreadand ResumeThreadmethods to control the execution of a thread. A suspend


count is maintained with each thread; thus, for each call that you make to SuspendThread, you must
make an equal number of calls to ResumeThreadbefore the thread resumes execution. These public
methods are declared as shown here:

DWORD SuspendThread();
DWORD ResumeThread();

The return value from each of these methods is the suspend count for the thread upon completion
of the call.

GetThreadPriority and SetThreadPriority

If you want control over the priority of a thread owned by a CWinThreadobject, use the
GetThreadPriorityand SetThreadPrioritymethods. The Windows thread scheduler, of course, prefers
higher priority threads to lower priority ones. The priority values can be one of the following (or an
integer between any two levels): THREAD_PRIORITY_IDLE, THREAD_PRIORITY_LOWEST,
THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL,
THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST, and
THREAD_PRIORITY_TIME_CRITICAL.

The thread priority methods are declared as follows:

int GetThreadPriority();
void SetThreadPriority(int nPriority);

Caution:

Do not assume that priority is an absolute value in Windows. The thread scheduler,
by default and design, can randomly and temporarily boost a thread’s priority. If you
need to disable this behavior, use the SetThreadPriorityBoostAPI.

IsIdleMessage and OnIdle

You can use the IsIdleMessageand OnIdlemethods to control processing that occurs when no
messages exist in the thread’s message queue.
If you think that idle message processing might be required for your application, first consider using
either worker or user interface threads to do the work. If you do not want the overhead and
synchronization required for threading, use the age-old technique of Windows idle message
processing.

The OnIdlemethod is called when the thread’s message queue is empty. In your override
implementation, it is suggested that you call PeekMessagewith the PM_NOREMOVEflag to check for
the arrival of new messages in the queue. If a message arrives, return from this function to allow the
thread’s message processing to continue. The OnIdlemethod is declared as follows:

virtual BOOL OnIdle(LONG userCount);

By overriding the IsIdleMessagemethod when you also override the OnIdlemethod, you can prevent
OnIdlefrom being repeatedly called in response to recurring messages. This public method is
declared as shown here:

virtual BOOL IsIdleMessage(MSG *pMsg);

PreTranslateMessage

You can override the PreTranslateMessagevirtual function to handle a message before it is translated
from an accelerator to a command and dispatched to a window. Return TRUEfrom this method if you
do not want the message to be processed any more. This public method is declared as follows:

virtual BOOL PreTranslateMessage(MSG *pMsg);

ProcessMessageFilter

The ProcessMessageFiltervirtual function allows your application to catch messages that are trapped
by the MFC message hooks. Return TRUEif you process a message passed to your implementation.
In addition, call your class’s base class implementation to ensure that the message hook processing
continues as designed. This method is declared as follows:

virtual BOOL ProcessMessageFilter(int hookCode, LPMSG lpMsg);

ProcessWndProcException

Override the ProcessWndProcExceptionmethod to handle any MFC exceptions that have been trapped
from within the Windows message processing. This method is declared as shown here:

virtual LRESULT ProcessWndProcException(CException *xcp,


Äconst MSG *pMsg);

CWinApp

The CWinAppclass, derived from the CWinThreadclass, represents not only the program’s main
thread of execution, but also the application itself. As a result, there is only one CWinAppobject in
any MFC application.

Typically, you derive your application class from CWinApp. In addition, you would override the
InitInstanceand ExitInstancevirtual functions to provide your own initialization and termination support
(like creating and destroying your application’s main window). If you do override these functions,
remember to call your base class’s implementations, too.

Functions

There are several functions that you can call to obtain global application information. They are
AfxGetApp, AfxGetInstanceHandle, AfxGetResourceHandle, and AfxGetAppName.

AfxGetApp and AfxGetAppName

Use the AfxGetAppfunction to obtain the pointer to the executable’s CWinAppobject. Similarly, you
can use AfxGetAppNameto get the name of your MFC program. These functions are declared as
shown here:

CWinApp * AFXAPI AfxGetApp();


LPCTSTR AFXAPI AfxGetAppName();

Note:

The MFC application name is determined first by finding a string resource with an
ID equal to AFX_IDS_APP_TITLE. If a matching string resource does not exist, the
fully qualified executable filename is returned.

AfxGetInstanceHandle, AfxGetResourceHandle, and AfxSetResourceHandle

Use the AfxGetInstanceHandleto obtain a handle to the loaded executable file (EXE or DLL) within
which the current code is located. Use the AfxGetResourceHandleand AfxSetResourceHandlefunctions
to find and specify the location of the executable file that contains your bound resources. With these
functions, your strings and dialogs are not required to be located in the same executable file as your
code. By default, the resource handle for each module is set to be the handle of the executable file
that contains that code. These functions are declared as follows:

HINSTANCE AFXAPI AfxGetInstanceHandle();


HINSTANCE AFXAPI AfxGetResourceHandle();
void AFXAPI AfxSetResourceHandle(HINSTANCE hInstResource);

If you need to determine whether or not your code is in an EXE or a DLL, use the Boolean value
from the afxContextIsDLLmacro.

Registry Support

Applications often use the registry to store custom parameters that assist in the usability of the
software. For example, applications store items such as window positions, path names, and so on,
to allow them to be customized to the user’s preferences. If your application needs to store large
amounts of data, you are strongly encouraged to store this data in your own custom file, rather than
the registry.

For more information about using the registry, refer to Chapter 32, “Inside the Registry.”
SetRegistryKey

Use the SetRegistryKeymethod to inform MFC of the location (under HKEY_CURRENT_USER\Software)


at which to store all application profile data. You should use a key value that is unique and is
unlikely to conflict with other applications. For example, the trademarked name of your company is
an excellent choice. If you do not specify a registry key, all registry method calls will refer to a text-
based .INI file.

This public method is declared as follows in the CWinAppclass:

void SetRegistryKey(LPCTSTR lpszRegistryKey);


void SetRegistryKey(UINT nIDRegistryKey);

Note:

The second form of the SetRegistryKeymethod takes the ID of a string resource as a


parameter.

Tip:

After you’ve called SetRegistryKey, you can use the GetAppRegistryKeyand


GetSectionKeymethods to get the HKEYregistry handle directly. When finished with
the handle, you must call RegCloseKeyto release it.

GetProfileDataType and WriteProfileDataType

Use the GetProfileInt, WriteProfileInt, GetProfileString, WriteProfileString, GetProfileBinary, and


WriteProfileBinarymethods to store or retrieve key and value pairs in or from the registry (or an .INI
file, if no registry has been set via SetRegistryKey). When using these functions, use a unique name
(within your specified registry key) for your application as the “section”name.

These public methods are declared as follows:

UINT GetProfileInt(LPCTSTR lpszSection, LPCTSTR lpszEntry, int nDefault);


BOOL WriteProfileInt(LPCTSTR lpszSection, LPCTSTR lpszEntry, int nValue);
CString GetProfileString(LPCTSTR lpszSection, LPCTSTR lpszEntry,
LPCTSTR lpszDefault = NULL);
BOOL WriteProfileString(LPCTSTR lpszSection, LPCTSTR lpszEntry,
LPCTSTR lpszValue);
BOOL GetProfileBinary(LPCTSTR lpszSection, LPCTSTR lpszEntry,
LPBYTE* ppData, UINT* pBytes);
BOOL WriteProfileBinary(LPCTSTR lpszSection, LPCTSTR lpszEntry,
LPBYTE pData, UINT nBytes);

Document Support
For more information about MFC document and view support, refer to Chapter 7, “The Document/
View Architecture.”

AddDocTemplate

The AddDocTemplatemethod adds a document template to the list of documentemplates available to


the application. You would typically call this method in your override of the InitInstancemethod. This
method is declared as follows:

void AddDocTemplate(CDocTemplate *pTemplate);

GetFirstDocTemplatePosition and GetNextDocTemplate

The GetFirstDocTemplatePositionand GetNextDocTemplatemethods allow you to iterate through all the


document template objects (CDocTemplate) that currently are added to the application. If no
document templates are available, GetFirstDocTemplatePositionreturns NULL.

These public methods are declared as follows:

POSITION GetfirstDocTemplatePosition() const;


CDocTemplate *GetNextDocTemplate(POSITION&pos) const;

Note:

Because GetNextDocTemplateupdates the position value, always check that the


current position is not NULLbefore calling GetNextDocTemplate.

OpenDocumentFile

The OpenDocumentFilemethod opens a file representing a document. It creates a frame and view for
that document, based on matching the file extension to a registered document template. If the
document is already loaded, this method activates that frame and view. This public method is
declared as shown here:

virtual CDocument *OpenDocumentFile(LPCTSTR lpszFileName);

LoadStdProfileSettings and AddToRecentFileList

Use the LoadStdProfileSettingsand AddToRecentFileListmethods to initialize and manage your


application’s recent file list. Call LoadStdProfileSettingsin your InitInstanceoverride to add the list of
most recently used files to your application. Call AddToRecentFileListwhen you want to add another
file to the recent file list. These methods are declared thus:

void LoadStdProifleSettings(UNIT nMaxMRU = _AFX_MRU_COUNT);


virtual void AddToRecentFileList(LPCTSTR lpszPathName);
EnableShellOpen, RegisterShellFileTypes, and UnregisterShellFileTypes

Call the EnableShellOpenand RegisterShellFileTypesmethods in sequence to register your document


templates in the system registry. By default, the RegisterShellFileTypesmethod iterates through your
document templates and adds support for printing from the desktop shell (though the Print and
PrintTo keys) and associates an icon for each template type (via the DefaultIcon key). If
EnableShellOpenis called first, the RegisterShellFileTypesmethod will add support for opening the
document from the desktop shell. Call UnregisterShellFileTypesto remove all the registered
associations.

These methods are declared as follows:

void EnableShellOpen();
void RegisterShellFileTypes(BOOL bCompat=FALSE);
void UnregisterShellFileTypes();

Tip:

Make sure that you add all your application’s document templates (via
AddDocTemplate) before calling RegisterShellFileTypes.

Command-Line Parsing

Sometimes you’ll find it necessary to handle the command line passed to your application in a
standard Windows application way. Use the RunEmbedded, RunAutomated, ParseCommandLine, and
ParseShellCommandmethods to help your application respond properly to command-line options
passed to it.

RunEmbedded and RunAutomated

The RunEmbeddedand RunAutomatedmethods search for /Embeddingor /Automated(or the dash forms)
in the passed command line. If found, the appropriate option is removed from the command line and
TRUEis returned. Your program will receive these options if it is being launched as a server to an
automation client application. Refer to Chapter 12, “MFC OLE Servers,”for more information.

These public methods are declared as follows:

BOOL RunEmbedded();
BOOL RunAutomated();

ParseCommandLine and ProcessShellCommand

The ParseCommandLineand ProcessShellCommandmethods parse the command-line parameters and


perform the standard application actions (such as printing, DDE, and automation) for your
application. Use these methods in sequence in your application’s InitInstanceoverride. These public
methods are declared as follows:

void ParseCommandLine(CComandLineInfo&rCmdInfo);
BOOL ProcessShellCommand(CCommandLineInfo&rCmdInfo);
Tip:

If you need to handle additional options that could be passed from the command
line, derive a class from the CCommandLineInfoobject and override its
ParseParammethod.

CWnd

The CWndclass, derived from CCmdTarget, is the most fundamental GUI object class in MFC.
Instances of this class and derived classes are windows and have a system window handle (HWND)
associated with them.

From the Windows point of view, a window is an object that has a registered window procedure, and
consequently, can receive and handle system and window events. Most windows have a graphical
representation and many respond to user input. Examples of windows include main application
windows, dialogs, and controls (such as list boxes, pushbuttons, and static text fields).

NOTES on MFC Classes That Wrap System Handles

Several classes in MFC (such as CWnd, CDC, CPen, and CBrush, to name a few) provide wrappers
to their respective system handles (HWND, HDC, HPEN, and HBRUSH, respectively).

When using these classes, you must understand the relationship of the lifetime of objects of that
class and the lifetime of the associated system handles.

First, you can create the system handle for some classes, like CPen, in either a one- or two-step
process. To create this object in one step, simply use one of its nondefault constructors that takes
a style, width, and color parameter. To create a CPenobject in two steps, use its default
constructor and then call one of the CreatePenor CreatePenIndirectmethods.

Classes typically have a Createmethod. However, some classes can have completely different
names. For example, CFrameWndhas both a Createand a LoadFramemethod to accomplish this.

Likewise, some system handle wrapper classes require a two-step process to fully construct the
object. For example, if you construct a CWndclass, no system handle is created until you call the
Create(or similar) method on the class. Therefore, there is a two-step process for constructing
system handle wrapper objects.

First, when the MFC object has the system handle, that remains valid until the object is
destroyed—at which time the handle is returned to the system.

Second, if you happen to have a system handle for which you’d like an object constructed, use
the object’s Attachmethod to assign handle ownership to the object.

If you’d like to take ownership of the system handle from an MFC object, call its Detachmethod.
When this call is made, the handle is disconnected completely from the object and you are
responsible for releasing the handle back to the system.

Finally, if you have a system handle, you can look up and/or create a temporary instance of a
wrapper class. Most system handle wrapper classes provide a FromHandlefunction that returns an
instance of a class given a handle. If the handle does not have an object associated with it, a
temporary object is created.

The CWndclass and CImageListclasses also provide a FromHandlePermanentfunction that returns an


object only if one exists for the handle. This function does not create a temporary object.

Registering New Window Classes

You can use either AfxRegisterClassor AfxRegisterWndClassto register a new window class. In either
case, you can pass the registered window class’s name in a subsequent call to CWnd::Createto
create an instance of that window.

AfxRegisterClassis very similar to the Win32 RegisterClassAPI, except that if the class is registered
from within a DLL, that class is automatically unregistered when the DLL is unloaded.

The preferred window class registration function, AfxRegisterWndClass, returns a string containing the
generated class name.

These functions are prototyped as follows:

BOOL AFXAPI AfxRegisterClass(WNDCLASS *lpWndClass);


LPCTSTR AFXAPI AfxRegisterWndClass(UINT nClassStyle, HCURSOR hCursor = 0,
HBRUSH hbrBackground = 0, HICON hIcon = 0);

Obtaining an Application’s Main Window

The AfxGetMainWndfunction to get the pointer to the CWndobject that represents the main window for
your thread. Typically, it simply returns the m_pMainWndmember of the active CWinThreadobject. It is
prototyped as follows:

CWnd * AFXAPI AfxGetMainWnd();

Note:

Don’t assume that every CWinThreadobject in an application has the same


m_pMainWndobject. When a new CWinThreadobject is constructed, it “inherits”the
m_pMainWndpointer from the thread that created it. However, each thread can
change this pointer at any time.

Creation and Use

Like other system handle wrapper classes, CWndprovides many of the standard mechanisms for
creating system handles or attaching existing ones. The methods supported are Create, CreateEx,
CreateControl, FromHandle, FromHandlePermanent, Attach, and Detach.

Create and CreateEx

The Createand CreateExmethods map very closely to the Win32 APIs CreateWindowand
CreateWindowEx. Like the Win32 APIs, the difference between the two is the ability to specify
extended window styles in the extended version. These methods are declared as follows:

virtual BOOL Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName,


DWORD dwStyle, const RECT&rect, CWnd *pParentWnd,
UINT nID, CCreateContext* pContext = NULL);
BOOL CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
ÄLPCTSTR lpszWindowName,
DWORD dwStyle, int x, int y, int nWidth, int nHeight,
HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam = NULL);
BOOL CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
ÄLPCTSTR lpszWindowName,
DWORD dwStyle, const RECT&rect, CWnd *pParentWnd,
ÄUINT nID,
LPVOID lpParam = NULL);

CreateControl

Use the CreateControlmethod to instantiate an ActiveX control and associate it with the CWndobject.
Specify either the control’s ProgID or CLSID for the class name. The various forms of this method
are declared as follows:

BOOL CreateControl(REFCLSID clsid, LPCTSTR pszWindowName, DWORD dwStyle,


const RECT&rect, CWnd* pParentWnd, UINT nID, CFile* pPersist=NULL,
BOOL bStorage=FALSE, BSTR bstrLicKey=NULL);
BOOL CreateControl(LPCTSTR pszClass, LPCTSTR pszWindowName, DWORD dwStyle,
const RECT&rect, CWnd* pParentWnd, UINT nID, CFile* pPersist=NULL,
BOOL bStorage=FALSE, BSTR bstrLicKey=NULL);
BOOL CreateControl( REFCLSID clsid, LPCTSTR pszWindowName, DWORD dwStyle,
const POINT* ppt, const SIZE* psize, CWnd* pParentWnd, UINT nID,
CFile* pPersist = NULL, BOOL bStorage = FALSE, BSTR bstrLicKey = NULL );

FromHandle and FromHandlePermanent

The FromHandleand FromHandlePermanentstatic functions look up and return an existing CWndobject


based upon a passed system window handle (HWND). If a matching CWndobject does not exist, the
FromHandlefunction creates a temporary CWndobject and attaches the specified handle to it. These
functions are prototyped as shown here:

static CWND * PASCAL FromHandle(HWND hWnd);


static CWND * PASCAL FromHandlePermanent(HWND hWnd);

Attach and Detach

You can associate and unassociate a system window handle (HWND) with a CWndobject using the
Attachand Detachmethods. After you’ve attached the handle to the object, the object is responsible
for releasing the handle back to the system, unless you detach the handle. These methods are
declared as follows:
BOOL Attach(HWND hWndNew);
HWND Detach();

ExecuteDlgInit

The ExecuteDlgInitmethod creates a dialog window based upon the specified dialog resource. Use
this function if you need to load a window from a dialog resource. It is recommended that you try to
use the CDialogclass first, however. This method is declared as shown here:

BOOL ExecuteDlgInit(LPCTSTR lpszResourceName);


BOOL ExecuteDlgInit(LPVOID lpResource);

PreCreateWindow

The PreCreateWindowvirtual function is called before a window gets created. You can override this
function if you need to modify the CREATESTRUCTof a window before it gets created. If you need to
terminate the construction of a window, return FALSE. This function is declared as follows:

virtual BOOL PreCreateWindow(CREATESTRUCT&cs);

Subclassing Windows

Briefly, subclassing a window is the process of hooking its window procedure to change how that
window receives and responds to specific window events. You can subclass a window to give that
control a different look and feel. Subclassing windows is described in detail in Chapter 5, “Custom
Control Development.”The methods that support subclassing are SubclassWindow, SubclassDlgItem,
UnsubclassWindow, and PreSubclassWindow.

SubclassWindow, SubclassDlgItem, and UnsubclassWindow

The SubclassWindowand SubclassDlgItemmethods provide an easy mechanism to hook into the


window procedure of another window. After the window has been subclassed, you can respond to
system and window messages passed to that control’s window procedure via the MFC message
map mechanism. The UnsubclassWindowmethod returns the subclassed window to its original state.

These methods are declared as follows:

BOOL SubclassWindow(HWND hWnd);


BOOL SubclassDlgItem(UINT nID, CWnd *pParent);
HWND UnsubclassWindow();

PreSubclassWindow

Your CWnd-derived object receives the PreSubclassWindownotification prior to being subclassed. If


you choose to override this virtual function, your object can perform whatever operations it needs to
do prior to being subclassed. This function is declared as follows:

virtual void PreSubclassWindow();

GetSafeHwnd

Use the GetSafeHwndmethod to obtain the window handle attached to the window object. It is called
“safe”because it returns NULLif the thispointer of the CWndobject is equal to NULL. Additionally, this
method returns NULLif the CWndobject is not attached to a window, or is attached to a NULLwindow
handle (usually meaning the desktop window). This public method is declared as follows:

HWND GetSafeHwnd() const;

GetStyle, GetExStyle, ModifyStyle, and ModifyStyleEx

Use the GetStyle, GetExStyle, ModifyStyle, and ModifyStyleExmethods to obtain and change the style
and extended style flags for a window. These public methods are declared thus:

DWORD GetStyle() const;


DWORD GetExStyle() const;
BOOL ModifyStyle(DWORD dwRemove, DWORD dwAdd, UINT nFlags = 0);
BOOL ModifyStyleEx(DWORD dwRemove, DWORD dwAdd, UINT nFlags = 0);

GetOwner and SetOwner

Use the GetOwnerand SetOwnermethods to obtain and modify the owner of the current window.
Some controls, such as CToolBar, send notification messages to their owner windows. If the window
has no owner, the parent becomes its owner. Unlike the parent/child relationships, owner/ownee
relationships between windows do not limit the drawing area for the owned window. These public
methods are declared as follows:

CWnd *GetOwner() const;


(e)void SetOwner(CWnd *pOwnerWnd);

ToolTip Support

ToolTips are the small yellow textual pop-ups that appear when the mouse pointer is positioned
over user interface elements. You can control their appearance using the methods described in the
following sections.

EnableToolTips and OnToolHitTest

Use the EnableToolTipsmethod to turn on or off the display of ToolTips for the current window. You
can override the OnToolHitTestvirtual function to control the location and positioning of the ToolTip
message. These methods are declared as shown here:

BOOL EnableToolTips(BOOL bEnable);


virtual int OnToolHitTest(CPoint point, TOOLINFO *pTI) const;

UpdateData

You can override the UpdateDatavirtual function to either initialize data into the window’s child
controls or validate and save the data. If the bSaveAndValidateparameter is equal to TRUE, your
function must validate and save the child controls’data. Otherwise, you must initialize the child
controls from their appropriate data source.

This public virtual function is declared as follows:


BOOL UpdateData(BOOL bSaveAndValidate = TRUE);

This method is the primary mechanism for Dialog Data Validation (DDV) and Dialog Data Exchange
(DDX). For more information, refer to the section “Dialog Data Exchange”section in Chapter 2,
“MFC Dialogs, Controls, and Data Interaction.”

UpdateDialogControls

Use the UpdateDialogControlsmethod to disable child controls, menu items, and toolbar buttons if no
handler exists for them. This method is declared as follows:

void UpdateDialogControls(CCmdTarget *pTarget, BOOL bDisableIfNoHandler);

CenterWindow

The CenterWindowmethod centers a window relative to another window. By default, it centers the
window relative to its parent. If the window is owned, it centers the window relative to its owner. This
public method is declared as follows:

void CenterWindow(CWnd *relativeTo = NULL);

RunModalLoop, ContinueModal, and EndModalLoop

Use the RunModalLoop, ContinueModal, and EndModalLoopmethods to make the specified window
modal. RunModalLoopplaces the window into a modal mode. The window remains modal until your
application calls EndModalLoop, which causes ContinueModalto return FALSEand the modal loop to be
ended. These methods are declared as follows:

int RunModalLoop(DWORD dwFlags = 0);


virtual BOOL ContinueModal();
virtual void EndModalLoop(int nResult);

Win32 Methods

As an experienced Win32 programmer, you are probably familiar with many of the functions and
windows messages. Many of the methods in the CWndclass map (more or less) directly to the
Win32 API functions of the same name (but without the HWNDparameter, of course). Others map to
some of the standard notification messages. Please consult documentation on each of these Win32
API functions and messages for more information.

Arranged by functional category, these methods are as follows:

Message Handling

SendMessage, PostMessage, IsDialogMessage, SendNotifyMessage


Window Text

SetWindowText, GetWindowText, GetWindowTextLength, SetFont, GetFont


Menu Support

GetMenu, SetMenu, DrawMenuBar, GetSystemMenu, HiliteMenuItem


Window Size and Positioning

IsIconic, IsZoomed, MoveWindow, SetWindowRgn, GetWindowRgn, SetWindowPos,


ArrangeIconicWindows, BringWindowToTop, GetWindowRect, GetClientRect, GetWindowPlacement,
SetWindowPlacement
Coordinate Mapping

ClientToScreen, ScreenToClient, MapWindowPoints


Painting

BeginPaint, EndPaint, GetDC, GetWindowDC, ReleaseDC, UpdateWindow, SetRedraw,


GetUpdateRect, GetUpdateRgn, Invalidate, InvalidateRect, InvalidateRgn, ValidateRect, ValidateRgn,
ShowWindow, IsWindowVisible, ShowOwnedPopups, GetDCEx, LockWindowUpdate,
UnlockWindowUpdate, RedrawWindow, Print, PrintClient
Timer

SetTimer, KillTimer
Window Styles

IsWindowEnabled, EnableWindow, GetActiveWindow, SetActiveWindow, SetForegroundWindow,


GetForegroundWindow, GetCapture, SetCapture, GetFocus, SetFocus, GetDesktopWindow
Child Window

GetDlgCtrlID, SetDlgCtrlID, GetDlgItem, CheckDlgButton, CheckRadioButton,


GetCheckedRadioButton, DlgDirList, DlgDirListComboBox, DlgDirSelect, DlgDirSelectComboBox,
GetDlgItemInt, GetDlgItemText, GetNextDlgGroupItem, GetNextDlgTabItem, IsDlgButtonChecked,
SendDlgItemMessage, SetDlgItemInt, SetDlgItemText
Scrolling

GetScrollPos, GetScrollRange, ScrollWindow, SetScrollPos, SetScrollRange, ScrollWindowEx,


GetScrollInfo, SetScrollInfo, GetScrollLimit, ShowScrollBar, EnableScrollBarCtrl, GetScrollBarCtrl
Z-order and Location

ChildWindowFromPoint, FindWindow, GetNextWindow, GetTopWindow, GetWindow,


GetLastActivePopup, IsChild, GetParent, SetParent, WindowFromPoint, DestroyWindow
Alert

FlashWindow, MessageBox
Clipboard

ChangeClipboardChain, SetClipboardViewer, OpenClipboard, GetClipboardOwner,


GetClipboardViewer, GetOpenClipboardWindow
Caret

CreateCaret, CreateSolidCaret, CreateGrayCaret, GetCaretPos, SetCaretPos, HideCaret, ShowCaret


Shell Interaction

DragAcceptFiles
Icon

SetIcon, GetIcon
Help

GetWindowContextHelpId, SetWindowContextHelpId
CFrameWnd

The CFrameWndclass, derived from CWnd, is a window that contains the title bar, system menu,
border, minimize/maximize buttons, and an active view window. The CFrameWndclass supports the
single document interface (SDI).

For multiple document interface (MDI) frame windows, use CMDIFrameWndfor the workspace frame
and CMDIChildWndfor the MDI child windows. Both of the aforementioned classes are derived from
CFrameWnd.

For the thinner, toolbox-style frame window, use the CMiniFrameWndclass. To support in-place
editing, use the COleIPFrameWndclass.

Functions

The CFrameWndclass provides several functions that allow you to obtain the active document, view,
and frame. In addition, there are functions to interact with the title bar and status bar text. These
functions are described in this section.

GetActiveDocument, GetActiveView, SetActiveView, and GetActiveFrame

The GetActiveDocument, GetActiveView, and GetActiveFramemethods return a pointer to the current


document, view, or frame, respectively. The SetActiveViewmethod activates a view window. These
methods are declared as follows:

virtual CDocument *GetActiveDocument();


virtual CFrameWnd *GetActiveFrame();
CView *GetActiveView() const;
void SetActiveView(CView *pViewNew, BOOL bNotify = TRUE);

GetTitle and SetTitle

The GetTitleand SetTitlemethods obtain and set the text in the frame window’s title bar. These
methods are declared as shown here:

CString GetTitle() const;


void SetTitle(LPCTSTR lpszTitle);

SetMessageText

The SetMessageTextmethod sets the status bar text (in pane zero of the status bar). You can specify
either a string or a string resource identifier. The variations of this method are declared as follows:

void SetMessageText(LPCTSTR lpszText);


void SetMessageText(UINT nID);

BeginModalState, EndModalState, and InModalState

The BeginModalState, EndModalState, and InModalStatemethods control the modality of the frame
window. Use BeginModalStateto enter the modal state, ExitModalStateto end the modal state, and
InModalStateto determine your current state. These methods are declared as follows:

virtual void BeginModalState();


virtual void EndModalState();
BOOL InModalState() const;

CView

The CViewclass, derived from CWnd, is responsible for displaying/printing and handling all user
interactions for the document attached to it. You need to derive classes from CViewto present your
document’s data to the user in the required ways.

When you create a CView-derived class, at the minimum, you need to override the OnDrawand
OnUpdatemethods.

Methods

The CViewclass provides methods that you can override to allow your application to respond to
external events (such as redraw requests, window activation, and document changes). Additionally,
there is a method to return a pointer to the document associated with the view.

OnDraw

Override the OnDrawmethod to draw your document view to the passed device context. This method
handles drawing both to the display and a printer. If you need to have printer versus screen drawing
logic within this method, call CDC::IsPrintingto determine the current state. The OnDrawmethod is
declared as shown here:

virtual void OnDraw(CDC *pDC) = 0;

OnUpdate

Override the OnUpdatemethod to allow your view class to be notified when the document has
changed. This method is invoked when CDocument::UpdateAllViewsis called or when the view is
initially attached to the document, but before it is displayed.

When you receive this notification, do not redraw the changes directly. You can cause the changes
to be redrawn by calling CWnd::InvalidateRect.

The OnUpdatemethod is declared as follows:

virtual void OnUpdate(CView *pSender, LPARAM lHint, CObject *pHint);

GetDocument

The GetDocumentmethod returns the document to which this view is attached. It is declared as
follows:

CDocument *GetDocument() const;

OnActivateVie and OnActivateFrame


Override the OnActivateViewand OnActivateFrameif your view needs to respond in some way to its
activation or deactivation. These virtual functions are declared as follows:

virtual void OnActivateView(BOOL bActivate, CView *pActivateView,


CView *pDeactivateView);
virtual void OnActivateFrame(UINT nState, CFrameWnd *pFrameWnd);

CDocument

How Are Documents Related to Views?

With MFC, you can partition your application into a document and one or more views of that
document. A documentcontains the data that your application works with. A viewis a graphical
representation of that data. Hence, all the views reference the same document. If a change is
made to the document from one view, all the other views get updated with that change.

An example of a document could be a set of data that correlates the names of cities with a value
representing each city’s population.

You could have several types of views of this same data. One view could be a text list that allows
the user to sort itself by either city name or by population. Another view could display a map that
puts the population value next to the appropriate city name. Yet another view could be the same
as the first view text list, except scrolled down to a different location in the list. So, in this case,
you have three total view instances of two view types.

To create a document, derive a class that represents your data from the CDocumentclass. To
create a view type, derive a class from the CViewor a CView-derived class.

For more information about MFC document and view support, refer to Chapter 7.

GetTitle and SetTitle

The GetTitleand SetTitlemethods obtain and set the text in the frame window’s title bar for all the
views attached to this document. These methods are declared as shown here:

const CString&GetTitle() const;


virtual void SetTitle(LPCTSTR lpszTitle);

GetPathName and SetPathName

The GetPathNameand SetPathNamemethods obtain and set the fully qualified path associated with the
document. You only need to call these methods if you are also overriding OnOpenDocumentand
OnSaveDocument. The GetPathNameand SetPathNamemethods are declared as shown here:

const CString&GetPathName() const;


virtual void SetPathname(LPCTSTR lpszPathname, BOOL bAddToMRU = TRUE);

GetDocTemplate
The GetDocTemplatemethod returns the CDocTemplateobject upon which this document is based. If
this document does not have a document template associated with it, NULLis returned. This method
is declared as follows:

CDocTemplate *GetDocTemplate() const;

IsModified, SetModifiedFlag, and SaveModified

The IsModified, SetModifiedFlag, and SaveModifiedmethods are used to control the modification state
within the document object. Call SetModifiedFlagwhenever you make a change to your document. To
determine the current modification state, call IsModified. If you want to prompt the user about saving
the document modification in a way different from the default implementation, override the
SaveModifiedmethod.

These methods are declared as shown here:

virtual BOOL IsModified();


virtual void SetModifiedFlag(BOOL bModified = TRUE);
virtual BOOL SaveModified();

AddView, RemoveView, and OnChangedViewList

Call the AddViewand RemoveViewmethods to attach and detach a CView-derived object to and from
this document. Each successful call to AddViewand RemoveViewresults in a call to OnChangedViewList.
The default implementation of OnChangedViewListdeletes the document when the last view is
detached. These public methods are declared as shown here:

void AddView(CView *pView);


void RemoveView(CView *pView);
virtual void OnChangedViewList();

GetFirstViewPosition and GetNextView

The GetFirstViewPositionand GetNextViewmethods enable you to iterate through all the view objects
(CView) that currently are associated with the document. If no views are available,
GetFirstViewPositionreturns NULL.

These public methods are declared as follows:

virtual POSITION GetFirstViewPosition() const;


virtual CView *GetNextView(POSITION&pos) const;

Note:

Because GetNextViewupdates the position value, always check that the current
position is not NULLbefore calling GetNextViewagain.
UpdateAllViews

Use the UpdateAllViewsmethod to have the CView::OnUpdatemethod to be called on one or all views
associated with this document. If the pSenderparameter is equal to NULL, the update is broadcast to
all views associated with this document. This method is declared as shown here:

void UpdateAllViews(CView *pSender, LPARAM lHint = 0, CObject


Ä*pHint = NULL);

DeleteContents

Override the DeleteContentsmethod to delete your document’s data before the document is
destroyed or reused. Because SDI applications have only one document, it is reused.
Consequently, it is important to delete your document’s data in this method. This method is declared
as follows:

virtual void DeleteContents();

OnNewDocument, OnOpenDocument, OnSaveDocument, and OnCloseDocument

Override the OnNewDocument, OnOpenDocument, OnSaveDocument, and OnCloseDocumentvirtual


functions to respond to each of the new, open, save, and close events for the document.

Tip:

Because a single document object is used in SDI applications, you must initialize it
by overriding the OnNewDocumentmethod.

These functions are declared as follows:

virtual BOOL OnNewDocument();


virtual BOOL OnOpenDocument(LPCTSTR lpszPathName);
virtual BOOL OnSaveDocument(LPCTSTR lpszPathName);
virtual void OnCloseDocument();

ReportSaveLoadException

Override the ReportSaveLoadExceptionvirtual function if you need to provide custom error reporting
when an exception is caught while saving or loading the document. This virtual function is declared
as shown here:

virtual void ReportSaveLoadException(LPCTSTR lpszPathName,


CException *e, BOOL bSaving,
UINT nIDPDefault);

GetFile and ReleaseFile

Override the GetFileand ReleaseFilemethods to provide a custom mechanism for opening and closing
the document data file. These methods are declared as follows:

virtual CFile *GetFile(LPCTSTR lpszFileName, UINT nOpenFlags,


CFileException *pError);
virtual void ReleaseFile(CFile *pFile, BOOL bAbort);

CanCloseFrame and PreCloseFrame

The CanCloseFrameand PreCloseFramemethods give you an opportunity to handle the closing of


document views contained in frame windows. Override the CanCloseFramevirtual function if you need
to provide special handling when frame windows are closed. The default implementation prompts
the user to save the document upon closing the last frame. Override the PreCloseFramevirtual
function to provide custom handling when a frame window containing views associated with a
document is closed. These methods are declared as shown here:

virtual void CanCloseFrame(CFrameWnd *pFrame);


virtual void PreCloseFrame(CFrameWnd *pFrame);

Summary

This chapter covers the fundamental architectural classes involved in almost every MFC-based
application. You will incorporate all these classes either directly or indirectly in every MFC GUI
application that you write.

The CObjectclass is the root of all other MFC classes and provides general methods that are useful
to classes derived from it. The runtime type information allows each class to identify its type. The
serialization support provides a mechanism for persisting the object to any byte stream. Finally, the
diagnostic support provides a standard way of dumping the contents of an object and ensuring that
the object is in a valid state.

The CCmdTarget, CWinThread, and CWinAppclasses are a line of related classes in the MFC class
hierarchy that form the basis of your GUI application. These classes allow your application to
receive and process GUI events. Additionally, these classes provide the general framework for the
controller part of the object-oriented model/view/controller (MVC) paradigm.

The object-oriented model and view classes are supported via MFC’s document/view architecture
and the CDocumentand CViewclasses, respectively. When you derive from each of these classes,
you can separate your application’s business logic from its display logic.

Finally, the document views are displayed in GUI windows that are represented by the
CFrameWndand CWndclasses. The windowing classes are the fundamental objects that manage
your GUI application and present its data to the user.
Chapter 2
MFC Dialogs, Controls, and Data Interaction

by Bill Heyman

In This Chapter

• Creating an Application 50
• Modifying the Application 57
• ToolTips 71
• Dialog Data Exchange 72
• Using Standard Dialog Boxes 74

This chapter describes the general techniques for creating dialog-based MFC applications using Microsoft
Visual Studio. If you have already created MFC applications using AppWizard, you are probably already familiar
with this material and might want to move to the more in-depth topics in later chapters.

Creating an Application

This section describes the MFC AppWizard supplied by Visual Studio. It leads you through the process of
generating a new dialog-based MFC application and describes the basic features of the generated code.

Starting and Using MFC AppWizard

If you want, you can certainly derive your own classes from CWinApp and create your own MFC application from
scratch. However, you can save yourself much effort by using the AppWizard, which automatically creates a lot
of the common application code for you. The AppWizard gives you a jump start on getting into the actual coding
of the business logic necessary for your application—without having to worry about the MFC infrastructure
issues initially.

Starting the AppWizard is easy. In Microsoft Visual Studio, simply choose File, New from the menu. This
displays the dialog shown in Figure 2.1, which gives you a choice of new projects to create.
Figure 2.1 The Projects tab of the New dialog displaying a list of the types of projects that you can create.

Because you want to create an MFC application, choose the project type entry named MFC AppWizard (exe). In
the Location entry field, type the parent directory to contain this project (in Figure 2.1, e:\MyProjects was typed).
Finally, in the Project Name, type the name of your project (in the Figure 2.1, MFCSample was typed). When you
type the project name, the project location is automatically extended to have a subdirectory with the name of the
project (in the Figure 2.1, this is how E:\MyProjects\MFCSample was created).

Note:

Among the project types, you could also choose MFC AppWizard (dll). Choose this AppWizard
if you want to package your MFC application in a dynamic link library to be used by other
applications.

You might also note that the Create New Workspace radio button is automatically selected for you. A project
contains all of the file references and compile and link options to allow you to create a single executable (EXE)
or dynamic link library (DLL). When you have a more sophisticated application that has multiple DLLs and
EXEs, you can associate all of your projects in an entity called a workspace. In the case of MFCSample, you
are only creating a single executable—so a single workspace is appropriate. Choose OK to move to the start of
the MFC AppWizard.

Note:

You can add a project to an existing workspace at a later time, by opening that workspace.
From the FileView tab, select the Workspace entry (on the top line), and right-click to display the
context menu. Choose Insert Project into Workspace to display a dialog that allows you to
select the project to add to the workspace.

Also, if you need to logically associate your EXE and DLL projects in multiple groupings, you
can add your projects to more than one workspace.

The MFC AppWizard generates the initial code for your application, so you don’t have to code it by hand.
Consequently, it guides you through the process of defining the general characteristics of your application, so it
can generate code suited to your application’s needs. Figure 2.2 shows the MFC AppWizard - Step 1 dialog
box.

The MFC AppWizard - Step 1 dialog box enables you to select whether your application uses the Single
Document Interface (SDI), Multiple Document Interface (MDI), or is dialog-based. In addition, you choose
whether or not your application uses the MFC document/view architecture, which separates the logic that
displays your data to the user from logic that interacts with your data. Refer to Chapter 7, “The Document/View
Architecture,” for more specific information on using the MFC document/view architecture. Because the
document/view architecture is described later (and to keep things simple), the MFC Sample application is a
dialog-based application. The document/view architecture is not available to dialog-based applications; hence,
that check box is disabled. Click Next to proceed to the next MFC AppWizard step.

Figure 2.2 The first MFC AppWizard dialog box enables you to specify whether your application is a single
document, multiple documents, or dialog-based.
SDI, MDI, and Dialog-Based Applications

A Single Document Interface (SDI) application is like the Paint accessory shipped with Windows. It has a
single client area and often has a pull-down menu and toolbar. Use an SDI application when each instance of
an application interacts with a single file or data and it displays data within a client area.

A Multiple Document Interface (MDI) application is like Microsoft Word. It has multiple client areas, but often
has a single pull-down menu and toolbar. Use an MDI application when you want the user to have multiple
documents open within the same application.

A dialog-based application is similar to the AppWizard within Visual Studio. It consists of a single form-based
window with any of the various Windows controls, such as pushbuttons, list boxes, and entry fields. Use a
dialog-based application when you do not need a client area in which to draw in your application or when your
application is form-based and needs only the various Windows controls to interact with the user.

The MFC AppWizard - Step 2 of 4 dialog enables you to specify some of the features of the generated code.
These include whether or not your application has an About box, WinSock capabilities, and ActiveX controls,
and whether it supports automation. In addition, you can specify text that appears in your dialog’s title bar. This
dialog is shown in Figure 2.3. Click Next to move to the next AppWizard step.

Figure 2.3 The Step 2 MFC AppWizard dialog enables you to add an About box and specify title bar text for
your application.

Figure 2.4 shows the MFC AppWizard - Step 3 of 4 dialog. Using this dialog, you can specify whether you’d like
your application to appear as an MFC Standard application or as a Windows Explorer-like application, with a
tree view in one child window and a list view in another. In addition, you can indicate whether you’d like to link
the MFC class library statically as part of your application—or have your application use the MFC dynamic link
library. Click Next to move to the final step.
Figure 2.4 The Step 3 MFC AppWizard dialog allows you to choose to make your application appear like
Windows Explorer.

The MFC AppWizard - Step 4 of 4 dialog is informational and requires no input from you. It lists the classes and
source files that will be generated for your application. For the MFCSample project, Figure 2.5 shows the
classes and files that are generated. Click Finish to complete the AppWizard process.

Note:

Remember that if you link MFC as a DLL, your application’s installation routines must install the
appropriate MFC DLLs needed by your application.
Figure 2.5 The Step 4 MFC AppWizard dialog shows you the classes and source files that AppWizard will
generate for your application.

The AppWizard-Generated Code

After AppWizard has generated code for your MFC application, your current project will be loaded into the
Visual Studio environment. You might be interested to know that, without any modification, you can compile and
run this application. Of course, without the logic that you need to add, this application won’t do anything specific.

On the left side of the Visual Studio environment, there are three tabs containing different views of your
application: FileView, ClassView, and ResourceView.

The FileView Tab

The rightmost tab, FileView, is shown in Figure 2.6 for the MFCSample application. There are five source files
that are used to contain the definitions for the primary application classes. These files are MFCSample.cpp and
MFCSampleDlg.cpp. The description of these files’contents is discussed with the ClassView tab later in this
section.
Figure 2.6 The FileView tab in Visual Studio for MFCSample.

Other files of interest include MFCSample.rc and Resource.h. The MFCSample.rc file contains the binary
resource definitions of the icons, string table, version information, and dialogs for the application. Additionally, a
file named Resource.h accompanies this file to define the numeric ids for each of the resources. The actual
resources are discussed with the ResourceView tab later in this section.

Some of the more intriguing files that AppWizard generates for every MFC application are StdAfx.cpp and
StdAfx.h. These are common files that are used to improve compile-time performance for MFC applications.
Specifically, StdAfx.h is included by every source file first and contains the #includelines for each of the MFC
class library headers. If you do this, this header file (and all of the MFC headers) can be parsed and
precompiled into a binary form. Consequently, when subsequent source files are compiled, the compiler does
not have to read in and parse all of the system header files. Thus, each of the source files can be compiled
much faster.

Caution:

Although each project can contain files of the same name (StdAfx.h and Resource.h), these
files are likely to have different contents, particularly if the AppWizard options are different
between projects.

The ClassView Tab

The leftmost tab, ClassView, for the MFCSample application is shown in Figure 2.7. The class view of your
application lists all of the classes in it, as well as all of the methods and data members contained within each
class. For the MFCSample application, three classes are generated: CAboutDlg, CMFCSampleApp, and
CMFCSampleDlg.
Figure 2.7 The ClassView tab for MFCSample in Visual Studio.

The primary application class that AppWizard generated is named CMFCSampleApp. Declared in MFCSample.h
and defined in MFCSample.cpp, this class is derived from CWinAppand represents the main class for your MFC
application. See Chapter 1, “The MFC Architecture,”for more information on the CWinAppclass.

The CMFCSampleDlgclass represents the main window of the dialog-based MFCSample application. The
CMFCSampleDlgclass is derived from the CDialogclass, and you can modify it to add the data and logic required
for your application. This class is associated with a generated dialog resource, named
IDD_MFCSAMPLE_DIALOG, which you can edit via the ResourceView tab. The CMFCSampleclass is declared in
MFCSampleDlg.h and defined in MFCSampleDlg.cpp.

Likewise, the CAboutDlgclass represents the About box in your application. You can add methods and data to
this class if you need to modify the look and feel of the modal window that is displayed when Help, About is
selected from your application’s pull-down menu. The CAboutDlgclass is derived from CDialogand is associated
with a generated dialog resource, named IDD_ABOUTBOX. The CAboutDlgclass declaration is located in
MFCSampleDlg.h and its definition is located in MFCSampleDlg.cpp.

The ResourceView Tab

The final tab available for browsing your application in the Visual Studio environment is the ResourceView tab.
Shown in Figure 2.8, this tab enables you to browse and edit the binary resources associated with your
application. The actual resources are contained in the file MFCSample.rc, and the numeric identifiers for the
resources are stored in a file named Resources.h.

Figure 2.8 The ResourceView tab for MFCSample in Visual Studio.


The MFC AppWizard generated four different types of resources for your application: dialog, icon, string table,
and version. Each of these types has one or more instances of that type of resource.

The dialog resource contains the layout and controls contained on your application’s dialog windows. MFC
AppWizard generated two dialog resources for the MFC Sample application, named IDD_ABOUTBOXand
IDD_MFCSAMPLE_DIALOG, representing the About box and the main window, respectively. By double-clicking on
the dialog’s name (such as IDD_ABOUTBOX), you can edit and make changes to the layout of and controls within
that dialog. By default, the About box dialog contains a generic icon, application name, and copyright string, and
the main window dialog contains an OK and a Cancel pushbutton. You will certainly need to edit both of these
dialogs before shipping your application.

MFCAppWizard also generated a graphical icon resource named IDR_MAINFRAMEfor the MFCSample
application. The IDR_MAINFRAMEicon is the image that appears in your application’s upper-left hand corner of
the main frame, in its About box, and is associated with your executable.

The next resource type is the string table resource, which maps numeric identifiers to language-dependent
strings. If you double-click on the string table resource, you can see that the MFC AppWizard generated a single
string that can be used in a menu resource

Note:

It is a good idea to isolate all of your language-dependent strings into a string resource table. In
this way, your application code refers only to numeric identifiers, rather than literal strings. If you
use string resources, when you need to translate your application to another language, the
string tables can be translated to that language without change to your source code.

Finally, the MFC AppWizard generated a version resource named VS_VERSION_INFOfor the MFCSample
application. The version information contains both standard numeric information (such as a four-part version
number, like 1.0.0.1, and an operating system flag on which the application can run, such as
VOS__WINDOWS32) and standard and user-definable string information (such as an application description and
copyright string). Utilities such as installation programs can use this information to compare one executable file
to another to decide whether they need to replace an older version with a new one, for example.

Modifying the Application

At this point, you have a shell MFC dialog-based application called MFCSample. Of course, it contains no
business logic, and the dialog is pretty boring with the lonely OK and Cancel buttons. If you want, you can run
and debug it from the Visual Studio environment. Next, you can starting doing something interesting—like
adding more controls and actually having the application do something.

Adding Dialog Controls

Certainly the first thing that you need to do is to add controls to your dialog. Otherwise, you cannot add code to
handle and trigger events for the controls. Consequently, you’ll add a few basic controls to the MFCSample
application to demonstrate some of the features of dialog-based MFC applications.

This chapter glosses over the actual functionality of the controls it describes and defers the in-depth analysis of
control events and behavior to Chapter 3, “The Windows Common Controls.”However, this chapter does
introduce you to the look and feel of the controls that you can add to your application.

In the MFCSample application, there are simply some pushbuttons that demonstrate some of the standard
dialogs available to you as well as some entry fields and static text fields to demonstrate Dialog Data Exchange
(DDX). The MFCSample main window is shown in Figure 2.9.
Figure 2.9 The MFCSample main window containing pushbuttons and other controls.

Dialog Control Types

A controlis a specialized GUI object that can allow passive or active user interaction with the application.
Controls usually serve a limited number of purposes and provide an intuitive means of the user interacting with
those purposes. Controls can be sorted in three basic categories: information presentation, information request,
and information modifiers. Some controls can be classified in only one category, whereas others could be
classified in more than one category.

An information presentation type of control simply displays data to the user. Perhaps the most basic information
presentation controls are the static text and the picture controls. Other controls such as the tree control and the
list box can be classified in the other two categories—but, in any case, still present information to the end user.

The second type of control is the information request control, which enables the user to obtain more information
from the application. The most basic control serving this purpose is the button control. However, depending on
how the application is written, a tree control and list box could be in this category also; namely, where an
application adds more data to the tree control when a node is expanded and a list box item is double-clicked.

Finally, the last type of control is the information modifier, which enables the user to change the application’s
data. The edit box, radio button, and check box are the most basic controls that serve this purpose.
Alternatively, the list control can be used to serve this purpose if it allows data to be edited within its cells.

Furthermore, three classifications of Windows controls are available to you through the Visual Studio resource
editor palette. These are the standard controls, the common controls, and the custom controls.

Standard Controls

The standard controls are the classic controls that have been a part of Windows since its earliest days. These
are the most basic controls that all Windows users know and love. These include buttons, list boxes, check
boxes, radio buttons, and scrollbars. In the subsequent sections, the behavior and use of each of these controls
is discussed briefly. Again, refer to Chapter 3 for a more in-depth discussion of the methods and events for each
of these controls that are programmatically available to your application.

Picture

The picture control represents a set of information presentation controls available to your application. This
control can contain an icon, a bitmap, a metafile, a rectangular outline (called a frame), or a filled rectangle,
depending on the style flag set at design time. Use this control to display an image or draw a filled or outlined
rectangle in your dialog. Figure 2.10 shows an example of a picture control with the default MFC icon in it.
Figure 2.10 A picture control with the default MFC icon in it.

Static Text

The static text control is another information presentation control. It simply displays a line of text to the
user—however, the user typically does not directly interact with this text in any way. This control simply displays
text to the user. Figure 2.11 shows an example of a static text control.

Figure 2.11 A static text control with an output-only message in it.

Edit Box

The edit box control is very similar to the static text control, except that the user can actually edit the textual data
contained within it. Use this type of control when you want the user to edit a single line or multiple lines of
textual data. It is considered to be both an information presentation and an information modifier type of control.
Figure 2.12 shows an example of an edit box control.

Figure 2.12 An edit box control just waiting for its text to be modified.

Group Box

The group box control is an information presentation control that provides simply a way of grouping other
controls. Group boxes are often used to contain a set of radio buttons that provide a mutually exclusive choice.
Figure 2.13 demonstrates this case.

Figure 2.13 A group box control with a set of mutually exclusive radio button choices within it.

Button

The button control (also known as a pushbutton) is an information request control. Use this control to enable
your application to respond to a user-triggered event. Perhaps the most common control on dialogs is a button
named Cancel that typically dismisses the dialog without saving its data. Figure 2.14 shows a Cancel button.

Figure 2.14 A button control named Cancel.

Check Box

The check box control is often used as an information modifier. It can represent either two or three states; two
states are true and false and three states are true, false, and indeterminate. Use this control when you’d like the
user to choose a Boolean state. Figure 2.15 shows a check box.
Figure 2.15 A check box control enables the user to specify his or her preferences.

Radio Button

The radio button control is another information modifier. Rarely used in isolation, this control is often part of a
group of mutually exclusive choices. Use a group of these controls when you’d like the user to choose one of a
finite number of selections that are known at application design time. Figure 2.13, earlier in the chapter, shows
such a group of radio buttons.v

Combo Box

Like the radio button, the combo box control is another information modifier. It provides a similar purpose to a
group of radio buttons, except the finite number of selections can be specified at runtime. Using the combo box,
the user can choose one of its entries. There are three types of combo box available to you: simple, drop-down,
and drop-list. The simple and drop-list combo boxes are different only in visual appearance. The drop-down
combo box allows the user to enter a new entry in the edit box portion of the control. Figures 2.16 and 2.17
demonstrate each of these types.

Figure 2.16 A simple combo box control.

Figure 2.17 A drop-down or drop-list combo box control.

List Box

The list box control is another information modifier. It displays a list of choices to the user—and, depending on
its actual style, the user can choose either none, one, or many of its items. With a single selection list box, only
one entry can be selected, which makes its behavior similar to that of a simple combo box. Figure 2.18 shows a
list box.

Figure 2.18 A list box control.

Horizontal and Vertical Scrollbars

The scrollbar control is often used to enable the user to move a graphical image or text within the current view.
Additionally, it can enable the user to select a contiguous range within a finite set, similar to the Slider control
(described following). Figures 2.19 and 2.20 show a horizontal and a vertical scrollbar, respectively.
Figure 2.19 A horizontal scrollbar control.

Figure 2.20 A vertical scrollbar control.

Common Controls

As Windows matured and the user-interaction needs of applications increased, Microsoft introduced a set of
advanced controls for dialogs. Called the “common”controls, they were shipped as a separate, redistributable
DLL and were not part of any standard Windows installation until Windows 95 and Windows NT 4. These
controls include the spin control, progress indicator, slider, hot key, list control, tree control, tab control,
animation control, rich text edit, date/time picker, month calendar, and IP address control.

Spin

The spin control (sometimes called a spin button or an up-down control) allows the user to increment or
decrement a value in an associated control. Consequently, it is an information modifier. This control allows the
user to fine-tune the data in another control and is often used to allow the user to scroll through a list of numeric
values. The spin control is shown in Figure 2.21.

Progress

The progress indicator control is an information presentation control that displays a graphical representation of
the percent progress. Use this control when you’d like your application to indicate graphically the current amount
of progress. Figure 2.22 demonstrates this control.

Figure 2.21 A spin control.

Figure 2.22 A progress indicator control.

Slider

The slider control allows the user to select one of a range of values. It is similar to a level control on a stereo’s
equalizer. You can choose to have either horizontal or vertical sliders. This control is shown in Figure 2.23.

Figure 2.23 A horizontal slider control with a point and tick marks.

Hot Key

The hot key control enables the user to enter a combination keystroke that includes Alt, Shift, or Ctrl, and
another key to define a hot key to the application. Use this control when you let the user define a keystroke
combination for the application. Figure 2.24 demonstrates this control.

Figure 2.24 A hot key control.

List Control

The list control is essentially the classic list box on steroids. It allows one of four views: large icon (single column
with text), small icon (single column with text), list (multi-column, small icon with text), and report (single column,
small icon with text and other textual details in columns). Figure 2.25 shows a list control in report view.

Figure 2.25 A list control in report view.

Tree Control

The tree control provides a means of displaying a hierarchy of data within your application. The standard
Windows Explorer window is a tree control, showing a hierarchy of drives, folders, and files. The tree control is
shown in Figure 2.26.

Figure 2.26 A tree control showing a hierarchy of data.

Tab Control

The tab control provides a means of logically organizing your application’s data in your user interface. If you use
this control, your GUI needs to display only the controls in which the user is interested—rather than
overwhelming him with a large number of controls at once. Figure 2.27 shows a tab control.

Figure 2.27 A tab control.

Animation

The animation control enables you to display an AVI file within the control’s area when clicked. Use this control
when you have multimedia graphics that you’d like displayed within your dialog.

Rich Edit

The rich edit control enables you to embed more sophisticated text processing in your application, so you can
provide the user a means of formatting text and paragraphs, as well as loading and saving the text in RTF
format. This control is shown in Figure 2.28.

Note:

You must call AfxInitRichEditin your application class’s InitInstancemethod to use the rich edit
control.

Figure 2.28 A rich edit control.

Date/Time Picker

The date/time picker control provides a means of allowing the user to select a date or time through your user
interface. Its visual appearance can be similar to a combo box or an edit text/spin control combination. The latter
combination is shown in Figure 2.29.

Figure 2.29 A date/time picker control using a spin control.

Month Calendar

The month calendar control allows your application to display an entire calendar month to the end user. It
provides an intuitive way for the user to interact with dates in your application. The month calendar control is
shown in Figure 2.30.

Figure 2.30 The month calendar control.

IP Address

The IP address control provides a straightforward means of displaying and allowing the user to modify an
Internet network address in a way consistent with the rest of the system. This control is shown in Figure 2.31.

Figure 2.31 An IP address control.

Extended Combo Box

The extended combo box control has the same behavior and types as the standard combo box control.
However, unlike the standard control, it supports the use of images—without requiring an owner draw routine to
draw them. Use this control if you need to allow the user to select one of a set of items with images.

Custom Controls

Another type of control that you can add to your dialog is called a customcontrol. You can create your own
controls to satisfy specific requirements for your application. Refer to Chapter 5, “Custom Control
Development,”for information on how to create and use custom controls.

Adding Initialization

There are two locations where dialog initialization is typically performed: in the constructor and in the
OnInitDialogmethod for your dialog class. The actual location for your particular initialization code depends on
what is required at the time of initialization.

Use the dialog class constructor to initialize member variables that have no dependence on any controls on the
dialog. For example, the constructor is an excellent place to initialize any class pointers to zero. Listing 2.1
demonstrates how the constructor is used in the MFCSample application to initialize the m_currentColormember
variables.

Listing 2.1Initialization of Class Members in the Constructor

CMFCSampleDlg::CMFCSampleDlg(CWnd* pParent /*=NULL*/)


: CDialog(CMFCSampleDlg::IDD, pParent)
, m_currentColor(RGB(0,0,255))
{
//{{AFX_DATA_INIT(CMFCSampleDlg)
m_fileName = _T(“”);
//}}AFX_DATA_INIT

// [omitted code]
}

Listing 2.1 also shows the initialization of a member variable, m_fileName, used in DDX. This variable represents
the filename that is displayed in the MFCSample application’s main window, as shown earlier in Figure 2.9.

Note:

The initialization of m_fileNameis contained within a set of comments that are tagged with
AFX_DATA_INIT. These comments are flags that indicate to the Visual Studio environment that
the code between the comments was generated. As a general rule, you typically leave this code
alone and do not put your own code within these comments, for fear of losing it at a later date.
The second (and perhaps most common) location of initialization code for your dialog is in the
OnInitDialogmethod. MFC calls OnInitDialogafter the dialog window and all of its controls have been created.
Consequently, feel free to interact with any of the controls in your dialog window in your dialog class’s
OnInitDialogmethod. Listing 2.2 shows the calls to EnableToolTipsand DrawBitmapWithColorin initialization section
for the MFCSample application.

Listing 2.2Dialog Initialization in the OnInitDialogMethod

BOOL CMFCSampleDlg::OnInitDialog()
{
CDialog::OnInitDialog();

// [omitted code]

// TODO: Add extra initialization here


EnableToolTips();
DrawBitmapWithColor();

return TRUE; // return TRUE unless you set the focus to a control
}

Note:

In general, insert your application’s initialization code after the Visual Studio-generated
comment, “TODO: Add extra initialization here.”

Using Dialog Controls

After you’ve created your dialog class, you’ll most certainly need to programmatically interact with and receive
events from the controls in your dialog. The next section describes exactly how to do just that.

Interacting with Controls

To interact with your dialog’s controls, you’ll need instances of classes for each of your dialog’s controls that you
need to manipulate. There are two basic ways of creating an instance of an object class. The first way is by
using the Visual Studio ClassWizard to define a member variable that is associated with your dialog’s control.
The second way is to create an MFC control class object on-the-fly.

To create an instance of an object class using Class Wizard, you must first open the ClassWizard dialog by
selecting View, ClassWizard from the Visual Studio pull-down menu. When the ClassWizard dialog appears,
choose the tab named Member Variables. The ClassWizard open to this tab is shown in Figure 2.32.
Figure 2.32 Using the ClassWizard to view the current member variables associated with dialog controls.

Next, select the control for which you need a member variable defined and click Add Variable. At this point, the
Add Member Variable dialog appears as shown in Figure 2.33. Enter a name for your variable, choose the
Control category, and select the type for your variable. When you click OK, this member variable is added
automatically to your class.

Note:

Some controls might have additional categories beyond Control—usually types such as
CStringand int. These types of member variables are used with Dialog Data Exchange, which is
explained later in this chapter.

Now that the member variable is defined, you can manipulate the control as required. Hence, to disable a button
named m_pbOK, code the following:

m_pbOK.EnableWindow(FALSE);
Figure 2.33 Using the ClassWizard to define a member variable for a dialog control.

The methods that you use are limited to the methods available to you for the control type that you specified
when you created the member variable.

The second way of interacting with a dialog control is by creating an automatic variable directly within your code.
To do this, you must first create an automatic instance of the class appropriate for your control. Next, you mt
obtain a pointer to a CWndobject for your dialog’s control using the GetDlgItemmethod. Listing 2.3 demonstrates
the creation of an automatic variable named pbOKthat is associated with the IDOK control in your dialog.

Listing 2.3On-the-Fly Control Variable Creation

CButton *pbOK = DYNAMIC_DOWNCAST(CButton, GetDlgItem(IDOK))

Caution:

Because the pointer returned from GetDlgItemis temporary and is not owned by your application,
do not store or delete it.

Receiving Events from Controls

Each type of control (CButton, CListCtrl, and so on) has its own set of events that you can handle in your
application. With the Visual Studio environment, it is straightforward to add event handlers to your application.

The MFCSample application sets up several event handlers to perform different actions. For example, each
button on its dialog has an event handler to perform an action when the user pushes it. To create an event
handler for a button click, you can use ClassWizard to help you quickly add it to your class.

First, from the Visual Studio pull-down menu, select View, ClassWizard. When the ClassWizard window opens,
select the Message Maps tab, if that tab is not already on top. Figure 2.34 shows this tab for the MFCSample
application.

Figure 2.34 Using the ClassWizard to view the current message maps associated with dialog controls.

Next, select the class owning a control for which you’d like to set up a message map in the Class Name combo
box. After you choose the class, a list of the objects for which you can set up a message map is shown in the
Object IDs list box. By selecting the various Object IDs, you can see which events can be handled for that
object. Additionally, the events that are in bold in the Messages list box already have a handler defined.

To add a new event handler, choose the appropriate message in the Messages list box and click the Add
Function button. At this point, you are prompted for the name of the function to add, as shown in Figure 2.35.
When you click OK to name the function, Visual Studio generates code within your application to handle that
event.

Figure 2.35 Using the ClassWizard to name a member function to handle a dialog control event.

Finally, when you click Edit Code, Visual Studio displays the handler method for that event and allows you to
edit it. Listing 2.4 shows the handler method for the File button in the MFCSample application.

Listing 2.4Handling a File Pushbutton Click in MFCSample


void CMFCSampleDlg::OnPbFile()
{
// TODO: Add your message handler code here and/or call default
}

ToolTips

ToolTips are small, yellow text windows that appear over certain dialog controls after the mouse pointer stops
for a moment. They provide a mechanism for you to give the user more detailed information about how that
control is used. By adding ToolTips to your application, you can make it more user-friendly and usable.

Enabling ToolTips

To use ToolTips, you must first enable them for your dialog window by calling EnableToolTips. An ideal place for
doing this is in your OnInitDialogmethod, as shown earlier in Listing 2.2. After you’ve done this, your application
receives TTN_NEEDTEXTnotification messages when a ToolTip window is about to be displayed by Windows.

Note:

If you need to disable ToolTips in your application, call CancelToolTips.

Displaying Text

The ToolTip notification message, TTN_NEEDTEXT, gives your application the opportunity to provide the text that
needs to appear in the ToolTip window. Follow these steps to add a handler for this message:

1. Add the following line to your message map declaration for your class in its declaration in the header
file:

afx_msg BOOL OnNeedToolTipText(UINT id, NMHDR * pTTTStruct,


LRESULT * pResult);

2. Add the following line to your class’s message map definition in the source file between
BEGIN_MESSAGE_MAPand END_MESSAGE_MAP:

ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnNeedToolTipText)

3. Add a definition of the OnNeedToolTipTextmethod that you just declared in your class’s header file and
message map. Listing 2.5 shows the definition of this method for MFCSample.

Listing 2.5Handling of ToolTips in MFCSample

BOOL CMFCSampleDlg::OnNeedToolTipText(UINT id, NMHDR * pNMHdr,


LRESULT * pResult)
{
TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *) pNMHdr;

if (pTTT->uFlags &TTF_IDISHWND) {
UINT idCtrl = ::GetDlgCtrlID(HWND(pTTT->hdr.idFrom));

if (idCtrl == IDC_PB_CUSTOM_MODELESS &&m_custDlg.m_hWnd) {


pTTT->lpszText = MAKEINTRESOURCE(IDS_TT_PB_CUSTOM_MODELESS2);
}
else {
pTTT->lpszText = MAKEINTRESOURCE(idCtrl);
}

pTTT->hinst = AfxGetResourceHandle();

return TRUE;
}

return FALSE;
}

Listing 2.5 demonstrates how to add custom logic to dynamically change the actual ToolTip text according to the
current state of the application. In MFCSample, generally all ToolTips are loaded from string resources where
the string resource identifier is equal to the control identifier. However, if a modeless dialog is currently
displayed, the ToolTip for the custom modeless pushbutton changes accordingly.

Dialog Data Exchange

Dialog Data Exchange is a powerful means of associating non-control datatypes with controls. When DDX is
used, your application logic can deal with the data itself, rather than the controls to get and set the data.

The “Interacting with Controls”section discussed associating a control-type member variable with a control. With
DDX, you can associate a data member variable with a control. For example, rather than having an
m_edMyTextCEditBoxmember variable, you can have an m_myTextCStringmember variable. Furthermore, using a
DDX method named UpdateData, your application can always ensure that the data member variable and actual
control remain in sync.

Standard DDX

When you create a non-control member variable that is associated with a control, Visual Studio generates code
to initialize and associate the data member and the control. The MFCSample application has one member
variable, m_fileName, which is associated with a static text control to display its data.

Upon creating the member variable (as discussed in “Interacting with Controls”), Visual Studio creates
initialization code for those data members in the class’s constructor, as shown earlier in Listing 2.1.

Additionally, it creates a method named DoDataExchangethat exchanges the data between the control and the
data member (and vice versa). For the MFCSample application, this method is shown in Listing 2.6.

Listing 2.6DDX Method DoDataExchangein MFCSample

void CMFCSampleDlg::DoDataExchange(CDataExchange* pDX)


{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CMFCSampleDlg)
DDX_Control(pDX, IDC_ST_COLOR, m_stColor);
DDX_Control(pDX, IDC_PB_CUSTOM_MODELESS, m_pbCustomModeless);
DDX_Control(pDX, IDC_ST_FONT, m_stFont);
DDX_Text(pDX, IDC_ST_FILE, m_fileName);
//}}AFX_DATA_MAP
}

In the DoDataExchangemethod for the CMFCSampleDlgclass, four DDX associations are implemented. The first
three associate the dialog control with the control-type member variable that references it. In this way,
IDC_ST_COLORis associated with m_stColor, IDC_PB_CUSTOM_MODELESSis associated with
m_pbCustomModeless, and IDC_ST_FONTis associated with m_stFont. The fourth association is between a control
and a data member variable: IDC_ST_FILEand m_fileName.

UpdateData

The UpdateDatamethod is what keeps all of the DDX associations in sync. Using this method, you can force the
data to go either from the control to the member variable or vice versa, based upon the passed parameter.

This UpdateDatamethod is declared as follows in the CWndclass:

BOOL UpdateData(BOOL bSaveAndValidate= TRUE);

If you pass TRUEas the parameter to UpdateData, the data moves from the controls to their associated data
members. Passing FALSEdoes the reverse. If an error occurs due to a data validation error, FALSEis returned
from this method.

Using Standard Dialog Boxes

MFC provides four standard dialog boxes that allow your application to get information from the user in a way
consistent with other applications. These dialogs are the file open/save dialog, the color selector, the font
selector, and the print configuration dialog.

File Open/Save

The MFC CFileDialogclass represents the File Open/Save dialog. By instantiating an object from this class, your
application can prompt the user for a filename to open or save. The File Open dialog is shown in Figure 2.36.

Figure 2.36 The Windows standard File Open dialog.

The MFCSample application displays the File Open dialog when the user clicks the File button. If the user
selects a file and clicks OK, that file is displayed in the main dialog. The code to support this use is shown in
Listing 2.7.

Listing 2.7Simple Code to Display the File Open Dialog

void CMFCSampleDlg::OnPbFile()
{

CFileDialog fileDlg(TRUE);

if (fileDlg.DoModal() == IDOK) {
m_fileName = fileDlg.GetFileName();
UpdateData(FALSE);
}
}

Color Selector

The MFC CColorDialogclass represents the Color selection dialog. By instantiating an object from this class, your
application can prompt the user for a color. The Color selection dialog is shown in Figure 2.37.

Figure 2.37 The Windows standard Color selection dialog.

The MFCSample application displays the Color Selection dialog when the user clicks the Color button. If the
user selects a color and clicks OK, that color is displayed in the main dialog. The code to support this use is
shown in Listing 2.8.

Listing 2.8Simple Code to Display the Color Selection Dialog


void CMFCSampleDlg::OnPbColor()
{

CColorDialog colorDlg(m_currentColor);

if (colorDlg.DoModal() == IDOK) {
m_currentColor = colorDlg.GetColor();
DrawBitmapWithColor();
}
}

Font Selector

The MFC CFontDialogclass represents the Font selection dialog. By instantiating an object from this class, your
application can prompt the user for a font face, size, and style. The Font selection dialog is shown in Figure 2.38.

The MFCSample application displays the Font Selection dialog when the user clicks the Font butn. If the user
selects a font face, size, and style and clicks OK, that font is used to draw some sample text that is displayed in
the main dialog. The code to support this use is shown in Listing 2.9.

Figure 2.38 The Windows standard Font selection dialog.

Listing 2.9Simple Code to Display the Font Selection Dialog

void CMFCSampleDlg::OnPbFont()
{

CFontDialog fontDlg;

if (fontDlg.DoModal() == IDOK) {
CFont font;
LOGFONT logFont;
fontDlg.GetCurrentFont(&logFont);
font.CreateFontIndirect(&logFont);

m_stFont.SetFont(&font);
}
}

Print Dialog

The MFC CPrintDialogclass represents the Print selection dialog. By instantiating an object from this class, your
application can prompt the user for a printer. The Print selection dialog is shown in Figure 2.39.

The MFCSample application displays the Printer Selection dialog when the user clicks the Print button. If the
user selects a printer and clicks OK, a sample page is printed on that printer. The code to support this use is
shown in Listing 2.10.

Listing 2.10Simple Code to Display the Printer Selection Dialog

void CMFCSampleDlg::OnPbPrint()
{
CPrintDialog printDlg(FALSE);

if (printDlg.DoModal() == IDOK) {
CDC printerDC;

printerDC.Attach(printDlg.GetPrinterDC());

int cx = printerDC.GetDeviceCaps(PHYSICALWIDTH);
int cy = printerDC.GetDeviceCaps(PHYSICALHEIGHT);

// add printing logic here...


printerDC.StartDoc(“MFCSample Document”);
printerDC.StartPage();

DrawClock(printerDC, __min(cx, cy));

printerDC.EndPage();
printerDC.EndDoc();

CString msg = “Printed to: ”;


msg += printDlg.GetDeviceName();
MessageBox(msg);
}
}
Figure 2.39 The Windows standard Print selection dialog.

Summary

Microsoft Visual Studio’s AppWizard makes creating a simple dialog-based MFC application easy. After you
have followed the AppWizard through its steps and have generated an application shell, you can add your
application’s functionality in a relatively straightforward manner.

Additionally, the Visual Studio resource editor enables you to add standard controls to your application—from
basic controls such as pushbuttons and list boxes to the newer, more specialized controls such as the hot key
control and IP address control. You can use one of standard selection dialogs to prompt the user for a file,
printer, color, and font at the appropriate places within your application.

The MFC architecture provides a standard mechanism, Dynamic Data Exchange, for interchanging data
between variables and your dialog’s controls. When equipped with DDX support, your application’s dialogs can
validate and transfer user input to your dialog in a well-designed manner.

For more information on programming the dialog controls, see Chapter 3.


Chapter 3
The Windows Common Controls

by Rob McGregor

In This Chapter

• Initializing and Using the Common Controls 80


• Notifications for Windows Common Controls 81
• Hot Key Controls: Class CHotKeyCtrl 85
• Spin Controls: Class CSpinButtonCtrl 88
• Slider Controls: Class CSliderCtrl 95
• Progress Bar Controls: Class CProgressCtrl 105
• Image Lists: Class CImageList 107
• List View Controls: Class CListCtrl 110
• List View Items and Subitems 114
• Tree View Controls: Class CTreeCtrl 120
• Tab Controls: Class CTabCtrl 125
• Animate Controls: Class CanimateCtrl 131
• Rich Edit Controls: Class CRichEditCtrl 135

Windows controls, those doodads and widgets that make the Windows GUI so appealing, include both the Windows standard controls and the Windows
common controls. The Windows standard controls include list boxes, edit boxes, combo boxes, scrollbars, and various types of buttons. The Windows common
controls were introduced with Windows 95, and they add a lot of additional GUI power to the Windows programmer’s toolkit.

Because the common controls are specific to Win32, the MFC common control classes are available only to programs running under Windows 95 or later,
Windows NT version 3.51 or later, and Windows 3.1x with Win32s 1.3 or later.

Initializing and Using the Common Controls

Before you can use any of the Windows common controls in your applications, you must initialize the common control DLLs with a Win32 API function call.
Windows 95 applications use the InitCommonControls() function to register and initialize all the common control window classes. Its use is easy, as you can see
by its function prototype:

void InitCommonControls(VOID);

The InitCommonControls() function is now obsolete; it has been replaced with the more intelligent InitCommonControlsEx() function, which loads only the specific
control classes that you specify. Its function prototype looks like this:

BOOL InitCommonControlsEx(LPINITCOMMONCONTROLSEX lpInitCtrls);

InitCommonControlsEx() registers specific common control classes, specified by the parameter lpInitCtrls, from the common control dynamic-link library (DLL). The
lpInitCtrls parameter is of type LPINITCOMMONCONTROLSEX, which is a structure containing information about which control classes to load from the common
control DLL.

The INITCOMMONCONTROLSEX structure looks like this:

typedef struct tagINITCOMMONCONTROLSEX


{
DWORD dwSize;
DWORD dwICC;
}
INITCOMMONCONTROLSEX, *LPINITCOMMONCONTROLSEX;

The parameters are as follows:

• dwSize The size of the structure, in bytes.


• dwICC The set of bit flags that specifies which common control classes to load from the common control DLL. This parameter can be any combination
of the values in Table 3.1.

Table 3.1The Bit Flags Used by the INITCOMMONCONTROLSEXStructure

Bit Flags Name Meaning


ICC_ANIMATE_CLASS Loads the animate control class.
ICC_BAR_CLASSES Loads the toolbar, status bar, trackbar, and tooltip control classes.
ICC_COOL_CLASSES Loads the rebar control class.
ICC_DATE_CLASSES Loads the date and time picker control class.
ICC_HOTKEY_CLASS Loads the hot key control class.
ICC_INTERNET_CLASSES Loads the IP address class.
ICC_LISTVIEW_CLASSES Loads the list view and header control classes.
ICC_PAGESCROLLER_CLASS Loads the pager control class.
ICC_PROGRESS_CLASS Loads the progress bar control class.
ICC_TAB_CLASSES Loads the tab and tooltip control classes.
ICC_TREEVIEW_CLASSES Loads the tree view and tooltip control classes.
ICC_UPDOWN_CLASS Loads the up-down control class.
ICC_USEREX_CLASSES Load the ComboBoxEx class.
ICC_WIN95_CLASSES Loads the animate control, header, hot key, list view, progress bar, status bar, tab, tooltip,
toolbar, trackbar, tree view, and up-down control classes.

Notifications for Windows Common Controls

Windows common controls use a different messaging system than do standard Windows controls. This section takes a look at just what’s going on in the new
messaging system and demonstrates how to tap into common control communications.

Each of the Win32 common controls has a corresponding set of notification codes defined for its control type. In addition to these codes, there is a set of codes
shared by all the common controls. These notifications all pass a pointer to an NMHDRstructure (described in the following section), and are shown in Table 3.2.

Table 3.2Notification Codes Used by All the Win32 Common Controls

Notification Code Meaning


NM_CLICK Sent when a user clicks the left mouse button within the control.
NM_DBLCLK Sent when a user double-clicks the left mouse button within the control.
NM_KILLFOCUS Sent when the control loses the input focus.
NM_OUTOFMEMORY Sent when the control can’t finish an operation because of insufficient free memory.
NM_RCLICK Sent when a user clicks the right mouse button within the control.
NM_RDBLCLK Sent when a user double-clicks the right mouse button within the control.
NM_RETURN Sent when a user presses the Enter key and the control has the input focus.
NM_SETFOCUS Sent when the control receives the input focus.

The Notification Message Structure

Win32 common controls use a special structure to send notification messages: the NMHDRstructure. This structure contains the window handle of the sender,
the control ID of the sender, and the notification code being sent. The NMHDRstructure looks like this:

typedef struct tagNMHDR


{
HWND hwndFrom; // Window handle of the sender
UINT idFrom; // Control ID
UINT code; // Notification code
} NMHDR;

Overview of the Notification Process

Win32 common controls send most notification messages as WM_NOTIFYmessages; Windows standard controls (also used by 16-bit Windows) send most
notification messages as WM_COMMANDmessages.

Note:

The WM_NOTIFYmessage isn’t used for 16-bit Windows because the message was designed specifically for 32-bit Windows.
WM_NOTIFYprovides a standard way for the Win32 common controls to communicate information about control activities to Windows and to your applications
(as opposed to creating a huge number of new WM_*macros for each new control).

A Win32 common control sends WM_NOTIFYnotification messages to its parent window, which is usually a CDialog-derived class; in response, MFC calls the
CWnd::OnNotify()method to process these messages. To intercept and handle these messages for a common control, you can override the CWnd::OnNotify()
method for the control’s owner class. The prototype for CWnd::OnNotify()looks like this:

BOOL CWnd::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)

To discover the reason for the occurrence of the WM_NOTIFYmessage (or, “What’s the user doing with this control?”), you must look closely at the parameters of
the CWnd::OnNotify()method:

• The wParamparameter is the control ID of the control that sent the message or is NULLif the message didn’t come from a control.
• The lParamparameter is a pointer to a notification message structure (NMHDR) that contains the current notification code along with some additional
information.
• The pResultparameter is a pointer to an LRESULTvariable that stores the result code if the message is handled.

If the NMHDRstructure is actually a member of a larger structure (as it is for most Win32 common controls), you must cast it to an NMHDRwhen you use it; only a
few common controls actually use the simple NMHDRstructure.

The lParamparameter can be a pointer to either an NMHDRstructure or some other structure that has an NMHDRstructure embedded as its first data member and
has been typecast to an NMHDRstructure.

Usually, the pointer isto a larger structure containing an NMHDRstructure and not to the NMHDRstructure itself. In these cases, because the NMHDRstructure is
the first member of the larger structure, it can be successfully typecast to an NMHDRstructure.

A Better Notification Handling Scheme

As stated earlier in this chapter, the CWnd::OnNotify()method handles notification messages. Although you cando it, you really shouldn’toverride the CWnd::
OnNotify()method to receive notifications from controls—there’s no need to do so in most cases. Instead, you should provide a message handler method to trap
the notification and add a corresponding message map entry in a control’s parent class.

The ON_NOTIFYmessage map macro uses this syntax:

ON_NOTIFY(NotifyCode, ControlID, ClassMethod)

In this syntax, NotifyCoderepresents the notification code being sent, ControlIDrepresents the control identifier the parent window uses to communicate with the
control that sent notification, and ClassMethodrepresents the message handler method called in response to the notification.

Your message handler method must be declared using this prototype format:

afx_msg void ClassMethod(NMHDR* pNotifyStruct, LRESULT* result);

In this syntax pNotifyStructis a pointer to an NMHDRstructure, and resultis a pointer to the result code your message handler method sets.

Specifying Notification Ranges with ON_NOTIFY_RANGE

To allow several controls to process a notification using the same message handler method, you can use the ON_NOTIFY_RANGEmacro in the message map
instead of using the ON_NOTIFYmacro. When you use ON_NOTIFY_RANGE, you must specify the beginning and ending control IDs for the controls that will have
access to the notification message.

Caution:

The control IDs used for the ON_NOTIFY_RANGEmacro must be numerically contiguous. For example, if three CHeadercontrols are to use the
same notification message handler, these control IDs would work:

// Contiguous
#define IDC_HEADER1 100
#define IDC_HEADER2 101
#define IDC_HEADER3 102
These control IDs wouldn’t work:

// Non-contiguous
#define IDC_HEADER1 100
#define IDC_HEADER2 103
#define IDC_HEADER3 108

The ON_NOTIFY_RANGEmessage map macro uses this format:

ON_NOTIFY_RANGE(NotifyCode, FirstCtrlID, LastCtrlID, ClassMethod)

In this syntax, NotifyCoderepresents the notification code being sent, ControlIDFirstrepresents the control identifier of the first control in the contiguous range,
ControlIDFirstrepresents the control identifier of the last control in the contiguous range, and ClassMethodrepresents the message handler method called in
response to the notification.

The message handler method prototype in the owner’s class declaration is as follows:

afx_msg void ClassMethod(UINT ControlID, NMHDR* pNotifyStruct,


LRESULT* result);

In this syntax, ControlIDis the identifier of the control that sent the notification, pNotifyStructis a pointer to an NMHDRstructure, and resultis a pointer to the result
code your message handler method sets when it processes the notification.

The new common controls provide a more structured method of notification messaging that takes some getting used to, but makes MFC programs more
readable and easier to maintain in the long run. Now let’s take a look at Windows common controls and see how MFC makes using them fairly easy.

Hot Key Controls: Class CHotKeyCtrl

A hot keyis a key combination used to perform some action quickly. A hot key controlis a window that stores the virtual key code and shift state flags that
represent a hot key. The window displays the key combination as a text string (for example, Alt+X). The control doesn’t actually set the hot key, however; your
code must do this explicitly. To enable a hot key, your application must get the hot key’s values and associate these values with either a window or a thread.
MFC provides the services of a Windows hot key common control in the class CHotKeyCtrl, which is derived directly from CWnd(and therefore inherits all the
functionality of CWnd). The CHotKeyCtrlclass is defined in AFXCMN.H. A hot key control can be created as a child control of any window by writing code; it can
also be defined in a dialog resource template. MFC automatically attaches a Windows hot key common control to a CHotKeyCtrlobject when the object is created.

CHotKeyCtrl Class Methods

The CHotKeyCtrlclass offers a minimal set of methods for manipulating the control and its data. The constructor, CHotKeyCtrl::CHotKeyCtrl(), allocates a
CHotKeyCtrlobject that is initialized with the CHotKeyCtrl::Create()method. Table 3.3 describes the class’s methods.

Table 3.3CHotKeyCtrlClass Methods

Method Description

GetHotKey() Gets the virtual-key code and modifier flags of a hot key from a hot key control.
SetHotKey() Sets the hot key combination for a hot key control.
SetRules() Defines invalid key combinations and the default modifier combination for a hot key control.

Creating and Initializing a CHotKeyCtrl Object

To create a CHotKeyCtrlobject, you use the two-step construction process typical of MFC:

1. Call the class constructor CHotKeyCtrl::CHotKeyCtrl()to allocate the object.


2. Initialize the CHotKeyCtrlobject and attach an actual Windows hot key common control to it with a call to the CHotKeyCtrl::Create()method.

The prototype for the CHotKeyCtrl::Create()method is shown here:

BOOL Create(DWORD dwStyle, const RECT&rect,


CWnd* pParentWnd, UINT nID);
In this syntax, the parameters are defined as follows:

• dwStyleSpecifies the window style for the control.


• rectThe rectangle specifying the size and position of the control.
• pParentWndA pointer to the owner of the control.
• nIDThe control ID used by the parent to communicate with the control.

Using a Hot Key Control

After a hot key control is created, a default hot key value can be set by calling the SetHotKey()method. To prevent specific shift states, call the SetRules()method.
A user can choose a hot key combination when the control has the focus, and an application uses the GetHotKey()method to get the virtual key and shift state
values from the hot key control.

Armed with the details of the selected key combination, you can set the actual hot key by doing one of the following:

• Set up a global hot keyfor activating a top-level window by using the CWnd::SendMessage()method, sending a WM_SETHOTKEYmessage to the window
to be activated.
• Set up a thread-specific hot keyby calling the Win32 API function RegisterHotKey().

Global Hot Keys

A global hot keyenables a user to activate a top-level window from any part of the system. To set a global hot key for a specific window, you must send the
WM_SETHOTKEYmessage to that window. Assume that m_pHotKeyis a pointer to a CHotKeyCtrlobject and that pWndis a pointer to the target window. When the
hot key is activated, you can associate m_pHotKeywith pWndlike this:

WORD wKeyShift = m_pHotKey->GetHotKey();


pWnd->SendMessage(WM_SETHOTKEY, wKeyShift);

Thread-Specific Hot Keys

A thread-specific hot keyenables a user to activate a top-level window that was created by the current thread. To set a thread-specific hot key for a particular
window, you must call the Win32 API function RegisterHotKey(). This function has the following prototype:
BOOL RegisterHotKey(HWND hWnd, int id, UINT fsModifiers, UINT vk);

In this syntax, hWndidentifies the window to receive the WM_HOTKEYmessages generated by the hot key. The idparameter is the control ID of the hot key, which
must be in the range of 0x0000through 0xBFFFfor an application, or 0xC000through 0xFFFFfor a shared DLL. The fsModifiersparameter is the modifier keys that
must be pressed in combination with the key specified by the vkparameter to generate the WM_HOTKEYmessage. This can be a combination of the values
shown in Table 3.4. The vkparameter is the virtual-key code of the hot key.

Table 3.4The Modifier Flags Used with RegisterHotKey()

Value Meaning

MOD_ALT The Alt key must be held down.


MOD_CONTROL The Ctrl key must be held down.
MOD_SHIFT The Shift key must be held down.

Note:

If the key combination specified for a hot key has already been registered by another hot key, the call to RegisterHotKey()fails.

Spin Controls: Class CSpinButtonCtrl

A spin control(or up-down control) is a friendly little control sporting a matching set of two linked arrow buttons. These controls are often found hanging around
with their buddies, trying to get information from a user. Seriously, spin controls dousually interact intimately with another Windows control, and the spin
control’s arrow buttons increment or decrement an internal spin control value (called the current position) when clicked. This value can be displayed as a
number in a buddy window, which is most often an edit control used to get numeric input from a user.
A spin control and its buddy window (if used) often look and act like a single control. The spin control can align itself with its buddy window automatically, and it
can send its current scroll position to the buddy, changing the buddy’s window text accordingly. The current position is limited to an application-defined minimum
and maximum range of values. Figure 3.1 shows the typical use of a spin control: getting a value from the user.

Figure 3.1 Typical spin controls with their buddies, conspiring to get values from a user.

A spin control doesn’t needa buddy control; it can be used for many things all by itself, very much like a minimalist scroll bar. MFC provides the services of a
Windows spin common control in the class CSpinButtonCtrl. Like other MFC controls, CSpinButtonCtrlis derived directly from CWndand inherits all the functionality
of CWnd.

A spin control can be created as a child control of any window by writing code, or it can be defined for use in a dialog resource template. A spin control sends
Windows notification messages to its owner (usually a CDialog-derived class), and these messages can be trapped and handled by writing message map entries
and message-handler methods for each message. These message map entries and methods are implemented in the spin control’s parent class.
Spin Control Styles

Like all windows, spin controls can use the general window styles available to CWnd. In addition, they use the spin styles shown in Table 3.5 (as defined in
AFXCMN.H). A spin control’s styles determine its appearance and operations. Style bits are typically set when the control is initialized with the CSpinButtonCtrl::
Create()method.

Table 3.5The Window Styles Defined for a Spin Control

Style Macro Meaning

UDS_ALIGNLEFT Aligns a spin control to the left edge of the buddy window.
UDS_ALIGNRIGHT Aligns a spin control to the right edge of the buddy window.
UDS_ARROWKEYS Allows the control to increment and decrement the current position when the up-arrow and
down-arrow keys are pressed on the keyboard.
UDS_AUTOBUDDY Automatically selects a buddy window by choosing the previous window in the Z-order.
UDS_HORZ Makes the control’s arrows point left and right instead of up and down.
UDS_NOTHOUSANDS Prevents the thousands separator between every three decimal digits.
UDS_SETBUDDYINT Tells the control to set the text of the buddy window when the current position changes. The
text represents the current position formatted as a decimal or hexadecimal string.
UDS_WRAP Allows the current position to wrap around. Values above the maximum wrap around to start
back at the minimum of the scroll range, and vice versa.

Note:

The spin button styles all have the UDS_*prefix. This is an indication of the SDK name for this control (and the name many people use): the up-
down control(Up-Down Styles—UDS). But because the MFC team at Microsoft wrapped this control into a class called a spin button control,
that’s what I’ll call it, too.
CSpinButtonCtrl Messages

Because MFC wraps the Windows spin control messages (such as UDM_GETRANGEor UDM_SETBUDDY) into CSpinButtonCtrlclass methods, an MFC program
usually only has to handle notification messages. These messages can be trapped and handled by writing message map entries and message-handler methods
for each message. You map the spin control messages to class methods by creating message map entries in the control’s parent class. Table 3.6 shows the
message map entries for spin control messages.

Table 3.6Message Map Entries for Spin Button Control Messages

Message Map Entry Meaning

ON_WM_HSCROLL Sent by a spin control with the UDS_HORZstyle when the arrow buttons are clicked.
ON_WM_VSCROLL Sent by a spin control with the UDS_VERTstyle (the default) when the arrow buttons are clicked.
ON_EN_UPDATE Sent by a buddy edit control when the text is changed.

CSpinButtonCtrl Class Methods

The CSpinButtonCtrlclass offers a concise set of methods for manipulating the control and its data. Because the MFC help files shipped with your compiler
contain all the CSpinButtonCtrlclass method declarations and detailed descriptions of their parameters, you won’t find detailed descriptions here, but I willprovide
an overview of each method so that you know what to look for when you need it.

The CSpinButtonCtrlconstructor, CSpinButtonCtrl::CSpinButtonCtrl(), allocates a spin control object that is initialized with the CSpinButtonCtrl::Create()method to set
attributes and ownership. The methods listed in Table 3.7 describe the methods used to get and set control attributes.

Table 3.7CSpinButtonCtrlClass Methods


Method Description

GetAccel() Retrieves acceleration information for a spin control.


GetBase() Retrieves the current base for a spin control.
GetBuddy() Retrieves a pointer to the current buddy window.
GetPos() Retrieves the current position of a spin control.
GetRange() Retrieves the upper and lower limits (range) for a spin control.
SetAccel() Sets the acceleration for a spin control.
SetBase() Sets the base for a spin control.
SetBuddy() Sets the buddy window for a spin control.
SetPos() Sets the current position for the control.
SetRange() Sets the upper and lower limits (range) for a spin control.

Creating and Initializing a Spin Control

A CSpinButtonCtrlobject, like most MFC objects, uses a two-step construction process. To create a spin control, perform the following steps:

1. Allocate an instance of a CSpinButtonCtrlobject by calling the constructor CSpinButtonCtrl::CSpinButtonCtrl()using the C++keyword new.
2. Initialize the CSpinButtonCtrlobject and attach a Windows spin button common control to it with the CSpinButtonCtrl::Create()method to set the spin
control’s parameters and styles.

For example, a CSpinButtonCtrlobject is allocated and a pointer to that object is returned with this code:

CSpinButtonCtrl* pMySpinner = new CSpinButtonCtrl ;

The pointer pMySpinneris then initialized with a call to the CSpinButtonCtrl::Create()method. This method is declared as follows:

BOOL Create(DWORD dwStyle, const RECT&rect,


CWnd* pParentWnd, UINT nID);

The first parameter, dwStyle, specifies the window style for the spin control. The window style can be any combination of the general window styles and the
special spin control styles listed in Table 3.5, earlier in this chapter. The second parameter, rect, is the rectangle specifying the size and position of the control.
The parameter pParentWndis a pointer to the owner of the control, and nIDis the control ID used by the parent to communicate with the spin control.

Sample Program: SPIN1

Now let’s take a look at a basic MFC program (SPIN1.EXE on the CD-ROM) that creates and displays several spin controls and buddy windows in a frame
window (see Figure 3.2).
Figure 3.2 The SPIN1 frame window with three child spin controls and their buddy controls.

The SPIN1 program uses three spin controls to set the RGB color components of the client area window color, and uses both left-aligned and right-aligned
buddy windows.

Examining the SPIN1 Application Header (SPIN1.H)

The header file for the SPIN1 program begins by defining window styles for the frame window’s child controls:

// Define some spin window styles


#define SBS_LEFT (WS_VISCHILD | UDS_ALIGNLEFT | UDS_SETBUDDYINT)
#define SBS_RIGHT (WS_VISCHILD | UDS_ALIGNRIGHT | UDS_SETBUDDYINT)

// Buddy control style


#define ES_SINGLE (WS_VISCHILD | ES_LEFT | WS_BORDER)

Then the control IDs for the child window controls in this program are defined, as are the two classes used in this application: CSpinAppand CMainWnd.

The first class is the application class CSpinApp, which is a descendant of CWinAppand simply overrides the inherited InitInstance()method to provide custom
application initialization.

The second class is CMainWnd, derived from CMainFrame. This class contains pointers to the child windows as class data members. These child windows consist
of three spin controls and three edit controls. The class also provides the UpdateClientColor()helper method for changing the frame window’s client area color as
well as these two message-handler methods:

// Message handlers
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnBuddyUpdate();

Finally, the DECLARE_MESSAGE_MAP()macro is used to set up message handling for the class.

Implementing the SPIN1 Program (SPIN1.CPP)

The first order of business is setting up the message map for the CMainWndclass. This message map contains four entries that correspond to the four message-
handler method prototypes given in the class definition:

// Message map for CMainWnd


BEGIN_MESSAGE_MAP(CMainWnd, CMainFrame)
ON_WM_SIZE()
ON_EN_UPDATE(IDC_BUDDY1, OnBuddyUpdate)
ON_EN_UPDATE(IDC_BUDDY2, OnBuddyUpdate)
ON_EN_UPDATE(IDC_BUDDY3, OnBuddyUpdate)
END_MESSAGE_MAP()

Notice that the CEditbuddy windows all use the same message handler to process update messages. The class constructor initializes all child control pointers to
NULL, and the class destructor destroys any allocated child objects.

CMainWnd::CreateChildControls()allocates and initializes the child controls. After this, the spin controls are each assigned a buddy edit window that displays their
current position value:

// Set buddies
m_pSpin1->SetBuddy(m_pBuddy1);
m_pSpin2->SetBuddy(m_pBuddy2);
m_pSpin3->SetBuddy(m_pBuddy3);

The spin buttons then receive a scroll range of 0 to 255, the possible range of byte values used by the RGBmacro (that macro is used to change the frame
window’s client area color later):

// Set scroll ranges


m_pSpin1->SetRange(0, 255);
m_pSpin2->SetRange(0, 255);
m_pSpin3->SetRange(0, 255);
As a final step in initializing the spin controls, their individual current positions are set to the halfway position, at 128:

// Set current position


m_pSpin1->SetPos(128);
m_pSpin2->SetPos(128);
m_pSpin3->SetPos(128);

The CMainWnd::OnSize()message handler is quite simple, consisting of a call to the inherited method that it overrides, and another call to CMainWnd::
UpdateClientColor()to repaint the client area in the color specified by the spin button positions:

void CMainWnd::OnSize(UINT nType, int cx, int cy)


{
// Call inherited method
CWnd::OnSize(nType, cx, cy);

// Repaint the window at the new size


UpdateClientColor();
}

The CMainWnd::UpdateClientColor()method is the heart of the program, reading the values from the buddy windows and converting them to the byte values
needed for the color components of the RGBmacro. Three CStringlocal variables are declared to hold these values: szBuddy1Text, szBuddy2Text, and
szBuddy3Text. The values are retrieved with a call to the CWnd::GetWindowText()method, returning the text from the edit controls:

m_pBuddy1->GetWindowText(szBuddy1Text);
m_pBuddy2->GetWindowText(szBuddy2Text);
m_pBuddy3->GetWindowText(szBuddy3Text);

These strings are converted to integers by calling the CMainFrame::StringToInt()method; the result is stored in three local integer variables:

INT nBuddy1 = StringToInt(szBuddy1Text);


INT nBuddy2 = StringToInt(szBuddy2Text);
INT nBuddy3 = StringToInt(szBuddy3Text);

A CBrushobject is declared and initialized using these integers in an RGBmacro to create a new brush of the desired color:

CBrush br(RGB(nBuddy1, nBuddy2, nBuddy3));


To finish off the method (and the SPIN1 program), the frame window’s client area is retrieved into a CRectobject and passed to the CWnd::FillRect()method, along
with the new brush, to specify the fill color:

CRect rcClient;
GetClientRect(&rcClient);

CBrush br(RGB(nBuddy1, nBuddy2, nBuddy3));


GetDC()->FillRect(&rcClient, &br);

Note:

For more information about brushes, rectangles, and device contexts such as those used in the SPIN1 program, read Chapter 4, “Painting,
Device Contexts, Bitmaps, and Fonts.”

Slider Controls: Class CSliderCtrl

Similar to a scroll bar, a slider control(or trackbar) is an interactive, highly visual control consisting of a slider box that runs along the length of the control, and
optional tick marks that delineate range values. The slider control also has a built-in keyboard interface that allows movement of the slider with the arrow keys
on the keyboard. Figure 3.3 shows a typical use of slider controls in the Windows Volume Control applet.
Figure 3.3 Slider controls provide instant visual feedback as the main user-interface component in this applet.

MFC provides the services of a Windows slider common control in the class CSliderCtrl. Like other MFC controls, CSliderCtrlis derived directly from CWndand
inherits all the functionality of CWnd. A slider control can be created as a window’s child control by writing code; alternatively, it can be defined for use in a dialog
resource template.

A slider control sends Windows notification messages to its owner (usually a CDialog-derived class), and these messages can be trapped and handled by writing
message map entries and message-handler methods for each message. These message map entries and methods are implemented in the slider control’s
parent class.

Slider Control Styles

Like all windows, slider controls can use the general window styles available to CWnd. In addition, they use the slider control styles shown in Table 3.8 (as
defined in AFXWIN.H). A slider control’s styles determine its appearance and operations. Style bits are typically set when the control is initialized with the
CSliderCtrl::Create()method. Slider controls can be oriented either horizontally or vertically and can have tick marks on one side, both sides, or no tick marks at all
(depending on the following styles).

Table 3.8The Window Styles Defined for a Slider Control

Style Macro Meaning

TBS_AUTOTICKS Gives a slider tick marks for each increment in its range of values. Tick marks are
automatically created with a call to the SetRange()method.
TBS_BOTH Puts tick marks on both sides of a slider control, no matter what its orientation.
TBS_BOTTOM Puts tick marks on the bottom of a horizontal slider control.
TBS_ENABLESELRANGE Gives a slider tick marks in the shape of triangles that indicate the starting and ending
positions of a selected range.
TBS_HORZ Orients a slider horizontally (the default).
TBS_LEFT Puts tick marks on the left side of a vertical slider control.
TBS_NOTICKS A slider won’t have tick marks.
TBS_RIGHT Puts tick marks on the right side of a vertical slider control.
TBS_TOP Puts tick marks on the top of a horizontal slider control.
TBS_VERT Orients a slider vertically.

Note:

These styles all have the TBS_*prefix. Like the spin button control’s UDS_*prefix, the TBS_*prefix is an indication of the SDK name of the slider
control (and the name many people use): the Track Bar control(Track Bar Styles—TBS). But because the MFC team at Microsoft has wrapped
this control into a class called a slider control, I refer to it as a slider, too.
CSliderCtrl Messages

An MFC program usually has to handle only two notification messages from slider controls. A slider control sends its parent window notifications of user actions
in the form of scroll messages (WM_HSCROLLand WM_VSCROLL), just like a scroll bar control. These messages can be trapped and handled by writing message
map entries and message-handler methods for each message. You map the slider control messages to class methods by creating message map entries in the
control’s parent class. Table 3.9 shows the message map entries for slider control messages.

Table 3.9The Two Message Map Entries for Slider Control Messages

Message Map Entry Meaning

ON_WM_HSCROLL Sent by a slider control with the TBS_HORZstyle when the arrow buttons are clicked.
ON_WM_VSCROLL Sent by a slider control with the TBS_VERTstyle (the default) when the arrow buttons are
clicked.

But there is more to the messaging than a few simple WM_*messages. The ON_WM_HSCROLLand ON_WM_VSCROLLmessages have some interesting
information hidden away inside them. MFC gives you access to this information through the OnHScroll()and OnVScroll()methods, which are called by MFC when a
user clicks a slider’s tick marks, drags the slider box, or uses the keyboard arrows to otherwise control slider movement. These scroll methods are typically used
to give the user some interactive feedback while a slider control is scrolling or while the scroll box is dragging across a slider control’s range of possible values.
Here’s the prototype for the OnVScroll()method (the OnHScroll()method is exactly the same):

afx_msg void OnVScroll(UINT nSBCode, UINT nPos,


CSliderCtrl* pScrollBar);

The first parameter, nSBCode, specifies one of 10 possible scrolling codes that tell your application what the user is doing with the slider control. The slider
control has its own set of notification codes (which are just like the scroll bar codes), as listed in Table 3.10.

Table 3.10The Notification Codes Used by the Slider Control in the OnHScroll()and OnVScroll()Methods
Code Meaning

TB_BOTTOM A user pressed the End key on the keyboard.


TB_ENDTRACK A user released a key, causing some virtual key code to be sent (WM_KEYUP).
TB_LINEDOWN A user pressed the down-arrow or right-arrow key on the keyboard.
TB_LINEUP A user pressed the up-arrow or left-arrow key on the keyboard.
TB_PAGEDOWN A user clicked the channel below or to the right of the slider or pressed the PageDown key.
TB_PAGEUP A user clicked the channel above or to the left of the slider or pressed the PageUp key.
TB_THUMBPOSITION A user released the left mouse button (WM_LBUTTONUP)after dragging the slider
(TB_THUMBTRACK).
TB_THUMBTRACK A user dragged the slider.
TB_TOP A user pressed the Home key on the keyboard.

Note:

When a user interacts with a slider control through the keyboard interface (and only then), the TB_BOTTOM, TB_LINEDOWN, TB_LINEUP, and
TB_TOPcodes are sent. Likewise, the TB_THUMBPOSITIONand TB_THUMBTRACKcodes are sent only when a user uses the mouse to drag the
slider box. The other notification codes are sent no matter how a user interacts with the control.

The second parameter in the OnVScroll()or OnHScroll()method, nPos, reveals the current slider position when the notification code is either SB_THUMBPOSITIONor
SB_THUMBTRACK. If the nSBCodeparameter is anything other than these two codes, nPosisn’t used.

The third and final parameter, pScrollBar, is a pointer to the slider control that sent the message. Even though a slider control isn’t a scroll bar, this pointer does
refer to a slider control. When overriding the OnHScroll()or OnVScroll()method, simply typecast the point to a CSliderCtrlpointer, like this:
void OnVScroll(UINT nSBCode, UINT nPos, CSliderCtrl* pScrollBar)
{
CSliderCtrl* pSlider = (CSliderCtrl*) pScrollBar;

//
// use the pointer...
//
}

CSliderCtrl Class Methods

The CSliderCtrlclass offers a nice set of methods for manipulating the control and its data. Because the MFC help files shipped with your compiler contain all the
CSliderCtrlclass method declarations and detailed descriptions of their parameters, detailed descriptions aren’t given here, but I doprovide an overview of each
method so that you know what to look for when you need it.

The CSliderCtrlconstructor, CSliderCtrl::CSliderCtrl(), allocates a slider control object that is initialized with the CSliderCtrl::Create()method to set attributes and
ownership. The methods listed in Table 3.11 describe the methods used to get and set slider control attributes.

Table 3.11CSliderCtrlClass Methods That Deal with Attributes

Method Description

GetChannelRect() Gets the size of the slider control’s channel.


GetLineSize() Gets the line size of a slider control.
GetNumTics() Gets the number of tick marks in a slider control.
GetPageSize() Gets the page size of a slider control.
GetPos() Gets the current position of the slider.
GetRange() Gets the minimum and maximum positions for a slider.
GetRangeMax() Gets the maximum position for a slider.
GetRangeMin() Gets the minimum position for a slider.
GetSelection() Gets the range of the current selection.
GetThumbRect() Gets the size of the slider control’s thumb.
GetTic() Gets the position of the specified tick mark.
GetTicArray() Gets the array of tick mark positions for a slider control.
GetTicPos() Gets the position of the specified tick mark, in client coordinates.
SetLineSize() Sets the line size of a slider control.
SetPageSize() Sets the page size of a slider control.
SetPos() Sets the current position of the slider.
SetRange() Sets the minimum and maximum positions for a slider.
SetRangeMax() Sets the maximum position for a slider.
SetRangeMin() Sets the minimum position for a slider.
SetSelection() Sets the selection range for a slider.
SetTic() Sets the position of the specified tick mark.
SetTicFreq() Sets the frequency of tick marks per slider control increment.

Table 3.12 shows the three operational methods a slider control can perform.

Table 3.12CSliderCtrlClass Methods That Deal with Operations

Method Description

ClearSel() Clears the current selection from a slider control.


ClearTics() Removes the current tick marks from a slider control.
VerifyPos() Verifies that the position of a slider control is zero.

Creating and Initializing a Slider Control


A CSliderCtrlobject, like most MFC objects, uses a two-step construction process. To create a slider control, perform the following steps:

1. Allocate an instance of a CSliderCtrlobject by calling the constructor CSliderCtrl::CSliderCtrl()using the C++keyword new.
2. Initialize the CSliderCtrlobject and attach a Windows slider common control to it with the CSliderCtrl::Create()method to set slider parameters and styles.

For example, a CSliderCtrlobject is allocated, and a pointer to the CSliderCtrlobject is returned with this code:

CSliderCtrl* pMySlider = new CSliderCtrl;

The pointer pMySlidermust then be initialized with a call to the CSliderCtrl::Create()method. This method is declared as follows:

BOOL Create(DWORD dwStyle, const RECT&rect,


CWnd* pParentWnd, UINT nID);

The first parameter, dwStyle, specifies the window style for the slider control. This can be any combination of the general SDK window styles and the special
slider control styles listed in Table 3.8, earlier in this chapter. The second parameter, rect, is the rectangle specifying the size and position of the control. The
parameter pParentWndis a pointer to the owner of the control, and nIDis the control ID used by the parent to communicate with the slider control.

Sample Program: Slider Controls (SLIDER1)

The sample program SLIDER1 is just like sample program SPIN1 except that it uses sliders instead of spin controls, and it creates and displays three slider
controls in a frame window (see Figure 3.4).
Figure 3.4 The SLIDER1 frame window with three child slider controls.

Sliders don’t respond to the system-wide scroll bar size change messages like scroll bars and spin controls do. Also, slider controls are much more independent
than their scroll bar cousins. They update their own current positions and are smart enough to know how much to move for page change and line change
notifications. To tell the sliders how much you want them to change for the notifications, use the following code in the CMainWnd::CreateChildControls()method
(which includes the tick frequency):

// Set tick frequency


m_pSlider1->SetTicFreq(8);
m_pSlider2->SetTicFreq(8);
m_pSlider3->SetTicFreq(8);

// Set page size


m_pSlider1->SetPageSize(8);
m_pSlider2->SetPageSize(8);
m_pSlider3->SetPageSize(8);

// Set line size


m_pSlider1->SetLineSize(1);
m_pSlider2->SetLineSize(1);
m_pSlider3->SetLineSize(1);

This makes a big difference between the code for a scroll bar and the code for a slider in the SLIDER1 program’s CMainWnd::OnHScroll()method, thanks to the
automatic tracking of the slider control. Here is the entire function that handles scrolling code:

void CMainWnd::OnHScroll(UINT nSBCode, UINT nPos,


CScrollBar* pScrollBar)
{
// *Much* simpler than a scroll bar!

// Change to the new color


UpdateClientColor();
// call inherited handler
CMainFrame::OnHScroll(nSBCode, nPos, pScrollBar);

If you’ve ever written scrollbar code, you’ll see instantly that the slider control is a lot more user-friendly than a scroll bar—and makes for much easier coding!

Sample Program: SLIDER1

Next let’s take a look at the sample program SLIDER1. This simple program creates and displays three slider controls in a frame window (refer to Figure 3.4),
and it’s very similar in design to the sample program SPIN1 that you saw earlier in this chapter. The SLIDER1 program uses three slider controls to set the RGB
color components of the client area window color.

Examining the SLIDER1 Application Header (SLIDER1.H)

The header file for the SLIDER1 program begins by defining window styles for the frame window and its child controls:

// Main window style


#define WS_VISCHILD (WS_VISIBLE | WS_CHILD)

// Define a slider control window style


#define TBS_COLOR \
(TBS_HORZ | TBS_AUTOTICKS | WS_VISCHILD | WS_TABSTOP)

// Static control style


#define SS_STATIC (WS_VISCHILD | SS_CENTER)

Then the control IDs for the child window controls in this program are defined, as are the two classes used in this application: CSliderAppand CMainWnd.

The application class CSliderAppis almost exactly like class CspinApp, which you saw earlier. The CMainWndclass, derived from CMainFrame, contains pointers to
the child windows as class data members. These child windows consist of three slider controls and three static controls. The class also provides the
UpdateClientColor()helper method for changing the frame window’s client area color as well as these two message-handler methods:

// Message handlers
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg void OnHScroll(UINT nSBCode, UINT nPos,
CScrollBar* pScrollBar);
afx_msg void OnSize(UINT nType, int cx, int cy);

Finally, the DECLARE_MESSAGE_MAP()macro is used to set up message handling for the class.

Implementing the SLIDER1 Program (SLIDER1.CPP)

The first order of business is setting up the message map for the CMainWndclass. This message map contains four entries that correspond to the four message-
handler method prototypes given in the class definition:

// Message map for CMainWnd


BEGIN_MESSAGE_MAP(CMainWnd, CMainFrame)
ON_WM_ERASEBKGND()
ON_WM_HSCROLL()
ON_WM_SIZE()
END_MESSAGE_MAP()

CMainWnd::CreateChildControls()allocates and initializes the child controls. The sliders then receive a scroll range of 0 to 255, the possible range of byte values
used by the RGBmacro (that macro is used to change the frame window’s client area color later):

// Set slider ranges


m_pSlider1->SetRange(0, 255);
m_pSlider2->SetRange(0, 255);
m_pSlider3->SetRange(0, 255);

As a final step in initializing the spin controls, their individual current positions are set to the halfway position, at 128:

// Set current positions


m_pSlider1->SetPos(128);
m_pSlider1->SetPos(128);
m_pSlider1->SetPos(128);

The CMainWnd::OnSize()message handler first calls the inherited method that it overrides, then resizes each of the sliders by calling the CSliderCtrl::SetWindowPos()
method, and finally calls CMainWnd::UpdateClientColor()to repaint the client area in the color specified by the current slider positions. Listing 3.1 shows how it’s
done.

Listing 3.1Resizing the Slider Controls Along with the Window

///////////////////////////////////////////////////////////////////
// CMainWnd::OnSize()

void CMainWnd::OnSize(UINT nType, int cx, int cy)


{
// Call inherited method
CWnd::OnSize(nType, cx, cy);

// set some initial positions


int nHeight = 20;
int cyTop = 10;

// Resize the color sliders


m_pSlider1->SetWindowPos(0, 10, cyTop, cx - 20, nHeight,
SWP_SHOWWINDOW);
cyTop += nHeight * 2;

m_pSlider2->SetWindowPos(0, 10, cyTop, cx - 20, nHeight,


SWP_SHOWWINDOW);
cyTop += nHeight * 2;

m_pSlider3->SetWindowPos(0, 10, cyTop, cx - 20, nHeight,


SWP_SHOWWINDOW);
cyTop += nHeight * 2;

// Resize the static control


m_pStatic1->SetWindowPos(0, 10, cyTop, cx - 20,
m_nTextHeight, SWP_SHOWWINDOW);

// Repaint the window at the new size


UpdateClientColor();
}
To get the current position of the sliders, the UpdateClientColor()method calls CSlider::GetPos()for each, like this:

// Get the current scroll position


nRed = m_pSlider1->GetPos();
nGreen = m_pSlider2->GetPos();
nBlue = m_pSlider3->GetPos();

This is followed by some informative text that I set into each static child window. The CString::Format()method takes care of the dirty work for formatting the string:

// Display current RGB color as a text string


CString szText;
szText.Format(_T(“RGB(%d, %d, %d)”), nRed, nGreen, nBlue);
m_pStatic1->SetWindowText(szText);

Progress Bar Controls: Class CProgressCtrl

A progress bar controlis a window that provides visual feedback to a user during a lengthy application operation. Because a progress bar control simply keeps a
user apprised of an operation’s progress, the progress bar is typically for output only.

Like a slider control, a progress bar control has a rangeand a current position. The range specifies the length of some operation, and the current position
represents how far along the operation has come at that time. Using these two values, the percentage of fill for the control is determined automatically.

A typical use of the progress bar control is for relaying information about the progress of file operations on a disk (see Figure 3.5).
Figure 3.5 Relaying information about the progress of a disk operation with a progress bar.

MFC provides the services of a Windows progress bar common control in the class CProgressCtrl, which is derived directly from CWndand inherits all the
functionality of CWnd. A progress bar control can be created as a child control of any window by writing code; it can also be defined in a dialog resource
template.

CProgressCtrl Class Methods

The CProgressCtrlclass offers a minimal set of methods for manipulating the control and its data. The constructor, CProgressCtrl::CProgressCtrl(), allocates a
CProgressCtrlobject that is initialized with the CProgressCtrl::Create()method. Table 3.13 describes the class’s methods.

Table 3.13CProgressCtrlClass Methods

Method Description

OffsetPos() Advances the current position of a progress bar control by a specified increment and redraws
the bar to show the new position.
SetPos() Sets the current position for a progress bar control and redraws the bar to show the new
position.
SetRange() Sets the minimum and maximum ranges for a progress bar control and redraws the bar to
show the new ranges.
SetStep() Specifies the step increment for a progress bar control.
StepIt() Advances the current position for a progress bar control by the step increment and redraws
the bar to show the new position.

Creating and Initializing a CProgressCtrl Object

To create a CProgressCtrlobject, you use the two-step construction process typical of MFC:

1. Call the class constructor CProgressCtrl::CProgressCtrl()to allocate the object.


2. Initialize the CProgressCtrlobject and attach an actual Windows progress common control to it with a call to the CProgressCtrl::Create()method.

The prototype for the CProgressCtrl::Create()method is shown here:

BOOL Create(DWORD dwStyle, const RECT&rect,


CWnd* pParentWnd, UINT nID);

In this syntax, the parameters are defined as follows:

• dwStyleSpecifies the window style for the control. This can be any combination of the general window styles.
• rectThe rectangle specifying the size and position of the control.
• pParentWndA pointer to the owner of the control.
• nIDThe control ID used by the parent to communicate with the control.

Using a Progress Control

The only necessary settings for a progress control are the range and current position. The rangerepresents the entire duration of the operation. The current
positionrepresents the progress that your application has made toward completing the operation. Any changes to the range or position cause the progress
control to redraw itself.

The default range for a progress control is from 0 to 100, with the default initial position set to zero. Use the SetRange()method to change the range of the
progress control; use the SetPos()method to set the current position. Alternatively, you can change the position by a preset amount by calling the SetStep()
method to set an increment amount for the control (10 by default) and then calling the StepIt()method to change the position.

Note:

The StepIt()method wraps around to the minimum range if the maximum is exceeded. The OffsetPos()method, however, doesn’t wrap back
around to the minimum value—instead, the new position is adjusted to remain within the control’s specified range.

In the TAB1 program I demonstrate a progress bar control on the third tab, creating the progress bar like this:

// Create the progress bar control


if (!m_ctlProgress.Create(
WS_CHILD | WS_VISIBLE | WS_BORDER,
CRect(0,0,0,0), &m_ctlTab, IDC_PROGRESSCTRL))
{
TRACE0(_T(“Problem creating progress bar control!”));
return FALSE;
}

Next, set the lower and upper limits of the progress range with a call to CProgressCtrl::SetRange():

m_ctlProgress.SetRange(0, 100);

To force the visual aspect of progress occurring, I simply call the CProgressCtrl::SetPos()method, like this:

// Make some progress...


for (int i = 0; i <100; i++)
{
m_ctlProgress.SetPos(i);
this->Wait(20);
}

Image Lists: Class CImageList

An image listmaintains an array of images. Each image is the same size, and each element in the list is referred to by its zero-based index. To efficiently handle
large numbers of icons or bitmaps, all images in an image list are placed into a single memory bitmap stored in DDB format. This bitmap has the same height as
the images in the list; all the list’s images are contained side by side horizontally, usually making a very short, wide bitmap.

An image list can also include a monochrome bitmap mask used to draw images transparently. Win32 API image list functions give you the ability to draw
images, replace images, merge images, add and remove images, drag images, and create and destroy image lists. This functionality is used by other common
controls that make internal use of image lists, including list view, tree view, and tab controls. MFC provides the services of a Windows image list common control
in the class CImageList, which is derived directly from CObject.

CImageList Class Methods

The CImageListclass offers a complete set of methods for manipulating the images stored in a control. The CImageListconstructor, CImageList::CImageList(),
allocates a CImageListobject that’s initialized with the CImageList::Create()method. Table 3.14 describes the methods provided by CImageList.

Table 3.14CImageListClass Methods

Method Description

Add() Adds an image (or multiple images) to an image list.


Attach() Attaches an image list to a CImageListobject.
BeginDrag() Begins dragging an image.
DeleteImageList() Deletes an image list.
Detach() Detaches an image list object from a CImageListobject and returns a handle to an image list.
DragEnter() Locks the window to prevent updates during a drag operation and displays a drag image at the
specified location.
DragLeave() Unlocks the window and hides the drag image so that the window can be updated.
DragMove() Moves the image being dragged during a drag-and-drop operation.
DragShowNolock() Shows or hides the drag image during a drag operation without locking the window.
Draw() Draws the image being dragged during a drag-and-drop operation.
EndDrag() Ends a drag operation.
ExtractIcon() Creates an icon based on an image and mask in an image list.
GetBkColor() Gets the current background color for an image list.
GetDragImage() Gets the temporary image list used for dragging.
GetImageCount() Gets the number of images in an image list.
GetImageInfo() Gets information about an image.
GetSafeHandle() Gets the underlying Windows image list stored in m_hImageList.
Read() Reads an image list from an archive.
Remove() Removes an image from an image list.
Replace() Replaces an image in an image list with a new image.
SetBkColor() Sets the background color for an image list.
SetDragCursorImage() Creates a new drag image.
SetOverlayImage() Adds the zero-based index of an image to the list of images to be used as overlay masks.
Write() Writes an image list to an archive.

Creating and Initializing a CImageList Control

To create a CImageListobject, you use the two-step construction process typical of MFC:

1. Call the class constructor CImageList::CImageList()to allocate the object.


2. Initialize the CImageListobject and attach an actual Windows image list common control to it with a call to the CImageList::Create()method.

There are four overloaded prototypes for the CImageList::Create()method:


// 1
BOOL Create(int cx, int cy, BOOL bMask, int nInitial, int nGrow);

// 2
BOOL Create(UINT nBitmapID, int cx, int nGrow, COLORREF crMask);

// 3
BOOL Create(LPCTSTR lpszBitmapID, int cx, int nGrow,
COLORREF crMask);

// 4
BOOL Create(CImageList&imagelist1, int nImage1,
CImageList&imagelist2, int nImage2, int dx, int dy);

The parameters used by these prototypes are listed in Table 3.15.

Table 3.15The Parameters Used by the CImageListControl

Parameter Description

cx, cy Width and height of each image, in pixels.


dx, dy Width and height of each image, in pixels (same as cxand cy).
bMask A Boolean flag that specifies whether an image contains a monochrome mask.
nInitial The number of images initially contained in an image list.
nGrow The number of new images a resized image list can contain.
nBitmapID The resource ID of a bitmap to be associated with an image list.
crMask The color used to generate an image mask. Each pixel of this color in the specified bitmap is
changed to black.
lpszBitmapID A string that contains the resource IDs of all images stored in an image list.
imagelist1 A pointer to another CImageListobject.
nImage1 The number of images contained in imagelist1.
imagelist2 A pointer to another CImageListobject.
nImage2 The number of images contained in imagelist2.

List View Controls: Class CListCtrl

A list view controlis a window that provides several ways of arranging and displaying a list of items. Each item is made up of an icon and a label. Unlike the list
box control, a list view control can display list items using four different views, and the current view is specified by the control’s current window style. Table 3.16
describes the four view styles provided by the list view control.

Table 3.16The Four Types of Views Supported by the List View Common Control

View Description

Icon view In this view, list items are displayed as full-sized icons with labels below them, as specified by
the LVS_ICONwindow style. In this view, a user can drag list items to any location in the list
view window.
Small icon view In this view, list items are displayed as small icons with the labels to their right, as specified by
the LVS_SMALLICONwindow style. In this view, a user can drag list items to any location in the
list view window.
List view In this view, list items are displayed as small icons with labels to their right, as specified by the
LVS_LISTwindow style. In this view, items are arranged and fixed in columns; they can’t be
dragged to any other list view location.
Report view In this view, list items are displayed each on its own line, with information arranged in columns
as specified by the LVS_REPORTwindow style. The left column displays a small icon and a
label; the columns that follow contain application-specific subitems. Each column uses a
Win32 header common control unless the LVS_NOCOLUMNHEADERwindow style is also
specified.

As you’ll see later in this chapter, other window styles enable you to manage a list view control’s visual and functional aspects. Figure 3.6 shows four instances
of Explorer, using the four view styles.
Figure 3.6 Four instances of Explorer, showing the four view styles.

MFC provides the services of a Windows list view common control in the class CListCtrl, which is derived directly from CWndand inherits all the functionality of
CWnd.

A list view control can be created as a child control of any window by writing code; it can also be defined in a dialog resource template. A list view control sends
Windows notification messages to its owner (usually a CDialog-derived class), and these messages can be trapped and handled by writing message map entries
and message-handler methods for each message. These message map entries and methods are implemented in the list view control’s parent class.

List View Control Styles

Like all windows, list view controls can use the general window styles available to CWnd. In addition, list view controls use the list view styles listed in Table 3.17
(as defined in AFXCMN.H). A list view control’s styles determine its appearance and operations. Style bits are typically set when the control is initialized with the
CListCtrl::Create()method.

Tip:

To retrieve the style bits present in a control, use the Windows API function GetWindowLong(). To change the style bits after the control has been
initialized, use the corresponding Windows API function SetWindowLong().

Table 3.17The Window Styles Defined for a List View Common Control
Style Macro Meaning

LVS_ALIGNLEFT Items are left-aligned in icon and small icon view.


LVS_ALIGNTOP Items are aligned with the top of the control in icon and small icon view.
LVS_AUTOARRANGE Icons are automatically arranged in icon view and small icon view.
LVS_EDITLABELS Allows item text to be edited in place.
LVS_ICON Icon view.
LVS_LIST List view.
LVS_NOCOLUMNHEADER No column header is displayed in report view.
LVS_NOLABELWRAP Item text is displayed on a single line in icon view.
LVS_NOSCROLL Disables scrolling.
LVS_NOSORTHEADER Column headers don’t function as buttons.
LVS_OWNERDRAWFIXED Enables the owner window to paint items as desired while in report view.
LVS_REPORT Report view.
LVS_SHAREIMAGELISTS Enables image lists to be used by multiple list view controls.
LVS_SINGLESEL Allows only one item at a time to be selected.
LVS_SMALLICON Small icon view.
LVS_SORTASCENDING Sorts items in ascending order based on item text.
LVS_SORTDESCENDING Sorts items in descending order based on item text.

In addition, many styles defined for the new Windows common controls can be used (see Table 3.18). These styles determine how a common control positions
and resizes itself.

Table 3.18Windows Common Control Window Styles Used by a List View Control

Style Macro Meaning


CCS_BOTTOM The control aligns itself at the bottom of the parent window’s client area and sizes itself to the
width of its parent window’s client area.
CCS_NODIVIDER Prevents a two-pixel highlight from being drawn at the top of the control.
CCS_NOHILITE Prevents a one-pixel highlight from being drawn at the top of the control.
CCS_NOMOVEY Causes the control to resize and move itself horizontally (but not vertically) in response to a
WM_SIZEmessage (default).
CCS_NOPARENTALIGN Prevents the control from automatically aligning to the top or bottom of the parent window.
CCS_NORESIZE Forces a control to use the width and height specified when created or resized.
CCS_TOP The control aligns itself at the top of the parent window’s client area and sizes itself to the
width of its parent window’s client area.

Image Lists and the List View Control

The icons used by list view items are stored as image lists; there are three image lists available to a list view control:

• Large image list An image list that contains images of the full-sized icons used by the LVS_ICONlist view style.
• Small image list An image list that contains the images of the small icons used by views that don’t have the LVS_ICONlist view style.
• State image list An image list that can contain state images that can appear next to an item’s icon. These images are typically used to denote some
application-specific state.

The large and small icon image lists should contain an icon for each type of item in the list. These lists are created individually as needed, and each uses the
same index values. This arrangement means that images in the large icon list, for example, should correspond one to one with the images in the small icon list
and the state icon list.

If you use a state image list for a list view control, the control reserves space for the state image just to the left of the icon for each list item.

Note:
The large and small icon image lists can also contain overlay images that can be superimposed on list item icons. Because of a 4-bit indexing
scheme for overlay images, overlay images must be stored within the first 15 images in a list.

List View Items and Subitems

A list view control contains a list of items; each item consists of four parts:

• An icon
• A label
• A current state
• An application-defined value

Each item can also contain strings called subitems. These subitems are used in the report view, and each subitem is displayed in its own column. You can use
the CListCtrlmethods to add, modify, retrieve, find, and delete list view items.

Note:

Every item in a list view control must have the same number of subitems, even if the items represent different types of data.

A list view item or subitem is defined with a Windows LV_ITEMstructure, which looks like this:

typedef struct _LV_ITEM


{
UINT mask;
int iItem;
int iSubItem;
UINT state;
UINT stateMask;
LPSTR pszText;
int cchTextMax;
int iImage;
LPARAM lParam;
}
LV_ITEM;

The data members of this structure are as follows:

• maskA set of bit flags specifying the members of the LV_ITEMstructure that contain valid data or that need to be filled in (see Table 3.19).
• iItemThe zero-based index of an item.
• iSubItemThe one-based index of a subitem.
• stateThe current state of an item.
• stateMaskSpecifies the bits of the state member that are valid.
• pszTextA pointer to a string that contains the item text if the structure specifies item attributes.
• cchTextMaxThe size of the buffer pointed to by the pszTextmember.
• iImageThe index of an item’s icon in the icon and small icon image lists.
• lParamAn application-defined 32-bit value to associate with the item.

Table 3.19The Bit Flags Used for the LV_ITEMmaskMember

Value Meaning

LVIF_TEXT The pszTextmember is valid.


LVIF_IMAGE The iImagemember is valid.
LVIF_PARAM The lParammember is valid.
LVIF_STATE The statemember is valid.
LVIF_DI_SETITEM Windows should store the requested list item information.

List View Notification Messages


Like most Windows common controls, a list view control sends WM_NOTIFYnotification messages to its parent window. These messages can be trapped and
handled by writing message handlers in the list view control’s parent class. Table 3.20 shows the notifications used by list view controls, as defined in
COMMCTRL.H.

Table 3.20Notification Messages Defined for List View Controls

Message Map Entry Meaning

LVN_BEGINDRAG A drag-and-drop operation involving the left mouse button is beginning.


LVN_BEGINLABELEDIT A label-editing operation is beginning.
LVN_BEGINRDRAG A drag-and-drop operation involving the right mouse button is beginning.
LVN_COLUMNCLICK A column was clicked.
LVN_DELETEALLITEMS A user has deleted all items from the control.
LVN_DELETEITEM A user has deleted a single item from the control.
LVN_ENDLABELEDIT A label-editing operation is ending.
LVN_GETDISPINFO A request for the parent window to provide information needed to display or sort a list view
item.
LVN_INSERTITEM A new item was inserted.
LVN_ITEMCHANGED An item was changed.
LVN_ITEMCHANGING An item is changing.
LVN_KEYDOWN A key was pressed.
LVN_PEN Used for pen Windows (for systems with a pen and digitizer tablet).
LVN_SETDISPINFO Forces the parent to update display information for an item.

Creating and Initializing a CListCtrl Object

To create a CListCtrlobject, you use the two-step construction process typical of MFC:
1. Call the class constructor CListCtrl::CListCtrl()to allocate the object.
2. Initialize the CListCtrlobject and attach an actual Windows list view common control to it with a call to the CListCtrl::Create()method.

The prototype for the CListCtrl::Create()method is shown here:

BOOL Create(DWORD dwStyle, const RECT&rect,


CWnd* pParentWnd, UINT nID);

In this syntax, the parameters are as follows:

• dwStyleSpecifies the combination of styles used by a list control.


• rectSpecifies a list control’s size and position.
• pParentWndSpecifies the list control’s parent window.
• nIDSpecifies the control identifier for a list control.

The dwStyleparameter specifies the styles used by a list control, which can be any of the values listed in Table 3.17 (earlier in this chapter).

Note:

MFC encapsulates the list control in the view class CListView, allowing you to take advantage of the benefits of an integrated list view control by
allowing you to typecast a CListViewobject to a CListCtrlobject at runtime.

Using the List View Control

Using the list view control generally requires several steps, including these:

1. Attaching the image list(s).


2. Adding columns for report view.
3. Adding items to a CListCtrlobject.
4. Overriding the inherited OnChildNotify()method to handle WM_NOTIFYmessages.

The following sections look at each of these items a little more closely.

Attaching the Image List(s)

If the list view control you’re creating uses the LVS_ICONstyle, you’ll need image lists for the list view items. Use the CImageListclass to create an image list (or
lists) for the list view to display. Next, call the CListCtrl::SetImageList()for all image lists used by the control.

Adding Columns for Report View

Columns can be used only with the LVS_REPORTstyle, which is the report view. The report view typically uses the header common control (CHeaderCtrl) to allow
users to resize the column. Adding columns is easy: Simply initialize an LV_COLUMNstructure and call the InsertColumn()method to create each desired column.
An LV_COLUMNstructure is defined as follows:

typedef struct _LV_COLUMN


{
UINT mask; // Specifies valid data members
int fmt; // Column alignment specifier
int cx; // Width of column, in pixels
LPTSTR pszText; // Column heading
int cchTextMax; // Character size of pszText
int iSubItem; // Index of a subitem
}
LV_COLUMN;

The members for this structure are described following:

• maskSpecifies which members of this structure contain valid information. This member can be zero, or one or more of the values listed in Table 3.21.
• fmtSpecifies the alignment of the column heading and the subitem text in the column. This can be one of the following values: LVCFMT_CENTER
(centered text), LVCFMT_LEFT(flush-left text), or LVCFMT_RIGHT(flush-right text).
• cxSpecifies the pixel width of a column.
• pszTextA pointer to a column’s heading text string.
• cchTextMaxSpecifies the number of characters in the buffer pointed to by the pszTextmember.
• iSubItemSpecifies the index of subitem associated with the column.

Table 3.21The Possible Values for the LV_COLUMNmaskMember

Value Meaning

LVCF_FMT The fmtmember is valid.


LVCF_SUBITEM The iSubItemmember is valid.
LVCF_TEXT The pszTextmember is valid.
LVCF_WIDTH The cxmember is valid.

Note:

The LV_COLUMNstructure is used with the LVM_GETCOLUMN, LVM_SETCOLUMN, LVM_INSERTCOLUMN, and LVM_DELETECOLUMNlist view
control messages. The CListCtrlclass wraps these messages with the default handler methods GetColumn(), SetColumn(), InsertColumn(), and
DeleteColumn().

Adding Items to a CListCtrl Object

Depending on the type of data that’s going into a list view control, you can call one of the overloaded InsertItem()methods. Each version of this method takes a
different type of data, and the list view control manages the storage for list items. For example, Listing 3.2 shows a code fragment from the TREELIST program
that adds the names of items found in the tree control (m_ctlTree) to a list control (m_ctlList).

Listing 3.2Adding Items to a List Control


///////////////////////////////////////////////////////////////////
// CMainWnd::ShowChildren()

void CMainWnd::ShowChildren(HTREEITEM hti)


{
m_ctlList.DeleteAllItems();

HTREEITEM htiNext = 0;
HTREEITEM htiChild = m_ctlTree.GetChildItem(hti);

if (htiChild)
{
// Add the child’s tree text to the list
int i = 0;
CString str = m_ctlTree.GetItemText(htiChild);

m_ctlList.InsertItem(i, (LPCTSTR) str);


htiNext = htiChild;

// Add sibling tree text to the list


while (TRUE)
{
htiNext = m_ctlTree.GetNextSiblingItem(htiNext);
if (!htiNext) return;

CString str = m_ctlTree.GetItemText(htiNext);


i++;
m_ctlList.InsertItem(i, (LPCTSTR) str);
}
}
}

Tree View Controls: Class CTreeCtrl

A tree view control is a window that provides a hierarchical view of some set of data, such as a directory structure on a disk. Each item in the tree is made up of
a label and an optional bitmap. Each item can own a list of subitems; by clicking an item, a user can expand or collapse the tree to reveal or hide subitems. MFC
provides the services of a Windows tree view common control in the class CTreeCtrl, which is derived directly from CWndand inherits all the functionality of CWnd.
A tree view control can be created as a child control of any window by writing code, or it can be defined in a dialog resource template. A tree view control sends
Windows notification messages to its owner (usually a CDialog-derived class), and these messages can be trapped and handled by writing message map entries
and message-handler methods for each message. These message map entries and methods are implemented in the list view control’s parent class.

Tree View Control Styles

Like all windows, tree view controls can use the general window styles available to CWnd. In addition, tree view controls use the tree view styles listed in Table
3.22 (as defined in COMMCTRL.H). A list view control’s styles determine its appearance and operation. Style bits are typically set when the control is initialized
with the CTreeCtrl::Create()method.

Table 3.22The Window Styles Defined for a Tree View Control

Style Macro Meaning

TVS_HASLINES Child items have lines linking them to corresponding parent items.
TVS_LINESATROOT Child items have lines linking them to the root of the tree.
TVS_HASBUTTONS The tree has a button to the left of each parent item.
TVS_EDITLABELS Tree view item labels can be edited.
TVS_SHOWSELALWAYS A selected item will remain selected, even if the tree view control loses the input focus.
TVS_DISABLEDRAGDROP Prevents the tree view control from sending TVN_BEGINDRAGnotification messages.

Tree View Notification Messages

Like most of the Windows common controls, a tree view control sends WM_NOTIFYnotification messages to its parent window, which is usually a CDialog-derived
class. These messages can be trapped and handled by writing message handlers in the list view control’s parent class. Table 3.23 shows the notification
messages defined in COMMCTRL.H.
Table 3.23Notification Messages used by CTreeCtrlObjects

Notification Meaning

TVN_BEGINDRAG A drag-and-drop operation has begun.


TVN_BEGINLABELEDIT In-place label editing has begun.
TVN_BEGINRDRAG A drag-and-drop operation, using the right mouse button, has begun.
TVN_DELETEITEM A specific item has been deleted.
TVN_ENDLABELEDIT In-place label editing has ended.
TVN_GETDISPINFO Gets information that the tree control requires to display an item.
TVN_ITEMEXPANDED A parent item’s list of child items was expanded or collapsed.
TVN_ITEMEXPANDING A parent item’s list of child items is about to be expanded or collapsed.
TVN_KEYDOWN A key was pressed down.
TVN_SELCHANGED The current selection has changed from one item to another.
TVN_SELCHANGING The selection is about to change from one item to another.
TVN_SETDISPINFO Updates the information maintained for an item.

CTreeCtrl Class Methods

The CTreeCtrlclass offers a full set of methods for manipulating the control and its data. The CTreeCtrlconstructor, CTreeCtrl::CTreeCtrl(), allocates a CTreeCtrlobject
that is initialized with the CTreeCtrl::Create()method to set attributes and ownership. Table 3.24 describes the methods used to get and set button control
attributes, as well as methods that perform operations on the control and its data.

Table 3.24CTreeCtrlClass Methods

Method Description
CreateDragImage() Creates a dragging bitmap for the specified tree view item.
DeleteAllItems() Deletes all items in a tree view control.
DeleteItem() Deletes a new item in a tree view control.
EditLabel() Edits a specified tree view item in place.
EnsureVisible() Ensures that a tree view item is visible in its tree view control.
Expand() Expands or collapses the child items of the specified tree view item.
GetChildItem() Gets the child of a specified tree view item.
GetCount() Gets the number of tree items associated with a tree view control.
GetDropHilightItem() Gets the target of a drag-and-drop operation.
GetEditControl() Gets the handle of the edit control used to edit the specified tree view item.
GetFirstVisibleItem() Gets the first visible item of the specified tree view item.
GetImageList() Gets the handle of the image list associated with a tree view control.
GetIndent() Gets the offset (in pixels) of a tree view item from its parent.
GetItem() Gets the attributes of a specified tree view item.
GetItemData() Gets the 32-bit application-specific value associated with an item.
GetItemImage() Gets the images associated with an item.
GetItemRect() Gets the bounding rectangle of a tree view item.
GetItemState() Gets the state of an item.
GetItemText() Gets the text of an item.
GetNextItem() Gets the next tree view item that matches a specified relationship.
GetNextSiblingItem() Gets the next sibling of the specified tree view item.
GetNextVisibleItem() Gets the next visible item of the specified tree view item.
GetParentItem() Gets the parent of the specified tree view item.
GetPrevSiblingItem() Gets the previous sibling of the specified tree view item.
GetPrevVisibleItem() Gets the previous visible item of the specified tree view item.
GetRootItem() Gets the root of the specified tree view item.
GetSelectedItem() Gets the currently selected tree view item.
GetVisibleCount() Gets the number of visible tree items associated with a tree view control.
HitTest() Gets the current position of the cursor related to the CTreeCtrlobject.
InsertItem() Inserts a new item in a tree view control.
ItemHasChildren() Determines whether an item has child items.
Select() Selects, scrolls into view, or redraws a specified tree view item.
SelectDropTarget() Redraws the tree item as the target of a drag-and-drop operation.
SelectItem() Selects a specified tree view item.
SetImageList() Sets the handle of the image list associated with a tree view control.
SetIndent() Sets the offset (in pixels) of a tree view item from its parent.
SetItem() Sets the attributes of a specified tree view item.
SetItemData() Sets the 32-bit application-specific value associated with an item.
SetItemImage() Associates images with an item.
SetItemState() Sets the state of an item.
SetItemText() Sets the text of an item.
SortChildren() Sorts the children of a given parent item.
SortChildrenCB() Sorts the children of a given parent item using an application-defined sort function.

Creating and Initializing a Tree View Control

To create a CTreeCtrlobject, you use the two-step construction process typical of MFC:

1. Call the class constructor CTreeCtrl::CTreeCtrl()to allocate the object.


2. Initialize the CTreeCtrlobject and attach an actual Windows tree view common control to it with a call to the CTreeCtrl::Create()method.

The prototype for the CTreeCtrl::Create()method is shown here:

BOOL Create(DWORD dwStyle, const RECT&rect,


CWnd* pParentWnd, UINT nID);

In this syntax, the parameters are as follows:


• dwStyleSpecifies the window style for the control. This can be any combination of the general window styles and the special tree view styles listed in
Table 3.22.
• rectThe rectangle specifying the size and position of the control.
• pParentWndA pointer to the owner of the control.
• nIDThe control ID used by the parent to communicate with the tree view control.

Using a CTreeCtrl Object

If a tree control is to use images, create and set an image list by calling SetImageList(). You can further initialize the control by calling SetIndent()to change the
indentation. Changing the indentation is usually done once, when the control is first initialized, typically in OnInitDialog()for a dialog box or in OnInitialUpdate()for a
view.

Use the InsertItem()method to add data items to the control. Each call to InsertItem()results in an item handle being returned for each item added to the tree.
These handles should be saved for later use.

Use an ON_NOTIFYmacro in the message map entry for control notifications in the parent class, or make the class more reusable by placing an
ON_NOTIFY_REFLECTmacro in the control window’s message map to let it handle its own notifications. The tree control’s notification messages are listed in
Table 3.23, earlier in this chapter.

Sample Program: TREELIST.EXE

On the companion CD-ROM, you’ll find the program TREELIST.EXE. This program uses the tree view and list view control classes (see Figure 3.7).
Figure 3.7 The TREELIST program.

The TREELIST program uses a CTreeCtrlobject and a CListCtrlobject; the list displays the children (if any) of the currently selected tree item. Tree item labels can
also be edited in place.

Tab Controls: Class CTabCtrl

A tab controlis a GUI metaphor for the tabs found on file folders. A tab control is a window that can be divided into several pages, each of which typically has a
set of controls. Each tab page provides a tab that, when clicked, displays its corresponding page. Figure 3.8 shows a typical tab window used for displaying
various controls.
Figure 3.8 A typical tab control window.

Note:

A tab control can also display buttons in place of tabs. Clicking a button should immediately perform a command instead of displaying a page.

MFC provides the services of a Windows tab common control in the class CTabCtrl, which is derived directly from CWndand inherits all the functionality of CWnd.
A tab control can be created as a child control of any window by writing code; it can also be defined in a dialog resource template.

A tab control sends Windows notification messages to its owner (usually a CDialog-derived class), and these messages can be trapped and handled by writing
message map entries and message-handler methods for each message. These message map entries and methods are implemented in the tab control’s parent
class.

Tab Control Styles

Like all windows, tab controls can use the general window styles available to CWnd. They can also use the additional styles listed in Table 3.25. A tab control’s
styles determine its appearance and operations; style bits are typically set when the control is initialized with the CTabCtrl::Create()method.

Tip:

To retrieve the style bits present in a control, use the Windows API function GetWindowLong(). To change the style bits after the control has been
initialized, use the corresponding Windows API function SetWindowLong().

Table 3.25The Window Styles Designed for Use with Tab Controls

Style Macro Meaning

TCS_BUTTONS Makes tabs appear as standard pushbuttons.


TCS_FIXEDWIDTH Makes all tabs the same width.
TCS_FOCUSNEVER Specifies that a tab never receives the input focus.
TCS_FOCUSONBUTTONDOWN A tab will receive the input focus when clicked (typically used only with the
TCS_BUTTONSstyle).
TCS_FORCEICONLEFT Forces a tab’s icon to the left but leaves the tab label centered.
TCS_FORCELABELLEFT Left-aligns both the icon and label.
TCS_MULTILINE A tab control displays multiple rows of tabs to ensure that all tabs can be displayed at once.
TCS_OWNERDRAWFIXED The parent window draws the tabs for the control.
TCS_RAGGEDRIGHT A default style that doesn’t force each row of tabs to fill the width of the control.
TCS_RIGHTJUSTIFY Right-justifies tabs.
TCS_SHAREIMAGELISTS A tab control’s image lists aren’t destroyed with the control. This allows multiple controls to use
the same image lists.
TCS_SINGLELINE Displays all tabs in a single row.
TCS_TABS The standard tab style; specifies that tabs appear as tabs (as opposed to buttons) and that a
border is drawn around the display area.
TCS_TOOLTIPS The tab control uses a ToolTip control.

Tab Control Notification Messages

Like most Windows common controls, a tab control sends WM_NOTIFYnotification messages to its parent window, which is usually a CDialog-derived class.
Table 3.26 shows the notification messages used by the tab control, as defined in COMMCTRL.H.

Table 3.26Notification Messages Used by the Tab Control

Notification Meaning

TCN_KEYDOWN A key was pressed.


TCN_SELCHANGE The currently selected tab has changed.
TCN_SELCHANGING The currently selected tab is about to change. By returning TRUEin response to this
notification, you can prevent the selection from changing.
Tip:

Use the GetCurSel()method to determine the currently selected tab.

CTabCtrl Class Methods

The CTabCtrlclass offers a complete set of methods for manipulating the control and its data. The CTabCtrlconstructor, CTabCtrl::CTabCtrl(), allocates a
CTabCtrlobject that is initialized with the CTabCtrl::Create()method to set attributes and ownership. The CTabCtrlclass methods are described in Table 3.27.

Table 3.27CTabCtrlClass Methods

Method Description

AdjustRect() Calculates a tab control’s display area given a window rectangle; alternatively, calculates the
window rectangle that corresponds to a given display area.
DeleteAllItems() Removes all items from a tab control.
DeleteItem() Removes an item from a tab control.
DrawItem() Draws a specified tab control item.
GetCurFocus() Gets the tab control tab that has the input focus.
GetCurSel() Gets the currently selected tab control tab.
GetImageList() Gets the image list associated with a tab control.
GetItem() Gets information about a tab in a tab control.
GetItemCount() Gets the number of tabs in the tab control.
GetItemRect() Gets the bounding rectangle for a tab in a tab control.
GetRowCount() Gets the current number of rows of tabs in a tab control.
GetTooltips() Gets the handle of the ToolTip control associated with a tab control.
HitTest() Determines which tab (if any) is located at a specified screen position.
InsertItem() Inserts a new tab into a tab control.
RemoveImage() Removes an image from a tab control’s image list.
SetCurSel() Selects one of a tab control’s tabs.
SetImageList() Assigns an image list to a tab control.
SetItem() Sets some or all of a tab’s attributes.
SetItemSize() Sets the width and height of an item.
SetPadding() Sets the amount of padding around each tab’s icon and label in a tab control.
SetTooltips() Assigns a ToolTip control to a tab control.

The Tab Item Structure (TC_ITEM)

The GetItem(), SetItem(), and InsertItem()methods all take a parameter of type TC_ITEM. This is a new datatype for Win32 that specifies or receives the attributes
of a tab. The TC_ITEMstructure has the following form:

typedef struct _TC_ITEM


{
UINT mask;
UINT lpReserved1; // reserved; do not use
UINT lpReserved2; // reserved; do not use
LPSTR pszText;
int cchTextMax;
int iImage;
LPARAM lParam;
}
TC_ITEM;

The members of this structure are as follows:

• maskA value specifying which members to retrieve or set. This member can be all members (TCIF_ALL), zero (0), or one or more of the values listed in
Table 3.28.
• pszTextA pointer to a string containing the tab text.
• cchTextMaxThe size of the buffer pointed to by the pszTextmember.
• iImageThe index into the tab control’s image list, or –1 if the tab has no image.
• lParamApplication-defined data associated with the tab.

Table 3.28Tab Item Structure maskValues

Value Description

TCIF_IMAGE The iImagemember is valid.


TCIF_PARAM The lParammember is valid.
TCIF_RTLREADING Displays the text pointed to by pszTextusing right-to-left reading order when running on Hebrew
or Arabic systems.
TCIF_TEXT The pszTextmember is valid.

Creating and Initializing a Tab Control

To create a CTabCtrlobject, you use the two-step construction process typical of MFC:

1. Call the class constructor CTabCtrl::CTabCtrl()to allocate the object.


2. Initialize the CTabCtrlobject and attach an actual Windows tab common control to it with a call to the CTabCtrl::Create()method.

The prototype for the CTabCtrl::Create()method is shown here:

BOOL Create(DWORD dwStyle, const RECT&rect,


CWnd* pParentWnd, UINT nID);

In this syntax, the parameters are as follows:

• dwStyleSpecifies the combination of styles used by the control.


• rectSpecifies a control’s size and position.
• pParentWndSpecifies a control’s parent window.
• nIDSpecifies the control identifier for a control.

Note:

As with other Windows controls, a tab control is created implicitly when used in a dialog box; a tab control is created explicitly when created in a
nondialog window.

Using a Tab Control

After the CTabCtrlobject is constructed, you add tabs to the tab control to complete its initialization. You are responsible for handling any tab notification
messages that apply to your application.

Adding Tabs to a Tab Control

After constructing the CTabCtrlobject, add tabs as needed by preparing a TC_ITEMstructure and calling the CTabCtrl::InsertItem()method. Pass the
TC_ITEMstructure as a parameter as shown in this simple example:

// Initialize the TC_ITEM structures


TC_ITEM tci;

CString str = “Tab 1”;

tci.mask = TCIF_TEXT;
tci.pszText = (LPSTR)(LPCTSTR)str;
tci.cchTextMax = str.GetLength();

// Add this tab to the tab control


m_ctlTab.InsertItem(0, &tci);

Note this line of code in the preceding example:


tci.pszText = (LPSTR)(LPCTSTR)str;

The CStringvariable struses the LPCTSTRoperator to get a constpointer to the character string contained in the string object. The constis then cast away to the
LPSTRexpected by tci.pszText.

Trapping Tab Notification Messages

The final step in dealing with tab controls is handling any tab notifications for your application. A simple notification handler that fires when a user clicks the
control is given in Listing 3.3. In this case, the hypothetical tab control (m_ctlTab) is assumed to be