50% found this document useful (2 votes)
2K views1,181 pages

MFC Programming With Visual C++ 6 Unleashed 1999

old mfc book but still good for advanced mfc programer

Uploaded by

Once More
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
50% found this document useful (2 votes)
2K views1,181 pages

MFC Programming With Visual C++ 6 Unleashed 1999

old mfc book but still good for advanced mfc programer

Uploaded by

Once More
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/ 1181

Table of Contents

Introduction
...................................................................................................................................................................................... 1 Foreword ...................................................................................................................................................................................... 4 About the Author ........................................................................................................................................................................ 6 Core MFC ...................................................................................................................................................................................... 10 The MFC Architecture ........................................................................................................................................................... 10 A Brief History of MFC ...................................................................................................................................................... 10 The MFC Class Hierarchy ................................................................................................................................................ 10 CObject ........................................................................................................................................................................ 10 CCmdTarget ................................................................................................................................................................. 10 CWinThread ................................................................................................................................................................. 10 CWinApp ...................................................................................................................................................................... 10 CWnd ........................................................................................................................................................................... 10 CFrameWnd ................................................................................................................................................................. 10 CView ........................................................................................................................................................................... 10 CDocument .................................................................................................................................................................. 10 Summary ............................................................................................................................................................................ 10 MFC Dialogs, Controls, and Data Interaction ................................................................................................................ 36 Creating an Application ..................................................................................................................................................... 36 Starting and Using MFC AppWizard ............................................................................................................................. 36 The AppWizard-Generated Code .................................................................................................................................. 36 Modifying the Application .................................................................................................................................................. 36 Adding Dialog Controls ................................................................................................................................................. 36 Adding Initialization ....................................................................................................................................................... 36 Using Dialog Controls ................................................................................................................................................... 36 ToolTips ............................................................................................................................................................................... 36 Enabling ToolTips ......................................................................................................................................................... 36 Displaying Text ............................................................................................................................................................. 36 Dialog Data Exchange ...................................................................................................................................................... 36 Standard DDX .............................................................................................................................................................. 36 UpdateData .................................................................................................................................................................. 36 Using Standard Dialog Boxes .......................................................................................................................................... 36 File Open/Save ............................................................................................................................................................. 36 Color Selector ............................................................................................................................................................... 36 Font Selector ................................................................................................................................................................ 36 Print Dialog ................................................................................................................................................................... 36 Summary ............................................................................................................................................................................ 36 The Windows Common Controls ....................................................................................................................................... 60 Initializing and Using the Common Controls ................................................................................................................. 60 Notifications for Windows Common Controls ................................................................................................................ 60 The Notification Message Structure .............................................................................................................................. 60 Overview of the Notification Process ............................................................................................................................ 60 A Better Notification Handling Scheme ......................................................................................................................... 60 Specifying Notification Ranges with ON_NOTIFY_RANGE ......................................................................................... 60 Hot Key Controls: Class CHotKeyCtrl ............................................................................................................................ 60 CHotKeyCtrl Class Methods ......................................................................................................................................... 60 Creating and Initializing a CHotKeyCtrl Object ............................................................................................................. 60 Using a Hot Key Control ............................................................................................................................................... 60 Spin Controls: Class CSpinButtonCtrl ............................................................................................................................ 60 Spin Control Styles ....................................................................................................................................................... 60 CSpinButtonCtrl Messages ........................................................................................................................................... 60

CSpinButtonCtrl Class Methods ................................................................................................................................... 60 Creating and Initializing a Spin Control ......................................................................................................................... 60 Sample Program: SPIN1 .............................................................................................................................................. 60 Slider Controls: Class CSliderCtrl ................................................................................................................................... 60 Slider Control Styles ..................................................................................................................................................... 60 CSliderCtrl Messages ................................................................................................................................................... 60 CSliderCtrl Class Methods ............................................................................................................................................ 60 Creating and Initializing a Slider Control ....................................................................................................................... 60 Sample Program: Slider Controls (SLIDER1) ............................................................................................................... 60 Sample Program: SLIDER1 .......................................................................................................................................... 60 Progress Bar Controls: Class CProgressCtrl ................................................................................................................ 60 CProgressCtrl Class Methods ...................................................................................................................................... 60 Creating and Initializing a CProgressCtrl Object .......................................................................................................... 60 Using a Progress Control .............................................................................................................................................. 60 Image Lists: Class CImageList ........................................................................................................................................ 60 CImageList Class Methods ........................................................................................................................................... 60 Creating and Initializing a CImageList Control ............................................................................................................. 60 List View Controls: Class CListCtrl ................................................................................................................................. 60 List View Control Styles ................................................................................................................................................ 60 Image Lists and the List View Control ........................................................................................................................... 60 List View Items and Subitems ......................................................................................................................................... 60 List View Notification Messages ................................................................................................................................... 60 Creating and Initializing a CListCtrl Object ................................................................................................................... 60 Using the List View Control ........................................................................................................................................... 60 Tree View Controls: Class CTreeCtrl .............................................................................................................................. 60 Tree View Control Styles .............................................................................................................................................. 60 Tree View Notification Messages .................................................................................................................................. 60 CTreeCtrl Class Methods ............................................................................................................................................. 60 Creating and Initializing a Tree View Control ............................................................................................................... 60 Using a CTreeCtrl Object .............................................................................................................................................. 60 Sample Program: TREELIST.EXE ................................................................................................................................ 60 Tab Controls: Class CTabCtrl ........................................................................................................................................... 60 Tab Control Styles ........................................................................................................................................................ 60 Tab Control Notification Messages ............................................................................................................................... 60 CTabCtrl Class Methods ............................................................................................................................................... 60 The Tab Item Structure (TC_ITEM) ............................................................................................................................... 60 Creating and Initializing a Tab Control .......................................................................................................................... 60 Using a Tab Control ...................................................................................................................................................... 60 Animate Controls: Class CAnimateCtrl .......................................................................................................................... 60 Animate Control Styles ................................................................................................................................................. 60 Animate Control Notification Messages ........................................................................................................................ 60 CAnimateCtrl Class Methods ........................................................................................................................................ 60 Creating and Initializing an Animate Control ................................................................................................................ 60 Using an Animate Control ............................................................................................................................................. 60 Rich Edit Controls: Class CRichEditCtrl ......................................................................................................................... 60 Rich Edit Control Window Styles .................................................................................................................................. 60 The Character Format Structure (CHARFORMAT) ....................................................................................................... 60 The Paragraph Format Structure (PARAFORMAT) ....................................................................................................... 60 CRichEditCtrl Class Methods ....................................................................................................................................... 60 CRichEditCtrl Line-Related Methods ............................................................................................................................ 60 CRichEditCtrl Text-Selection Methods .......................................................................................................................... 60 CRichEditCtrl Formatting Methods ............................................................................................................................... 60 CRichEditCtrl Editing Methods ..................................................................................................................................... 60 CRichEditCtrl Clipboard Methods ................................................................................................................................. 60

CRichEditCtrl General-Purpose Methods ...................................................................................................................... 60 Creating and Initializing a Rich Edit Control ................................................................................................................. 60 Using a Rich Edit Control ............................................................................................................................................. 60 Summary ............................................................................................................................................................................ 60 Painting, Device Contexts, Bitmaps, and Fonts ......................................................................................................... 118 Device Contexts .............................................................................................................................................................. 118 The Graphics Device Interface ..................................................................................................................................... 118 MFC Wrapping ........................................................................................................................................................... 118 MFC Device Context Classes ....................................................................................................................................... 118 The Base Class: CDC ................................................................................................................................................ 118 Painting with Class CPaintDC .................................................................................................................................... 118 Managing Client Areas with Class CClientDC ............................................................................................................ 118 Managing Frame Windows with Class CWindowDC .................................................................................................. 118 Windows Graphic Objects ............................................................................................................................................. 118 Pens: Class CPen ...................................................................................................................................................... 118 Brushes: Class CBrush .............................................................................................................................................. 118 Fonts: Class CFont .................................................................................................................................................... 118 Bitmaps: Class CBitmap ............................................................................................................................................ 118 Palettes: Class CPalette ............................................................................................................................................ 118 Regions: Class CRgn ................................................................................................................................................. 118 GDI Coordinate Systems ............................................................................................................................................... 118 Logical Mapping Modes ............................................................................................................................................. 118 Vector Graphics ............................................................................................................................................................... 118 Drawing Modes .......................................................................................................................................................... 118 Lines and Polylines .................................................................................................................................................... 118 Rectangles ................................................................................................................................................................. 118 Regions ..................................................................................................................................................................... 118 Polygons .................................................................................................................................................................... 118 Ellipses ...................................................................................................................................................................... 118 Bezier Curves ............................................................................................................................................................ 118 Fonts and Text ................................................................................................................................................................. 118 Font Characteristics ................................................................................................................................................... 118 The TEXTMETRIC Structure ...................................................................................................................................... 118 The LOGFONT Structure ........................................................................................................................................... 118 Font Creation ............................................................................................................................................................. 118 Drawing Text .............................................................................................................................................................. 118 Sample Program: Vector Graphics and Text Methods (VECTEXT1.EXE) ............................................................ 118 Raster Graphics .............................................................................................................................................................. 118 Named Raster Operations (ROPs) ............................................................................................................................. 118 Bitmaps ..................................................................................................................................................................... 118 Device-Dependent Bitmaps ....................................................................................................................................... 118 Device-Independent Bitmaps (DIBs) .......................................................................................................................... 118 The CBitmap Class .................................................................................................................................................... 118 Transferring and Contorting Bitmaps .......................................................................................................................... 118 Bitmap Resources .......................................................................................................................................................... 118 Tacking Resources onto an Executable File .............................................................................................................. 118 Getting Image Resources out of an Executable File ................................................................................................. 118 Sample Program: Exploring Bitmap Resources (BITMAP1) ................................................................................... 118 Summary .......................................................................................................................................................................... 118 Custom Control Development .......................................................................................................................................... 173 Window Classes Versus C++ Classes ........................................................................................................................ 173 A Validating Edit Control ................................................................................................................................................ 173 The Clock Static Control ................................................................................................................................................ 173 Control Metrics .......................................................................................................................................................... 173

Painting the Face ...................................................................................................................................................... 173 Locating the Hands .................................................................................................................................................... 173 Painting the Hands .................................................................................................................................................... 173 Setting the Time ........................................................................................................................................................ 173 Pitfalls of Subclassing Standard Controls .................................................................................................................. 173 The Hyperlink Control .................................................................................................................................................... 173 Implementation Strategy ............................................................................................................................................ 173 Font Processing ........................................................................................................................................................ 173 Painting the Window .................................................................................................................................................. 173 Controlling the Cursor ................................................................................................................................................ 173 Mouse Input ............................................................................................................................................................... 173 Keyboard Input .......................................................................................................................................................... 173 Launching the Link .................................................................................................................................................... 173 Advanced Custom Control Topics ................................................................................................................................ 173 Subclassing Limitations ............................................................................................................................................. 173 Notifications ............................................................................................................................................................... 173 Using the Resource Editor with Custom Classes ...................................................................................................... 173 Summary .......................................................................................................................................................................... 173 The MFC Application Object, Message Routing, and Idle Processing .............................................................. 195 The MFC Application Object ......................................................................................................................................... 195 CWinApp and Application Lifetime ............................................................................................................................. 195 The CWinApp Data Members .................................................................................................................................... 195 The CWinApp Member Functions .............................................................................................................................. 195 Application-Specific Initialization ................................................................................................................................ 195 Functionality in InitInstance ....................................................................................................................................... 195 OLE Container Support ............................................................................................................................................. 195 3D Look for Windows NT 3.5x ................................................................................................................................... 195 Registry Usage .......................................................................................................................................................... 195 Most Recently Used Files List ................................................................................................................................... 195 SDI and MDI Document/View .................................................................................................................................... 195 Main Frame Window Creation ................................................................................................................................... 195 Automation Support ................................................................................................................................................... 195 Rich Edit Control Support .......................................................................................................................................... 195 Command-Line Handling ........................................................................................................................................... 195 Message Routing, Message Maps, and Message Categories ............................................................................... 195 Message Routing ....................................................................................................................................................... 195 PreTranslateMessage ................................................................................................................................................ 195 Message Maps .......................................................................................................................................................... 195 Idle Processing ............................................................................................................................................................... 195 OnIdle ........................................................................................................................................................................ 195 Idle Processing for Dialogs ........................................................................................................................................ 195 The Splash Screen Component ................................................................................................................................... 195 Summary .......................................................................................................................................................................... 195 Documents, Views, and Applications That Use Them ................................................................................. 218 The Document/View Architecture ................................................................................................................................... 218 Documents, Frames, and Views .................................................................................................................................. 218 Document Templates ................................................................................................................................................. 218 Creating New Documents ............................................................................................................................................. 218 Opening New Files .................................................................................................................................................... 218 Single Versus Multiple Document Templates ............................................................................................................. 218 Views ................................................................................................................................................................................ 218 The CView Class ....................................................................................................................................................... 218 The CScrollView Class .............................................................................................................................................. 218 The CFormView Class ............................................................................................................................................... 218

The Database View Classes ...................................................................................................................................... 218 The Control Views ..................................................................................................................................................... 218 Changing Views in an SDI ......................................................................................................................................... 218 Using the MDI ............................................................................................................................................................ 218 Summary .......................................................................................................................................................................... 218 Extending the User Interface ............................................................................................................................................ 237 Responding to the User ................................................................................................................................................. 237 Keyboard Messaging ................................................................................................................................................. 237 Handling Keyboard Messages ................................................................................................................................... 237 Mouse Messaging ..................................................................................................................................................... 237 Handling Mouse Messages ........................................................................................................................................ 237 User Interfaces and AppWizard ................................................................................................................................... 237 Extending Menus ............................................................................................................................................................ 237 Obtaining Menus and Pop-up Menus ......................................................................................................................... 237 Adding Menu Items .................................................................................................................................................... 237 Using Floating Pop-up Menus .................................................................................................................................... 237 Putting Control Bars to Use .......................................................................................................................................... 237 Using Toolbars and Rebars ........................................................................................................................................ 237 Using Status Bars ...................................................................................................................................................... 237 Showing and Hiding Control Bars .............................................................................................................................. 237 Supporting ToolTips ................................................................................................................................................... 237 Updating the User Interface .......................................................................................................................................... 237 Property Sheets and Wizards ....................................................................................................................................... 237 MFC's Support for Property Sheets and Wizards ...................................................................................................... 237 Creating a Simple Wizard .......................................................................................................................................... 237 Splitting a View ............................................................................................................................................................... 237 Summary .......................................................................................................................................................................... 237 Printing .................................................................................................................................................................................... 271 Printing Fundamentals ................................................................................................................................................... 271 Printing with MFC ........................................................................................................................................................... 271 Printing in the View .................................................................................................................................................... 271 The PrintInfo Object ................................................................................................................................................... 271 Printing Menu Commands ......................................................................................................................................... 271 Printing and GDI Mapping Modes ................................................................................................................................ 271 WYSIWYG Printing ........................................................................................................................................................ 271 Application Resources ............................................................................................................................................... 271 The Application Class ................................................................................................................................................ 271 The View Class .......................................................................................................................................................... 271 Pagination ........................................................................................................................................................................ 271 Printing with a Known Page Count ............................................................................................................................. 271 Printing with an Unknown Page Count ....................................................................................................................... 271 Printing Page Numbers ............................................................................................................................................. 271 Stopping and Aborting Print Jobs ................................................................................................................................. 271 Halting a Print Job in OnPrepareDC() ........................................................................................................................ 271 Halting a Print Job in OnPrint() .................................................................................................................................. 271 Summary .......................................................................................................................................................................... 271 MFC and COM Programming ....................................................................................................................................... 291 COM ......................................................................................................................................................................................... 291 A Little History ................................................................................................................................................................. 291 Interfaces, Objects, and Methods ................................................................................................................................ 291 Some Terminology ..................................................................................................................................................... 291 A Real-World View ..................................................................................................................................................... 291 The IUnknown Interface ............................................................................................................................................. 291 Servers, Clients, and Classes ...................................................................................................................................... 291

291 Defining the Class Factory ......................................................................................................................................... 291 How Are COM Objects Reused? ................................................................................................................................ 291 Marshaling and Threading ............................................................................................................................................ 291 Marshaling ................................................................................................................................................................. 291 Threading .................................................................................................................................................................. 291 COM, OLE, and Automation ......................................................................................................................................... 291 IDispatch ................................................................................................................................................................... 291 Automation Servers, Objects, and Controllers ........................................................................................................... 291 Supporting Both IDispatch and IUnknown .................................................................................................................. 291 Persisting COM Data ..................................................................................................................................................... 291 Structured Storage .................................................................................................................................................... 291 Identifying COM Data (Monikers) ................................................................................................................................. 291 Transferring Data ............................................................................................................................................................ 291 Uniform Data Transfer ............................................................................................................................................... 291 Connectable Objects ................................................................................................................................................. 291 DCOM ............................................................................................................................................................................... 291 Object Creation ......................................................................................................................................................... 291 Invoking Methods ...................................................................................................................................................... 291 Security Issues .......................................................................................................................................................... 291 Some Important Information ......................................................................................................................................... 291 BSTR ......................................................................................................................................................................... 291 SAFEARRAY ............................................................................................................................................................. 291 HRESULT .................................................................................................................................................................. 291 VARIANT ................................................................................................................................................................... 291 Further Reading .............................................................................................................................................................. 291 Summary .......................................................................................................................................................................... 291 COM and MFC ...................................................................................................................................................................... 316 Understanding the Afx Global Functions ..................................................................................................................... 316 Application Lifetime Control ....................................................................................................................................... 316 Client Control Management ....................................................................................................................................... 316 Connection Point Management .................................................................................................................................. 316 Control Registration ................................................................................................................................................... 316 Exceptions ................................................................................................................................................................. 316 Initialization ................................................................................................................................................................ 316 Licensing ................................................................................................................................................................... 316 Type Information ........................................................................................................................................................ 316 Reviewing the OLE Macros ........................................................................................................................................... 316 Class Factories .......................................................................................................................................................... 316 Client/Container Common Commands ...................................................................................................................... 316 Control Property Persistence ..................................................................................................................................... 316 Dialog Data Exchange ............................................................................................................................................... 316 Dispatch Maps ........................................................................................................................................................... 316 Event Maps ............................................................................................................................................................... 316 Property Page Data Mapping ..................................................................................................................................... 316 Property Pages .......................................................................................................................................................... 316 Type Library Access .................................................................................................................................................. 316 MFC and the OLE Class Categories ........................................................................................................................... 316 Active Document ....................................................................................................................................................... 316 Automation ................................................................................................................................................................ 316 Common Dialogs for OLE .......................................................................................................................................... 316 Container ................................................................................................................................................................... 316 Control ....................................................................................................................................................................... 316 Drag and Drop (Universal Data Transfer) ................................................................................................................... 316
The COM Runtime Environment ..................................................................................................................................

Document Servers ..................................................................................................................................................... 316 Support ...................................................................................................................................................................... 316 Summary .......................................................................................................................................................................... 316 MFC OLE Servers ............................................................................................................................................................... 348 Document Servers .......................................................................................................................................................... 348 Server Types ................................................................................................................................................................... 348 Full Server ................................................................................................................................................................. 348 Active Document ....................................................................................................................................................... 348 Container/Server ....................................................................................................................................................... 348 Mini-Server ................................................................................................................................................................ 348 Document Server Design .............................................................................................................................................. 348 OLE Documents ........................................................................................................................................................ 348 Active Documents ..................................................................................................................................................... 348 Building an Active Document Server ........................................................................................................................... 348 Persistence ............................................................................................................................................................... 348 Rendering the View ................................................................................................................................................... 348 Automation Servers ........................................................................................................................................................ 348 IDispatch .......................................................................................................................................................................... 348 GetIDsOfNames ........................................................................................................................................................ 348 GetTypeInfo ............................................................................................................................................................... 348 GetTypeInfoCount ..................................................................................................................................................... 348 Invoke ........................................................................................................................................................................ 348 IDispatch-Derived Interface in ODL ............................................................................................................................. 348 Calling Methods Through IDispatch ............................................................................................................................. 348 GetIDsOfNames ........................................................................................................................................................ 348 Type Information Methods ......................................................................................................................................... 348 Invoke ........................................................................................................................................................................ 348 Dispinterfaces Differ from Interfaces ........................................................................................................................... 348 Calling Methods Through IDispatch ............................................................................................................................. 348 Dual Interfaces ................................................................................................................................................................ 348 The Variant ...................................................................................................................................................................... 348 An Automation Server Using MFC ............................................................................................................................... 348 Server Type ............................................................................................................................................................... 348 Declaring and Defining Additional Dispinterfaces ....................................................................................................... 348 Adding Methods and Properties ................................................................................................................................. 348 Summary .......................................................................................................................................................................... 348 MFC OLE Clients ................................................................................................................................................................. 369 IDispatch and Its Place in Automation ......................................................................................................................... 369 Interface Definition for Automation Servers ................................................................................................................ 369 IDL and ATL ............................................................................................................................................................... 369 ODL and MFC ........................................................................................................................................................... 369 Dual Interfaces .......................................................................................................................................................... 369 MFC and Automation ..................................................................................................................................................... 369 Controller ................................................................................................................................................................... 369 Connecting to a Server .............................................................................................................................................. 369 Server Review ........................................................................................................................................................... 369 Building a Controller ....................................................................................................................................................... 369 Using COleDispatchDriver ......................................................................................................................................... 369 Using #import ............................................................................................................................................................ 369 Remote Automation ................................................................................................................................................... 369 Summary .......................................................................................................................................................................... 369 MFC ActiveX Controls ........................................................................................................................................................ 395 Development Strategy ................................................................................................................................................... 395 MFC .......................................................................................................................................................................... 395

ATL ............................................................................................................................................................................ 395 MFC and ATL ............................................................................................................................................................. 395 Control Development ..................................................................................................................................................... 395 Two Faces of a Control .................................................................................................................................................. 395 Runtime ..................................................................................................................................................................... 395 Design Time .............................................................................................................................................................. 395 Subclassing a Control .................................................................................................................................................... 395 MFC .......................................................................................................................................................................... 395 ATL ............................................................................................................................................................................ 395

Component Categories .................................................................................................................................................. 395


ICatRegister .............................................................................................................................................................. 395 ICatInformation .......................................................................................................................................................... 395 Methods, Properties, and Events ................................................................................................................................. 395 Properties .................................................................................................................................................................. 395 Methods .................................................................................................................................................................... 395 Events ....................................................................................................................................................................... 395 Property Pages ............................................................................................................................................................... 395 Property Pages in ATL ............................................................................................................................................... 395 Property Pages in MFC ............................................................................................................................................. 395 Component Registration ................................................................................................................................................ 395 Registration Scripts ................................................................................................................................................... 395 Registration and Controls .......................................................................................................................................... 395 COM Object Subkeys .................................................................................................................................................... 395 Building an MFC Control ............................................................................................................................................... 395 Interface Definition ......................................................................................................................................................... 395 A Quick ATL Port ............................................................................................................................................................. 395 Summary .......................................................................................................................................................................... 395 MFC ActiveX Control Containers .................................................................................................................................... 429 Active Document Container .......................................................................................................................................... 429 Storage ...................................................................................................................................................................... 429 Site Objects ............................................................................................................................................................... 429 In-Place Activation ..................................................................................................................................................... 429 Document Extensions ................................................................................................................................................ 429 Building the Simplest Active Document Container .................................................................................................... 429 OLE Controls ................................................................................................................................................................... 429 Adding Containment to an Existing Project ................................................................................................................ 429 ActiveX Container ...................................................................................................................................................... 429 Control Containment and Events ............................................................................................................................... 429 Summary .......................................................................................................................................................................... 429 Using MFC and ATL ............................................................................................................................................................ 454 What Is ATL and Why Is It Important for Programming in MFC? ........................................................................... 454 Helpful ATL COM Support for MFC Applications ........................................................................................................ 454 COM Pointers the Smart Way ................................................................................................................................... 454 Other ATL COM Support ............................................................................................................................................ 454 Advanced ATL Support for MFC Applications ............................................................................................................. 454 Begin with Your MFC Application ............................................................................................................................... 454 Add the Required ATL Support .................................................................................................................................. 454 Summary .......................................................................................................................................................................... 454 Scripting Your MFC Application ....................................................................................................................................... 481 Scripting Basics .............................................................................................................................................................. 481 Scripting Interfaces .................................................................................................................................................... 481 Dual Interfaces .......................................................................................................................................................... 481 Object Models ................................................................................................................................................................. 481 Implementing a Scripted Application ........................................................................................................................... 481

Object Implementation ............................................................................................................................................... 481 Summary .......................................................................................................................................................................... 481 MFC Database Programming ....................................................................................................................................... 503 MFC Database Processing ............................................................................................................................................... 503 Relational Database Concepts ..................................................................................................................................... 503 Tables ........................................................................................................................................................................ 503 Columns .................................................................................................................................................................... 503 Records ..................................................................................................................................................................... 503 Cursors ...................................................................................................................................................................... 503 Transactions .............................................................................................................................................................. 503 Storing and Retrieving Data .......................................................................................................................................... 503 SELECT .................................................................................................................................................................... 503 INSERT ..................................................................................................................................................................... 503 DELETE .................................................................................................................................................................... 503 UPDATE .................................................................................................................................................................... 503 Database Communication Mechanisms ..................................................................................................................... 503 ODBC ........................................................................................................................................................................ 503 DAO .......................................................................................................................................................................... 503 Which Methodology Should I Use? ............................................................................................................................ 503 ODBC/MFC ...................................................................................................................................................................... 503 CDatabase ................................................................................................................................................................ 503 CRecordset ............................................................................................................................................................... 503 DAO .................................................................................................................................................................................. 503 CDaoWorkspace ....................................................................................................................................................... 503 CDaoDatabase .......................................................................................................................................................... 503 CDaoRecordset ......................................................................................................................................................... 503 CDaoTableDef ........................................................................................................................................................... 503 CDaoQueryDef .......................................................................................................................................................... 503 Summary .......................................................................................................................................................................... 503 Advanced Database Support ........................................................................................................................................... 528 The COM Approach ........................................................................................................................................................ 528 OLE DB ..................................................................................................................................................................... 528 ADO .......................................................................................................................................................................... 528 Which One Should I Use? ......................................................................................................................................... 528 OLE DB Consumers Using the ATL Wrapper Classes ............................................................................................. 528 CDataSource ............................................................................................................................................................. 528 CSession ................................................................................................................................................................... 528 Accessors .................................................................................................................................................................. 528 Rowsets .................................................................................................................................................................... 528 Accessing Datasource Data ...................................................................................................................................... 528 Using the ADO C++ Interfaces ..................................................................................................................................... 528 ADOConnection ......................................................................................................................................................... 528 ADORecordset .......................................................................................................................................................... 528 ADOCommand .......................................................................................................................................................... 528 ADOField ................................................................................................................................................................... 528 ADOProperty ............................................................................................................................................................. 528 ADOParameter .......................................................................................................................................................... 528 ADOError ................................................................................................................................................................... 528 Summary .......................................................................................................................................................................... 528 MFC Utility Classes .............................................................................................................................................................. 543 Strings and Collections ...................................................................................................................................................... 543 Strings and String Classes ............................................................................................................................................ 543 Inside the CString Class ............................................................................................................................................ 543 Practical CString Usage ............................................................................................................................................ 543

File

CString Summary ...................................................................................................................................................... 543 Collections ....................................................................................................................................................................... 543 Inside Collection Classes .......................................................................................................................................... 543 Templated Collections ............................................................................................................................................... 543 The UNL_MultiEd Application ....................................................................................................................................... 543 Overview ................................................................................................................................................................... 543 An STL Approach ............................................................................................................................................................ 543 Summary .......................................................................................................................................................................... 543 I/O and MFC .................................................................................................................................................................. 563

563 Processing Files with CFile ........................................................................................................................................ 563 Inside the CFile Class .................................................................................................................................................... 563 The CStdioFile Class ................................................................................................................................................. 563 The CMemFile Class ................................................................................................................................................. 563 The CSharedFile Class .............................................................................................................................................. 563 The CFileDialog Class ................................................................................................................................................... 563 The User-Defined CFileDialog Class ........................................................................................................................... 563 Practical Usage of CFile and CFileDialog .................................................................................................................. 563 Opening a File ........................................................................................................................................................... 563 Reading Data from a File ........................................................................................................................................... 563 A Classical Approach ..................................................................................................................................................... 563 What Are Streams? ................................................................................................................................................... 563 Summary .......................................................................................................................................................................... 563 Exceptions .............................................................................................................................................................................. 581 What Are They Good For? ............................................................................................................................................ 581 Types of Exceptions ....................................................................................................................................................... 581 Structured Exception Handlers ..................................................................................................................................... 581 Nesting of Structured Exception Handlers .................................................................................................................. 581 Raising Structured Exceptions ..................................................................................................................................... 581 Cleaning Up After an Exception ................................................................................................................................... 581 C++ Exceptions ............................................................................................................................................................... 581 Defining a C++ Exception Class .................................................................................................................................. 581 MFC Exceptions .............................................................................................................................................................. 581 MFC CException-Derived Classes ............................................................................................................................. 581 CMemoryException ................................................................................................................................................... 581 CNotSupportedException .......................................................................................................................................... 581 CArchiveException .................................................................................................................................................... 581 CFileException .......................................................................................................................................................... 581 CResourceException ................................................................................................................................................. 581 COleException ........................................................................................................................................................... 581 CDbException ............................................................................................................................................................ 581 COleDispatchException ............................................................................................................................................. 581 CUserException ......................................................................................................................................................... 581 CDaoException .......................................................................................................................................................... 581 CInternetException .................................................................................................................................................... 581 Deriving Your Own MFC-Compliant Exception Objects ........................................................................................... 581 Deleting Exceptions ....................................................................................................................................................... 581 Using MFC Exception Macros ...................................................................................................................................... 581 Mixing Exceptions ........................................................................................................................................................... 581 Summary .......................................................................................................................................................................... 581 MFC and the Web ................................................................................................................................................................ 609 MFC and DHTML ................................................................................................................................................................. 609 DHTML, MSIE, and the Internet ................................................................................................................................... 609 Using the Internet Explorer Web ActiveX Control ..................................................................................................... 609
The CFile Class ..............................................................................................................................................................

Internet Explorer ActiveX Control Basics .................................................................................................................... 609 CHtmlView ................................................................................................................................................................. 609 CHtmlView and the Document/View Relationship ...................................................................................................... 609 CHtmlView and COM ................................................................................................................................................. 609 Using the Internet Explorer ActiveX Control in a Dialog Box ..................................................................................... 609 Using DHTML .................................................................................................................................................................. 609 The DHTML Object Model ......................................................................................................................................... 609 The Document Object ................................................................................................................................................ 609 Other DHTML Objects ............................................................................................................................................... 609 Collection Objects ..................................................................................................................................................... 609 DHTML COM Interfaces ............................................................................................................................................ 609 Obtaining the Document DHTML Interface ................................................................................................................ 609 Obtaining the DHTML Collections Interfaces ............................................................................................................. 609 Using the DHTML Interface ....................................................................................................................................... 609 The DHTML Element Interface Methods .................................................................................................................... 609 Document Navigation ................................................................................................................................................ 609 Summary .......................................................................................................................................................................... 609 CSocket Programming ....................................................................................................................................................... 636 How Do Network Communications Work? ................................................................................................................. 636 Sockets, Ports, and Addresses .................................................................................................................................. 636 Winsock and MFC .......................................................................................................................................................... 636 Initializing the Winsock Environment ......................................................................................................................... 636 Creating a Socket ...................................................................................................................................................... 636 Making a Connection ................................................................................................................................................. 636 Sending and Receiving Messages ............................................................................................................................. 636 Closing the Connection .............................................................................................................................................. 636 Socket Events ........................................................................................................................................................... 636 Controlling Event Triggering ....................................................................................................................................... 636 Detecting Errors ........................................................................................................................................................ 636 Getting Socket Information ........................................................................................................................................ 636 Sockets and I/O Serialization ..................................................................................................................................... 636 Building a Networked Application ................................................................................................................................. 636 Creating the Application Shell .................................................................................................................................... 636 Window Layout and Startup Functionality .................................................................................................................. 636 Inheriting from the CAsyncSocket Class .................................................................................................................... 636 Connecting the Application ........................................................................................................................................ 636 Sending and Receiving .............................................................................................................................................. 636 Ending the Connection .............................................................................................................................................. 636 Summary .......................................................................................................................................................................... 636 WinInet Programming ........................................................................................................................................................ 664 Web Application Protocols and WinInet ...................................................................................................................... 664 Hypertext Transfer Protocol ....................................................................................................................................... 664 File Transfer Protocol ................................................................................................................................................ 664 Gopher Protocol ........................................................................................................................................................ 664 WinInet API and MFC ................................................................................................................................................ 664 Internet Session Basics ................................................................................................................................................. 664 CInternetSession ....................................................................................................................................................... 664 CInternetFile .............................................................................................................................................................. 664 CInternetException .................................................................................................................................................... 664 CInternetConnection .................................................................................................................................................. 664 Building a Simple WinInet Application ....................................................................................................................... 664 Application-Level Functionality ..................................................................................................................................... 664 HTTP Classes ........................................................................................................................................................... 664 FTP Classes .............................................................................................................................................................. 664

Gopher Classes ......................................................................................................................................................... 664 Building a Simple FTP Client ..................................................................................................................................... 664 Summary .......................................................................................................................................................................... 664 ISAPI Extensions ................................................................................................................................................................. 703 The Foundation ............................................................................................................................................................... 703 From There to Here ........................................................................................................................................................ 703 From GET to POST ........................................................................................................................................................ 703 Adding Some Form Elements ....................................................................................................................................... 703 Change the Form ....................................................................................................................................................... 703 Change the Parse Map .............................................................................................................................................. 703 Declare and Use the Handler Function ...................................................................................................................... 703 Add a Radio Button ................................................................................................................................................... 703 Other Form Input Elements ........................................................................................................................................... 703 Change the Form ....................................................................................................................................................... 703 Change the Parse Map .............................................................................................................................................. 703 Change the Handler Function .................................................................................................................................... 703 Summary .......................................................................................................................................................................... 703 MAPI and MFC ..................................................................................................................................................................... 725 The Messaging Application Programming Interface .................................................................................................. 725 Client Applications ..................................................................................................................................................... 725 Two Higher-Level Alternatives: Simple MAPI and CMC .......................................................................................... 725 Simple MAPI ............................................................................................................................................................. 725 Common Messaging Calls ......................................................................................................................................... 725 The MapiMessage Structure ...................................................................................................................................... 725 The MapiFileDesc Structure ...................................................................................................................................... 725 Limited MAPI Functionality in MFC .............................................................................................................................. 725 The CDocument Connection ........................................................................................................................................ 725 Doing MAPI the MFC Way ......................................................................................................................................... 725 The CDocument::OnFileSendMail() Method .............................................................................................................. 725 Sample Program: MAPI1.EXE ...................................................................................................................................... 725 Summary .......................................................................................................................................................................... 725 TAPE and MFC ..................................................................................................................................................................... 738 Overview .......................................................................................................................................................................... 738 Assisted Telephony .................................................................................................................................................... 738 Basic Telephony Service ............................................................................................................................................ 738 Supplemental Telephony Service ............................................................................................................................... 738 Extended Telephony Service ..................................................................................................................................... 738 History of TAPI ................................................................................................................................................................ 738 TAPI 1.3 .................................................................................................................................................................... 738 TAPI 1.4 .................................................................................................................................................................... 738 TAPI 1.5 .................................................................................................................................................................... 738 TAPI 2 ....................................................................................................................................................................... 738 TAPI 2.1 .................................................................................................................................................................... 738 TAPI 2.2 .................................................................................................................................................................... 738 TAPI 3 ....................................................................................................................................................................... 738 Using Assisted Telephony ............................................................................................................................................. 738 Using Basic Telephony .................................................................................................................................................. 738 Configuring TAPI ....................................................................................................................................................... 738 Connecting with TAPI ................................................................................................................................................ 738 Transmitting Data with TAPI ...................................................................................................................................... 738 Disconnection with TAPI ............................................................................................................................................ 738 Terminating a TAPI Session ....................................................................................................................................... 738 Summary .......................................................................................................................................................................... 738 MFC and Graphics Programming .............................................................................................................................. 763

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

Media Formats Supported by the Media Player Control ............................................................................................ 839 Inside the Media Player Control ................................................................................................................................. 839 The CMediaPlayer Class ........................................................................................................................................... 839 Using the Media Player Control .................................................................................................................................... 839 Playing Sound ................................................................................................................................................................. 839 Working with Waves .................................................................................................................................................. 839 Revisiting DirectSound .............................................................................................................................................. 839 Using DirectSound to Play Sound Effects .................................................................................................................. 839 Summary .......................................................................................................................................................................... 839

Advanced MFC

....................................................................................................................................................................... 871 Inside the Registry ............................................................................................................................................................... 871 Registry Usage ............................................................................................................................................................... 871 Configuration ............................................................................................................................................................. 871 Services Information .................................................................................................................................................. 871 State .......................................................................................................................................................................... 871 User Preferences ....................................................................................................................................................... 871 The Registry Structure ................................................................................................................................................... 871 Programmatic Control of the Registry ......................................................................................................................... 871 The Registry API ....................................................................................................................................................... 871 The Registry Sample Application ................................................................................................................................. 871 The Registry Key Editor Dialog .................................................................................................................................. 871 Creating a New Key ................................................................................................................................................... 871 Deleting a Key ........................................................................................................................................................... 871 Deleting a Name/Value Pair ....................................................................................................................................... 871 A Word About Wrapping the Registry Functions ....................................................................................................... 871 A Word About Installation .............................................................................................................................................. 871 Summary .......................................................................................................................................................................... 871 Writing and Using DLLs ..................................................................................................................................................... 885 Advantages of DLLs ....................................................................................................................................................... 885 Code Elimination ....................................................................................................................................................... 885 Modularity and Packaging ......................................................................................................................................... 885 Extensibility ............................................................................................................................................................... 885 Inside an AFX DLL .......................................................................................................................................................... 885 Exporting Classes, Functions, and Data ..................................................................................................................... 885 The Big Deal About Exports ....................................................................................................................................... 885 Mangled Names ........................................................................................................................................................ 885 Exporting Classes ...................................................................................................................................................... 885 What Goes Around, Comes Around ........................................................................................................................... 885 Exporting Explicit Functions ....................................................................................................................................... 885 Exporting Data ........................................................................................................................................................... 885 Exporting MFC Data .................................................................................................................................................. 885 Exporting the Destructor ............................................................................................................................................ 885 Export Toolkit include Files ....................................................................................................................................... 885 What to Export ........................................................................................................................................................... 885 Other DLL Issues ............................................................................................................................................................ 885 AfxLoadLibrary and AfxFreeLibrary ............................................................................................................................ 885 Designing for Extensibility and Reuse ........................................................................................................................ 885 Resource Location ..................................................................................................................................................... 885 Multiple Module Definition Files ................................................................................................................................. 885 Load Addresses and the Linker ................................................................................................................................. 885 Summary .......................................................................................................................................................................... 885 Creating Custom Wizards ................................................................................................................................................. 909 Property Sheets and Property Pages .......................................................................................................................... 909 The CPropertySheet and CPropertyPage Classes ................................................................................................... 909

The Wizard Walk and the Property Sheet Connection .............................................................................................. 909 Creating a Wizard ........................................................................................................................................................... 909 Setting the Wizard Mode ........................................................................................................................................... 909 Enabling the Wizard Buttons ..................................................................................................................................... 909 Displaying the Wizard ................................................................................................................................................ 909 Wizard Notification Messages ................................................................................................................................... 909 Sample Program: Off to See the Wizard (WIZARD1.EXE) ...................................................................................... 909 Creating Wizard Page Dialog Template Resources .................................................................................................. 909 Create a Dialog Class for Each Dialog Resource ...................................................................................................... 909 Exploring the Welcome Page: Class CIntroPage ....................................................................................................... 909 Exploring the About You Page: Class CPage1 .......................................................................................................... 909 Updating Wizard Information for CPage2 and CPage3 ............................................................................................. 909 Creating and Displaying the Wizard ........................................................................................................................... 909 Summary .......................................................................................................................................................................... 909 ............................................................................................................................................................................................... 927

Index

MFC Programming with Visual C++ 6 Unleashed

Introduction

Introduction If you shop like I do, youve just noticed this book on the shelf and picked it up to see what interesting topics it covers. Youve 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 arent convinced that this is the best MFC programming book on the market, this introduction wont 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 experiencenot 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 dont understand the Document/View architecture? Its explained here. You want to extend and go beyond the Document/View architecture? Thats here too, as well as all other aspects of MFC programming. In a nutshell, here is what youll find: MFCs 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 MFCs 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 MFCs 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 .
1 / 1166

MFC Programming with Visual C++ 6 Unleashed

Introduction

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, youll not only learn what makes MFC tick, but youll also learn how to supercharge MFC, customize it, and take the architecture and make it work the way you want it to work. Youll go way beyond the wizard-generated code and see how experienced MFC programmers do their magic. Soon, itll 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, youll 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 MFCs 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 applications 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 todays 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 MFCs 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 youll 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
2 / 1166

MFC Programming with Visual C++ 6 Unleashed

Introduction

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: BeginnersThe basic concepts are clearly and concisely covered. More important than that, however, is the valuable coverage youll find regarding pitfalls and potential optimizations. Youll learn the basics, but youll also learn how to avoid common beginning MFC programmer mistakes. Casual and accomplished MFC programmersUse 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. Youll also find detailed coverage of the latest Microsoft technologies with specific use from within MFCbased applications. ExpertsFor you, this book provides the detailed programmer-to-programmer information you wont 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, its here for you. We have worked exceptionally hard to provide you with the definitive work to date regarding MFC programming. Enjoy!

3 / 1166

MFC Programming with Visual C++ 6 Unleashed

Foreword

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 Microsofts 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 programmers 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, Ive used MFC on many projects. One of the most rewarding of those endeavors was being part of the software development team for Qualcomms 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.
4 / 1166

MFC Programming with Visual C++ 6 Unleashed

Foreword

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 PresidentEngineering Internet Systems Division Stellcom, Inc.

5 / 1166

MFC Programming with Visual C++ 6 Unleashed

About the Author

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 Researchs 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 didnt 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. (Hes 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 EnduraSofts 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 Developers Guide Using the Game SDK (Sams Publishing, 1997), Complete Idiots 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 DigitalThinks 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.
6 / 1166

MFC Programming with Visual C++ 6 Unleashed

About the Author

Davis Chapman first began programming computers while working on his masters degree in music composition. Writing applications for computer music, he discovered that he enjoyed designing and developing computer software. It wasnt 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 sons 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 fiance, 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, Comptons New Media, and Qualcomm. Keith is currently vice president of engineering for Stellcoms 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].
7 / 1166

MFC Programming with Visual C++ 6 Unleashed

About the Author

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 Im 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 couldnt 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 its 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 understandingand to my
8 / 1166

MFC Programming with Visual C++ 6 Unleashed

About the Author

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 were doing right, what we could do better, what areas youd like to see us publish in, and any other words of wisdom youre 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 didnt like about this bookas 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 books 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: Email: Mail: (317)581-4770 [email protected] Bradley L. Jones Associate Publisher Sams Publishing 201 West 103rd Street Indianapolis, IN 46290 USA

9 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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. Microsofts 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 proceduralit 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.

10 / 1166

MFC Programming with Visual C++ 6 Unleashed


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 didnt 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).

Summary

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 programs 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 objects 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 applicationsas long as they do not change the behavior of the existing interfaces. (Occasionally some interfaces 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

11 / 1166

MFC Programming with Visual C++ 6 Unleashed 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.

Summary

Without a main window, an application wouldnt 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 RegisterClass to 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 frames application window is created in the CWinApp::InitInstance code. 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 GetMessage function returns a Boolean value TRUE for all messages, except the WM_QUIT message, which allows the while loop to end, due to application termination. After the call to GetMessage returns with a message (event) for one of the threads 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 windows registered window procedure. What Is a Thread You are probably intimately familiar with the concept of a process. In many operating systems, a process corresponds to an instance of executing code in the computers 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 programs 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::Run method. (More specifically, CWinApp::Run calls the CWinThread::Run method, which is in its base class.) For dialog-based applications, the message loop is instantiated during the CWinApp::initInstance through a call to CDialog::doModal. In

12 / 1166

MFC Programming with Visual C++ 6 Unleashed


addition, when messages are dispatched from the threads message loop, they are routed through the standard MFC-owned window procedures (AfxWndProcBase and 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 programs WinMain function is the destruction of the main window via a call to the DestroyWindow function. 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::ExitInstance method in your applications 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.

Summary

Figure 1.2 MFC architecture class hierarchy. CObject The CObject class is the mother of all MFC classes. Well, actually not quite all MFC classes, but quite a few of them. Its primary responsibilities are to support handle runtime type information and object persistence (or serialization in MFC-speak), and to perform diagnostic output for derived objects. Classes that are derived from the CObject class 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 classs 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 CObject class is limited to two methods: AssertValid and Dump. Each of these methods can be called at any time from your derived class.

AssertValid The AssertValid method allows a class to perform a sanity check on itself before continuing. It is declared as a public method in the CObject class as follows:
virtual void AssertValid() const;

If you choose to override this method, you will typically use the ASSERT macro to perform sanity checks on your objects data. In addition, you should call your base classs implementation of this method so it can also validate its data. Because this method is

13 / 1166

MFC Programming with Visual C++ 6 Unleashed


const, you cannot change your objects state from within this method. Caution: Do not depend on AssertValid working in release code. This is because the ASSERT macro is only defined when _DEBUG is 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 Dump method allows a class to put diagnostic information in the human-readable form of text and/or numbers to a stream, typically through the OutputDebugString API, which displays it in the debuggers output window (or any other program that traps debug strings). It is declared as a public method in the CObject class as follows:
virtual void Dump(CDumpContext& dc) const;

Summary

If you override this method, first call your base classs dump method. Next, call the insertion operator (<<) on the CDumpContext object to output information about your object. As a C++ programmer, this is no different from how you would use the cerr or cout object 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::dump displays only CObject as the class name. Otherwise, it properly displays the name of your derived class. Runtime Type Information (Dynamic and DynCreate Classes) When a class supports MFCs Runtime Type Information (RTTI), it can respond to a request for its name and its base class. To support the Dynamic form of RTTI, simply include a DECLARE_DYNAMIC macro invocation within your class declaration and an IMPLEMENT_DYNAMIC macro invocation near your class definition.

Caution: Do not confuse MFCs RTTI with the RTTI support built into the newer C++ compilers (and activated using Visual C++s /GR switch). As of MFC 4.2, MFCs RTTI support does not use C++s RTTI support in any way, shape, or form. If you have a class named CMyClass that is derived from CObject and 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 CRuntimeClass object 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_CLASS macro, invoking it using the name of the class that youd like. So, to obtain a pointer to the CRuntimeClass object associated with CMyClass (a Dynamic class), simply use the following code:
RUNTIME_CLASS(CMyClass)

An extension of the Dynamic class is a DynCreate class. 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

14 / 1166

MFC Programming with Visual C++ 6 Unleashed


constructor with no parameters) that creates a stable object, and you must add macro invocations for DECLARE_DYNCREATE and 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_DYNAMIC macros to your class files. The DynCreate macros add a method named CreateObject to your class. The implementation of this method simply calls new on your specific object and returns it as a pointer to a CObject. If you have a class named CMyClass that supports dynamic creation, you can instantiate one of these objects by using the RUNTIME_CLASS macro to get the RTTI information and call the CreateObject method on it as follows:
CMyClass *myObj = DYNAMIC_DOWNCAST(CMyClass, RUNTIME_CLASS(CMyClass)->CreateObject());

Summary

Note: The DYNAMIC_DOWNCAST and STATIC_DOWNCAST macros improve the type safety for casting operations on MFC CObject-derived types. By using MFCs RTTI, they can check if the cast is valid at runtime. You can use the C++ dynamic_cast and static_cast keywords to perform typesafe casts on MFC objects as long as you compile your application with C++ RTTI turned on (Visual C++s /GR compiler switch). MFC is already built with this switch on; however, the Visual Studio default is off. Serialization (Serial Classes) Serialization is 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_SERIAL macro inside its class declaration in the header file and the IMPLEMENT_SERIAL macro in the source file containing the classs method and data definitions, in the same manner as was demonstrated for a Dynamic class. Note: Because serialization is a superset of the dynamic and dynamic creation support, do not add invocations of the DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC or the DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE macros to your class files. When a CObject-derived class supports serialization, there are two methods that are used: IsSerializable and Serialize. IsSerializable The IsSerializable method allows another object to determine whether this CObjectderived class supports serialization. It is declared as a public method in the CObject class as follows:
BOOL IsSerializable() const;

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

15 / 1166

MFC Programming with Visual C++ 6 Unleashed


Serialize The Serialize method is called to actually perform the saving and restoring of the object from a serialization stream (within a CArchive object). It is declared as a public method in the CObject class as follows:
virtual void Serialize(CArchive& ar);

Summary

If your class needs to save or restore its state when being serialized, you must override this method. Of course, if youre implementing this method so your class can support serialization, you first need to know whether or not you need to save your objects data to or restore your objects data from the CArchive stream. You can use either the CArchive::IsLoading or CArchive::IsStoring methods to determine which direction you need to go. Note: You are guaranteed that if CArchive::IsLoading returns TRUE, CArchive::IsStoring returns FALSE and vice versa. Next, if the archive object is in loading mode, you can use the CArchive classs 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 classs Serialize method before performing your own serialization support. If you detect an error while handling your Serialize method, 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 Serialize method for the fictitious CMyClass class 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 CArchive object, associates it with a CFile object, and calls the CArchive::ReadObject or CArchive::WriteObject methods in response to the File menu bar, Open, Save, and Save As commands. These methods call the Serialize method on the appropriate objects. Likewise, you can perform your own serialization explicitly within your application. First, create a CFile or CFile-derived object (such as CSocketFile). Next, construct a CArchive object passing a pointer to your CFile object to its constructor. Finally, call either the CArchive::WriteObject or CArchive::ReadObject method to save or restore your object, respectively. CCmdTarget The CCmdTarget class, 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::OnCmdMsg method.

16 / 1166

MFC Programming with Visual C++ 6 Unleashed


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 CCmdTarget class can be organized into three categories: message routing, wait cursor, and automation support. Message Routing In all Windows GUI applications, the application threads message loop processes all system and window events for that threads windows. MFC is no exception. However, unlike the procedural window procedure with a large switch statement, 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 CCmdTarget class called a message map. In essence, this structure maps an event to its corresponding handler in the object. Why Doesnt 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 CWnd objects share the same vtable in memory. Also, each abstract class has a vtable that wont 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 carry still 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 objects 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.

Summary

17 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

The method that MFC uses for mapping system and window events to objects is called message mapping . Each class that is derived ultimately from CCmdTarget contains 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_MAP macro within your object. For the fictitious CMyView class, this code is as follows:
class CMyView : public CView { // CView is a subclass of CCmdTarget DECLARE_MESSAGE_MAP() // other class information };

The DECLARE_MESSAGE_MAP macro 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 CMyView class 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_MAP and END_MESSAGE_MAP that 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_COMMAND macro, CMyView maps the menu item command identifier ID_FILE_OPEN to the OnFileOpen method. In addition, using the ON_WM_SIZE macro maps the WM_SIZE window 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 TRUE if it handles the event, and FALSE otherwise. Wait Cursor The CCmdTarget class 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 BeginWaitCursor method 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 EndWaitCursor to change the pointer back to what it was at the BeginWaitCursor call. Use RestoreWaitCursor to 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 CCmdTarget class:

18 / 1166

MFC Programming with Visual C++ 6 Unleashed


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

Summary

Automation Support If your MFC application allows interaction through an IDispatch COM interface, it supports automation. The CCmdTarget class 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 CCmdTarget that support automation are: EnableAutomation, FromIDispatch, GetIDispatch, IsResultExpected, and OnFinalRelease. EnableAutomation The EnableAutomation method is called from your CCmdTarget-derived classs constructor to indicate that it contains both a dispatch and an interface map. It is declared in the public section of the CCmdTarget class 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_MAP and DECLARE_INTERFACE_MAP macros, respectively. The following code demonstrates these macros for the sample CMyDocument class:
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 CMyDocument class (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 FromIDispatch and GetIDispatch methods allow an application to get the CCmdTarget object given an IDispatch interface pointer and vice versa. Of course, not all CCmdTarget objects contain IDispatch interfaces and, hence, might return a NULL pointer. These methods are declared in the public section of the CCmdTarget class as follows:
static CCmdTarget * FromIDispatch(LPDISPATCH lpDispatch); LPDISPATCH GetIDispatch(BOOL bAddRef);

IsResultExpected The IsResultExpected method returns TRUE if 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 CCmdTarget class:
BOOL IsResultExpected();

19 / 1166

MFC Programming with Visual C++ 6 Unleashed


OnFinalRelease The OnFinalRelease method 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 CCmdTarget class as shown here:
virtual void OnFinalRelease();

Summary

CWinThread The CWinThread class is derived from the CCmdTarget class and represents a thread of execution within the MFC application. All MFC applications have at least one CWinThread objectthe main applications CWinApp object (which is derived from CWinThread). If you want to provide additional asynchronous processing within your application, you can construct and run additional CWinThread objects, as needed. You can obtain a pointer to the current CWinThread object by calling AfxGetThread. Within MFC, there are two different types of threads: worker threads and user interface threads.

Worker Threads Worker threads are 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 applications user interface. A worker thread is created using the AfxBeginThread function. In its simplest form, you simply need a callback function and a user-defined data pointer. After it has been created, a CWinThread object is returned to the calling thread, and the new worker thread starts execution. This form of AfxBeginThread is 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 threads are 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 threads user interface. To use user interface threads, you must first derive a class from the CWinThread class that creates a user interface element for which to handle events. You can choose to use the InitInstance and ExitInstance methods 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 CWinThread function and then call the CreateThread method to start it. This method is declared as follows in the CWinThread class:
BOOL CreateThread(DWORD dwCreateFlags = 0, UINT nStackSize = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);

Using the CreateThread method is a fairly clean coding approach to creating and starting a new user interface thread.

20 / 1166

MFC Programming with Visual C++ 6 Unleashed


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 AfxBeginThread function. 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);

Summary

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 AFXBeginThread overload requires a pointer to the runtime class of the CWinThread-derived object that you want to instantiate. Remember to use the RUNTIME_CLASS macro to obtain a pointer to this object for a specific class. When creating CWinThread objects, you might find it necessary to initialize some members of the CWinThread object before the thread starts executing. To do this, pass CREATE_SUSPENDED to the dwCreateFlags parameter, which creates the thread in suspended mode. After the thread has been created, set the members that you require and call the CWinThread::ResumeThread method to allow the thread to start executing. When the thread needs to terminate, it can simply call AfxPostQuitMessage to 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 CWinThread class. Remember that because your CWinApp-derived application object has CWinThread as a base class, you can use these methods on that object. InitInstance and ExitInstance The InitInstance and ExitInstance virtual functions can be overridden by your application to provide pre-message loop and post-message loop initialization and termination. If you override the Run method, 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::ExitInstance implementation automatically deletes the CWinThread object if the m_bAutoDelete member is set to TRUE. Therefore, if you override ExitInstance, call your base classs ExitInstance to maintain this behavior. Additionally, if the call to InitInstance fails, ExitInstance is called. Run The Run method is where the actual thread operation occurs. The default implementation provides a message loop for the thread and continues until a WM_QUIT message is encountered. You can create a CWinThread-derived worker thread if you override the Run method 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 GetExitCodeThread function in the Windows API. SuspendThread and ResumeThread Use the SuspendThread and ResumeThread methods 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 ResumeThread before 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.

21 / 1166

MFC Programming with Visual C++ 6 Unleashed


GetThreadPriority and SetThreadPriority If you want control over the priority of a thread owned by a CWinThread object, use the GetThreadPriority and SetThreadPriority methods. 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);

Summary

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 threads priority. If you need to disable this behavior, use the SetThreadPriorityBoost API.

IsIdleMessage and OnIdle You can use the IsIdleMessage and OnIdle methods to control processing that occurs when no messages exist in the threads 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 OnIdle method is called when the threads message queue is empty. In your override implementation, it is suggested that you call PeekMessage with the PM_NOREMOVE flag to check for the arrival of new messages in the queue. If a message arrives, return from this function to allow the threads message processing to continue. The OnIdle method is declared as follows:
virtual BOOL OnIdle(LONG userCount);

By overriding the IsIdleMessage method when you also override the OnIdle method, you can prevent OnIdle from 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 PreTranslateMessage virtual function to handle a message before it is translated from an accelerator to a command and dispatched to a window. Return TRUE from 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 ProcessMessageFilter virtual function allows your application to catch messages that are trapped by the MFC message hooks. Return TRUE if you process a message passed to your implementation. In addition, call your classs 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 ProcessWndProcException method to handle any MFC exceptions that have been trapped from within the Windows message processing. This method is declared as shown here:

22 / 1166

MFC Programming with Visual C++ 6 Unleashed


virtual LRESULT ProcessWndProcException(CException *xcp, const MSG *pMsg);

Summary

CWinApp The CWinApp class, derived from the CWinThread class, represents not only the programs main thread of execution, but also the application itself. As a result, there is only one CWinApp object in any MFC application. Typically, you derive your application class from CWinApp. In addition, you would override the InitInstance and ExitInstance virtual functions to provide your own initialization and termination support (like creating and destroying your applications main window). If you do override these functions, remember to call your base classs 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 AfxGetApp function to obtain the pointer to the executables CWinApp object. Similarly, you can use AfxGetAppName to 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 AfxGetInstanceHandle to obtain a handle to the loaded executable file (EXE or DLL) within which the current code is located. Use the AfxGetResourceHandle and AfxSetResourceHandle functions 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 afxContextIsDLL macro. 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 users 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 SetRegistryKey method 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 CWinApp class:
void SetRegistryKey(LPCTSTR lpszRegistryKey);

23 / 1166

MFC Programming with Visual C++ 6 Unleashed


void SetRegistryKey(UINT nIDRegistryKey);

Summary

Note: The second form of the SetRegistryKey method takes the ID of a string resource as a parameter.

Tip: After youve called SetRegistryKey, you can use the GetAppRegistryKey and GetSectionKey methods to get the HKEY registry handle directly. When finished with the handle, you must call RegCloseKey to release it. GetProfileDataType and WriteProfileDataType Use the GetProfileInt, WriteProfileInt, GetProfileString, WriteProfileString, GetProfileBinary, and WriteProfileBinary methods 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 AddDocTemplate method adds a document template to the list of documentemplates available to the application. You would typically call this method in your override of the InitInstance method. This method is declared as follows:
void AddDocTemplate(CDocTemplate *pTemplate);

GetFirstDocTemplatePosition and GetNextDocTemplate The GetFirstDocTemplatePosition and GetNextDocTemplate methods allow you to iterate through all the document template objects (CDocTemplate) that currently are added to the application. If no document templates are available, GetFirstDocTemplatePosition returns NULL. These public methods are declared as follows:
POSITION GetfirstDocTemplatePosition() const; CDocTemplate *GetNextDocTemplate(POSITION& pos) const;

Note: Because GetNextDocTemplate updates the position value, always check that the current position is not NULL before calling GetNextDocTemplate. OpenDocumentFile The OpenDocumentFile method opens a file representing a document. It creates a frame

24 / 1166

MFC Programming with Visual C++ 6 Unleashed


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);

Summary

LoadStdProfileSettings and AddToRecentFileList Use the LoadStdProfileSettings and AddToRecentFileList methods to initialize and manage your applications recent file list. Call LoadStdProfileSettings in your InitInstance override to add the list of most recently used files to your application. Call AddToRecentFileList when 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 EnableShellOpen and RegisterShellFileTypes methods in sequence to register your document templates in the system registry. By default, the RegisterShellFileTypes method 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 EnableShellOpen is called first, the RegisterShellFileTypes method will add support for opening the document from the desktop shell. Call UnregisterShellFileTypes to 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 applications document templates (via AddDocTemplate) before calling RegisterShellFileTypes. Command-Line Parsing Sometimes youll find it necessary to handle the command line passed to your application in a standard Windows application way. Use the RunEmbedded, RunAutomated, ParseCommandLine, and ParseShellCommand methods to help your application respond properly to command-line options passed to it. RunEmbedded and RunAutomated The RunEmbedded and RunAutomated methods search for /Embedding or /Automated (or the dash forms) in the passed command line. If found, the appropriate option is removed from the command line and TRUE is 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 ParseCommandLine and ProcessShellCommand methods 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 applications InitInstance override. 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 CCommandLineInfo object and override its ParseParam method.

25 / 1166

MFC Programming with Visual C++ 6 Unleashed


CWnd The CWnd class, 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 CPen object in two steps, use its default constructor and then call one of the CreatePen or CreatePenIndirect methods. Classes typically have a Create method. However, some classes can have completely different names. For example, CFrameWnd has both a Create and a LoadFrame method 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 CWnd class, 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 destroyedat which time the handle is returned to the system. Second, if you happen to have a system handle for which youd like an object constructed, use the objects Attach method to assign handle ownership to the object. If youd like to take ownership of the system handle from an MFC object, call its Detach method. 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 FromHandle function 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 CWnd class and CImageList classes also provide a FromHandlePermanent function that returns an object only if one exists for the handle. This function does not create a temporary object.

Summary

Registering New Window Classes You can use either AfxRegisterClass or AfxRegisterWndClass to register a new window class. In either case, you can pass the registered window classs name in a subsequent call to CWnd::Create to create an instance of that window. AfxRegisterClass is very similar to the Win32 RegisterClass API, 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.

26 / 1166

MFC Programming with Visual C++ 6 Unleashed


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);

Summary

Obtaining an Applications Main Window The AfxGetMainWnd function to get the pointer to the CWnd object that represents the main window for your thread. Typically, it simply returns the m_pMainWnd member of the active CWinThread object. It is prototyped as follows:
CWnd * AFXAPI AfxGetMainWnd();

Note: Dont assume that every CWinThread object in an application has the same m_pMainWnd object. When a new CWinThread object is constructed, it inherits the m_pMainWnd pointer 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, CWnd provides 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 Create and CreateEx methods map very closely to the Win32 APIs CreateWindow and 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, LPVOID lpParam = NULL);

UINT nID,

CreateControl Use the CreateControl method to instantiate an ActiveX control and associate it with the CWnd object. Specify either the controls 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 FromHandle and FromHandlePermanent static functions look up and return an existing CWnd object based upon a passed system window handle (HWND). If a matching CWnd object does not exist, the FromHandle function creates a temporary CWnd object 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

27 / 1166

MFC Programming with Visual C++ 6 Unleashed


You can associate and unassociate a system window handle (HWND) with a CWnd object using the Attach and Detach methods. After youve 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();

Summary

ExecuteDlgInit The ExecuteDlgInit method 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 CDialog class first, however. This method is declared as shown here:
BOOL ExecuteDlgInit(LPCTSTR lpszResourceName); BOOL ExecuteDlgInit(LPVOID lpResource);

PreCreateWindow The PreCreateWindow virtual function is called before a window gets created. You can override this function if you need to modify the CREATESTRUCT of 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 SubclassWindow and SubclassDlgItem methods 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 controls window procedure via the MFC message map mechanism. The UnsubclassWindow method 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 PreSubclassWindow notification 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 GetSafeHwnd method to obtain the window handle attached to the window object. It is called safe because it returns NULL if the this pointer of the CWnd object is equal to NULL. Additionally, this method returns NULL if the CWnd object is not attached to a window, or is attached to a NULL window 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 ModifyStyleEx methods to obtain and change the style and extended style flags for a window. These public methods are declared thus:

28 / 1166

MFC Programming with Visual C++ 6 Unleashed


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

Summary

GetOwner and SetOwner Use the GetOwner and SetOwner methods 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 EnableToolTips method to turn on or off the display of ToolTips for the current window. You can override the OnToolHitTest virtual 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 UpdateData virtual function to either initialize data into the windows child controls or validate and save the data. If the bSaveAndValidate parameter 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 UpdateDialogControls method 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 CenterWindow method 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 EndModalLoop methods to make the specified window modal. RunModalLoop places the window into a modal mode. The window remains modal until your application calls EndModalLoop, which causes ContinueModal to return FALSE and the modal loop to be ended. These methods are declared as follows:
int RunModalLoop(DWORD dwFlags = 0); virtual BOOL ContinueModal();

29 / 1166

MFC Programming with Visual C++ 6 Unleashed


virtual void EndModalLoop(int nResult);

Summary

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 CWnd class map (more or less) directly to the Win32 API functions of the same name (but without the HWND parameter, 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

30 / 1166

MFC Programming with Visual C++ 6 Unleashed


Shell Interaction DragAcceptFiles Icon SetIcon, GetIcon Help GetWindowContextHelpId, SetWindowContextHelpId CFrameWnd The CFrameWnd class, derived from CWnd, is a window that contains the title bar, system menu, border, minimize/maximize buttons, and an active view window. The CFrameWnd class supports the single document interface (SDI). For multiple document interface (MDI) frame windows, use CMDIFrameWnd for the workspace frame and CMDIChildWnd for the MDI child windows. Both of the aforementioned classes are derived from CFrameWnd. For the thinner, toolbox-style frame window, use the CMiniFrameWnd class. To support inplace editing, use the COleIPFrameWnd class.

Summary

Functions The CFrameWnd class 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 GetActiveFrame methods return a pointer to the current document, view, or frame, respectively. The SetActiveView method 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 GetTitle and SetTitle methods obtain and set the text in the frame windows title bar. These methods are declared as shown here:
CString GetTitle() const; void SetTitle(LPCTSTR lpszTitle);

SetMessageText The SetMessageText method 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 InModalState methods control the modality of the frame window. Use BeginModalState to enter the modal state, ExitModalState to end the modal state, and InModalState to determine your current state. These methods are declared as follows:
virtual void BeginModalState(); virtual void EndModalState(); BOOL InModalState() const;

CView

31 / 1166

MFC Programming with Visual C++ 6 Unleashed


The CView class, 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 CView to present your documents data to the user in the required ways. When you create a CView-derived class, at the minimum, you need to override the OnDraw and OnUpdate methods. Methods The CView class 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 OnDraw method 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::IsPrinting to determine the current state. The OnDraw method is declared as shown here:
virtual void OnDraw(CDC *pDC) = 0;

Summary

OnUpdate Override the OnUpdate method to allow your view class to be notified when the document has changed. This method is invoked when CDocument::UpdateAllViews is 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 OnUpdate method is declared as follows:
virtual void OnUpdate(CView *pSender, LPARAM lHint, CObject *pHint);

GetDocument The GetDocument method returns the document to which this view is attached. It is declared as follows:
CDocument *GetDocument() const;

OnActivateVie and OnActivateFrame Override the OnActivateView and OnActivateFrame if 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 document contains the data that your application works with. A view is 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 citys 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 CDocument class. To create a view type, derive a class from the CView or a CView-derived class.

32 / 1166

MFC Programming with Visual C++ 6 Unleashed


For more information about MFC document and view support, refer to Chapter 7. GetTitle and SetTitle The GetTitle and SetTitle methods obtain and set the text in the frame windows 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);

Summary

GetPathName and SetPathName The GetPathName and SetPathName methods obtain and set the fully qualified path associated with the document. You only need to call these methods if you are also overriding OnOpenDocument and OnSaveDocument. The GetPathName and SetPathName methods are declared as shown here:
const CString& GetPathName() const; virtual void SetPathname(LPCTSTR lpszPathname, BOOL bAddToMRU = TRUE);

GetDocTemplate The GetDocTemplate method returns the CDocTemplate object upon which this document is based. If this document does not have a document template associated with it, NULL is returned. This method is declared as follows:
CDocTemplate *GetDocTemplate() const;

IsModified, SetModifiedFlag, and SaveModified The IsModified, SetModifiedFlag, and SaveModified methods are used to control the modification state within the document object. Call SetModifiedFlag whenever 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 SaveModified method. 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 AddView and RemoveView methods to attach and detach a CView-derived object to and from this document. Each successful call to AddView and RemoveView results in a call to OnChangedViewList. The default implementation of OnChangedViewList deletes 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 GetFirstViewPosition and GetNextView methods enable you to iterate through all the view objects (CView) that currently are associated with the document. If no views are available, GetFirstViewPosition returns NULL. These public methods are declared as follows:
virtual POSITION GetFirstViewPosition() const; virtual CView *GetNextView(POSITION& pos) const;

Note: Because GetNextView updates the position value, always check that the current position is not NULL before calling GetNextView again.

33 / 1166

MFC Programming with Visual C++ 6 Unleashed


UpdateAllViews Use the UpdateAllViews method to have the CView::OnUpdate method to be called on one or all views associated with this document. If the pSender parameter 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);

Summary

DeleteContents Override the DeleteContents method to delete your documents 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 documents data in this method. This method is declared as follows:
virtual void DeleteContents();

OnNewDocument, OnOpenDocument, OnSaveDocument, and OnCloseDocument Override the OnNewDocument, OnOpenDocument, OnSaveDocument, and OnCloseDocument virtual 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 OnNewDocument method. These functions are declared as follows:
virtual virtual virtual virtual BOOL BOOL BOOL void OnNewDocument(); OnOpenDocument(LPCTSTR lpszPathName); OnSaveDocument(LPCTSTR lpszPathName); OnCloseDocument();

ReportSaveLoadException Override the ReportSaveLoadException virtual 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 GetFile and ReleaseFile methods 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 CanCloseFrame and PreCloseFrame methods give you an opportunity to handle the closing of document views contained in frame windows. Override the CanCloseFrame virtual 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 PreCloseFrame virtual 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 MFCbased application. You will incorporate all these classes either directly or indirectly in

34 / 1166

MFC Programming with Visual C++ 6 Unleashed


every MFC GUI application that you write. The CObject class 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 CWinApp classes 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 MFCs document/view architecture and the CDocument and CView classes, respectively. When you derive from each of these classes, you can separate your applications business logic from its display logic. Finally, the document views are displayed in GUI windows that are represented by the CFrameWnd and CWnd classes. The windowing classes are the fundamental objects that manage your GUI application and present its data to the user.

Summary

35 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 applicationwithout 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
36 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 executableso 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 dont 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 applications 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 dialogbased. 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.

37 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 formbased 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 dialogs title bar. This dialog is shown in Figure 2.3. Click Next to move to the next AppWizard step.

38 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 youd 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 youd like to link the MFC class library statically as part of your applicationor 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 applications installation routines must install the appropriate MFC DLLs needed by your application.

39 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 wont 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.
40 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 #include lines 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 CWinApp and represents the main class for your MFC application. See Chapter 1, The MFC Architecture, for more information on the CWinApp class. The CMFCSampleDlg class represents the main window of the dialog-based MFCSample application. The CMFCSampleDlg class is derived from the CDialog class, 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 CMFCSample class is declared in MFCSampleDlg.h and defined in MFCSampleDlg.cpp. Likewise, the CAboutDlg class 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 applications pull-down menu. The CAboutDlg class is derived from CDialog and is associated with a generated dialog resource, named IDD_ABOUTBOX. The CAboutDlg class 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
41 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 applications dialog windows. MFC AppWizard generated two dialog resources for the MFC Sample application, named IDD_ABOUTBOX and IDD_MFCSAMPLE_DIALOG, representing the About box and the main window, respectively. By double-clicking on the dialogs 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_MAINFRAME for the MFCSample application. The IDR_MAINFRAME icon is the image that appears in your applications 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_INFO for 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 userdefinable 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.
42 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 interestinglike 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, youll 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 control is 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 categoriesbut, 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
43 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 applications 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 userhowever, 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
44 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 youd 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 youd 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.


45 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 youd like your application to indicate graphically the current amount of progress. Figure 2.22 demonstrates this control.

46 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 stereos 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 applications data in your user interface. If you use this control, your GUI needs to display only the controls in which
47 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

the user is interestedrather 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 controls area when clicked. Use this control when you have multimedia graphics that youd 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 AfxInitRichEdit in your application classs InitInstance method 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


48 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 imageswithout 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 custom control. 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 OnInitDialog method 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_currentColor member variables. Listing 2.1 Initialization 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 applications main window, as shown earlier in Figure 2.9. Note: The initialization of m_fileName is 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.
49 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 OnInitDialog method. MFC calls OnInitDialog after 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 classs OnInitDialog method. Listing 2.2 shows the calls to EnableToolTips and DrawBitmapWithColor in initialization section for the MFCSample application. Listing 2.2 Dialog Initialization in the OnInitDialog Method
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 applications initialization code after the Visual Studiogenerated comment, TODO: Add extra initialization here. Using Dialog Controls After youve created your dialog class, youll 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 dialogs controls, youll need instances of classes for each of your dialogs 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 dialogs 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.

50 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 Controlusually types such as CString and 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 CWnd object for your
51 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

dialogs control using the GetDlgItem method. Listing 2.3 demonstrates the creation of an automatic variable named pbOK that is associated with the IDOK control in your dialog. Listing 2.3 On-the-Fly Control Variable Creation
CButton *pbOK = DYNAMIC_DOWNCAST(CButton, GetDlgItem(IDOK))

Caution: Because the pointer returned from GetDlgItem is 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 youd 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
52 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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.4 Handling 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 OnInitDialog method, as shown earlier in Listing 2.2. After youve done this, your application receives TTN_NEEDTEXT notification 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 classs message map definition in the source file between BEGIN_MESSAGE_MAP and END_MESSAGE_MAP:
53 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnNeedToolTipText)

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

Listing 2.5 Handling 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 return TRUE; } return FALSE; } = AfxGetResourceHandle();

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_edMyText CEditBox member variable, you can have an m_myText CString member 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
54 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 classs constructor, as shown earlier in Listing 2.1. Additionally, it creates a method named DoDataExchange that 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.6 DDX Method DoDataExchange in 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 DoDataExchange method for the CMFCSampleDlg class, 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_COLOR is associated with m_stColor , IDC_PB_CUSTOM_MODELESS is associated with m_pbCustomModeless, and IDC_ST_FONT is associated with m_stFont. The fourth association is between a control and a data member variable: IDC_ST_FILE and m_fileName. UpdateData The UpdateData method 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 UpdateData method is declared as follows in the CWnd class:
BOOL UpdateData(BOOL bSaveAndValidate = TRUE);

If you pass TRUE as the parameter to UpdateData, the data moves from the controls to their associated data members. Passing FALSE does the reverse. If an error occurs due to a data validation error, FALSE is 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 CFileDialog class represents the File Open/Save dialog. By instantiating an
55 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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.7 Simple 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 CColorDialog class 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
56 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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.8 Simple 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 CFontDialog class 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.9 Simple Code to Display the Font Selection Dialog
void CMFCSampleDlg::OnPbFont() { CFontDialog fontDlg; if (fontDlg.DoModal() == IDOK) { CFont font; LOGFONT logFont; fontDlg.GetCurrentFont(&logFont);
57 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

font.CreateFontIndirect(&logFont); m_stFont.SetFont(&font); } }

Print Dialog The MFC CPrintDialog class 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.10 Simple 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); } }

58 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Figure 2.39 The Windows standard Print selection dialog. Summary Microsoft Visual Studios 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 applications functionality in a relatively straightforward manner. Additionally, the Visual Studio resource editor enables you to add standard controls to your applicationfrom 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 dialogs controls. When equipped with DDX support, your applications 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.

59 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 programmers 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
60 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

{ 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.1 The Bit Flags Used by the INITCOMMONCONTROLSEX Structure Bit Flags Name ICC_ANIMATE_CLASS ICC_BAR_CLASSES ICC_COOL_CLASSES ICC_DATE_CLASSES ICC_HOTKEY_CLASS ICC_INTERNET_CLASSES ICC_LISTVIEW_CLASSES ICC_PAGESCROLLER_CLASS ICC_PROGRESS_CLASS ICC_TAB_CLASSES ICC_TREEVIEW_CLASSES ICC_UPDOWN_CLASS ICC_USEREX_CLASSES ICC_WIN95_CLASSES Meaning Loads the animate control class. Loads the toolbar, status bar, trackbar, and tooltip control classes. Loads the rebar control class. Loads the date and time picker control class. Loads the hot key control class. Loads the IP address class. Loads the list view and header control classes. Loads the pager control class. Loads the progress bar control class. Loads the tab and tooltip control classes. Loads the tree view and tooltip control classes. Loads the up-down control class. Load the ComboBoxEx class. 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 whats 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 NMHDR structure (described in the following section), and are shown in Table 3.2. Table 3.2 Notification Codes Used by All the Win32 Common Controls
61 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Notification Code NM_CLICK NM_DBLCLK NM_KILLFOCUS NM_OUTOFMEMORY NM_RCLICK NM_RDBLCLK NM_RETURN NM_SETFOCUS

Meaning Sent when a user clicks the left mouse button within the control. Sent when a user double-clicks the left mouse button within the control. Sent when the control loses the input focus. Sent when the control cant finish an operation because of insufficient free memory. Sent when a user clicks the right mouse button within the control. Sent when a user double-clicks the right mouse button within the control. Sent when a user presses the Enter key and the control has the input focus. Sent when the control receives the input focus.

The Notification Message Structure Win32 common controls use a special structure to send notification messages: the NMHDR structure. This structure contains the window handle of the sender, the control ID of the sender, and the notification code being sent. The NMHDR structure 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_NOTIFY messages; Windows standard controls (also used by 16-bit Windows) send most notification messages as WM_COMMAND messages. Note: The WM_NOTIFY message isnt used for 16-bit Windows because the message was designed specifically for 32-bit Windows. WM_NOTIFY provides 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_NOTIFY notification 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 controls owner class. The prototype for CWnd::OnNotify() looks like this:
62 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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

To discover the reason for the occurrence of the WM_NOTIFY message (or, Whats the user doing with this control?), you must look closely at the parameters of the CWnd::OnNotify() method: The wParam parameter is the control ID of the control that sent the message or is NULL if the message didnt come from a control. The lParam parameter is a pointer to a notification message structure (NMHDR) that contains the current notification code along with some additional information. The pResult parameter is a pointer to an LRESULT variable that stores the result code if the message is handled. If the NMHDR structure is actually a member of a larger structure (as it is for most Win32 common controls), you must cast it to an NMHDR when you use it; only a few common controls actually use the simple NMHDR structure. The lParam parameter can be a pointer to either an NMHDR structure or some other structure that has an NMHDR structure embedded as its first data member and has been typecast to an NMHDR structure. Usually, the pointer is to a larger structure containing an NMHDR structure and not to the NMHDR structure itself. In these cases, because the NMHDR structure is the first member of the larger structure, it can be successfully typecast to an NMHDR structure. A Better Notification Handling Scheme As stated earlier in this chapter, the CWnd::OnNotify() method handles notification messages. Although you can do it, you really shouldnt override the CWnd::OnNotify() method to receive notifications from controlstheres 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 controls parent class. The ON_NOTIFY message map macro uses this syntax:
ON_NOTIFY(NotifyCode, ControlID, ClassMethod)

In this syntax, NotifyCode represents the notification code being sent, ControlID represents the control identifier the parent window uses to communicate with the control that sent notification, and ClassMethod represents 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 pNotifyStruct is a pointer to an NMHDR structure, and result is 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
63 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

method, you can use the ON_NOTIFY_RANGE macro in the message map instead of using the ON_NOTIFY macro. 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_RANGE macro must be numerically contiguous. For example, if three CHeader controls are to use the same notification message handler, these control IDs would work:
// Contiguous #define IDC_HEADER1 #define IDC_HEADER2 #define IDC_HEADER3 100 101 102

These control IDs wouldnt work:


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

The ON_NOTIFY_RANGE message map macro uses this format:


ON_NOTIFY_RANGE(NotifyCode, FirstCtrlID, LastCtrlID, ClassMethod)

In this syntax, NotifyCode represents the notification code being sent, ControlIDFirst represents the control identifier of the first control in the contiguous range, ControlIDFirst represents the control identifier of the last control in the contiguous range, and ClassMethod represents the message handler method called in response to the notification. The message handler method prototype in the owners class declaration is as follows:
afx_msg void ClassMethod(UINT ControlID, NMHDR* pNotifyStruct, LRESULT* result);

In this syntax, ControlID is the identifier of the control that sent the notification, pNotifyStruct is a pointer to an NMHDR structure, and result is 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 lets take a look at Windows common controls and see how MFC makes using them fairly easy. Hot Key Controls: Class CHotKeyCtrl A hot key is a key combination used to perform some action quickly. A hot key control is 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 doesnt actually set the hot key, however; your code must do this explicitly. To enable a hot key, your application must get the hot keys values and associate these values with either a window or a thread.
64 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 CHotKeyCtrl class 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 CHotKeyCtrl object when the object is created. CHotKeyCtrl Class Methods The CHotKeyCtrl class offers a minimal set of methods for manipulating the control and its data. The constructor, CHotKeyCtrl::CHotKeyCtrl(), allocates a CHotKeyCtrl object that is initialized with the CHotKeyCtrl::Create() method. Table 3.3 describes the classs methods. Table 3.3 CHotKeyCtrl Class Methods Method GetHotKey() SetHotKey() SetRules() Description Gets the virtual-key code and modifier flags of a hot key from a hot key control. Sets the hot key combination for a hot key control. Defines invalid key combinations and the default modifier combination for a hot key control.

Creating and Initializing a CHotKeyCtrl Object To create a CHotKeyCtrl object, you use the two-step construction process typical of MFC: 1. Call the class constructor CHotKeyCtrl::CHotKeyCtrl() to allocate the object. 2. Initialize the CHotKeyCtrl object 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: dwStyle Specifies the window style for the control. rect The rectangle specifying the size and position of the control. pParentWnd A pointer to the owner of the control. nID The 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.
65 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 key for activating a top-level window by using the CWnd::SendMessage() method, sending a WM_SETHOTKEY message to the window to be activated. Set up a thread-specific hot key by calling the Win32 API function RegisterHotKey().

Global Hot Keys A global hot key enables 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_SETHOTKEY message to that window. Assume that m_pHotKey is a pointer to a CHotKeyCtrl object and that pWnd is a pointer to the target window. When the hot key is activated, you can associate m_pHotKey with pWnd like this:
WORD wKeyShift = m_pHotKey->GetHotKey(); pWnd->SendMessage(WM_SETHOTKEY, wKeyShift);

Thread-Specific Hot Keys A thread-specific hot key enables 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, hWnd identifies the window to receive the WM_HOTKEY messages generated by the hot key. The id parameter is the control ID of the hot key, which must be in the range of 0x0000 through 0xBFFF for an application, or 0xC000 through 0xFFFF for a shared DLL. The fsModifiers parameter is the modifier keys that must be pressed in combination with the key specified by the vk parameter to generate the WM_HOTKEY message. This can be a combination of the values shown in Table 3.4. The vk parameter is the virtual-key code of the hot key. Table 3.4 The Modifier Flags Used with RegisterHotKey() Value MOD_ALT MOD_CONTROL MOD_SHIFT Meaning The Alt key must be held down. The Ctrl key must be held down. 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.

66 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 do usually interact intimately with another Windows control, and the spin controls 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 buddys 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 doesnt need a 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, CSpinButtonCtrl is derived directly from CWnd and 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 controls 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 controls styles determine its appearance and operations. Style bits are typically set when the control is initialized with the CSpinButtonCtrl::Create() method. Table 3.5 The Window Styles Defined for a Spin Control Style Macro UDS_ALIGNLEFT Meaning Aligns a spin control to the left edge of the buddy
67 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

UDS_ALIGNRIGHT UDS_ARROWKEYS UDS_AUTOBUDDY UDS_HORZ UDS_NOTHOUSANDS UDS_SETBUDDYINT

UDS_WRAP

window. Aligns a spin control to the right edge of the buddy window. Allows the control to increment and decrement the current position when the up-arrow and down-arrow keys are pressed on the keyboard. Automatically selects a buddy window by choosing the previous window in the Z-order. Makes the controls arrows point left and right instead of up and down. Prevents the thousands separator between every three decimal digits. 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. 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 (UpDown StylesUDS). But because the MFC team at Microsoft wrapped this control into a class called a spin button control , thats what Ill call it, too.

CSpinButtonCtrl Messages Because MFC wraps the Windows spin control messages (such as UDM_GETRANGE or UDM_SETBUDDY) into CSpinButtonCtrl class 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 controls parent class. Table 3.6 shows the message map entries for spin control messages. Table 3.6 Message Map Entries for Spin Button Control Messages Message Map Entry ON_WM_HSCROLL ON_WM_VSCROLL ON_EN_UPDATE Meaning Sent by a spin control with the UDS_HORZ style when the arrow buttons are clicked. Sent by a spin control with the UDS_VERT style (the default) when the arrow buttons are clicked. Sent by a buddy edit control when the text is changed.
68 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

CSpinButtonCtrl Class Methods The CSpinButtonCtrl class 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 CSpinButtonCtrl class method declarations and detailed descriptions of their parameters, you wont find detailed descriptions here, but I will provide an overview of each method so that you know what to look for when you need it. The CSpinButtonCtrl constructor, 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.7 CSpinButtonCtrl Class Methods Method GetAccel() GetBase() GetBuddy() GetPos() GetRange() SetAccel() SetBase() SetBuddy() SetPos() SetRange() Description Retrieves acceleration information for a spin control. Retrieves the current base for a spin control. Retrieves a pointer to the current buddy window. Retrieves the current position of a spin control. Retrieves the upper and lower limits (range) for a spin control. Sets the acceleration for a spin control. Sets the base for a spin control. Sets the buddy window for a spin control. Sets the current position for the control. Sets the upper and lower limits (range) for a spin control.

Creating and Initializing a Spin Control A CSpinButtonCtrl object, 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 CSpinButtonCtrl object by calling the constructor CSpinButtonCtrl::CSpinButtonCtrl() using the C++ keyword new. 2. Initialize the CSpinButtonCtrl object and attach a Windows spin button common control to it with the CSpinButtonCtrl::Create() method to set the spin controls parameters and styles. For example, a CSpinButtonCtrl object is allocated and a pointer to that object is returned with this code:
CSpinButtonCtrl* pMySpinner = new CSpinButtonCtrl ;

The pointer pMySpinner is then initialized with a call to the CSpinButtonCtrl::Create() method. This method is declared as follows:
BOOL Create(DWORD dwStyle, const RECT& rect,
69 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 pParentWnd is a pointer to the owner of the control, and nID is the control ID used by the parent to communicate with the spin control. Sample Program: SPIN1 Now lets 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 windows 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)
70 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Then the control IDs for the child window controls in this program are defined, as are the two classes used in this application: CSpinApp and CMainWnd. The first class is the application class CSpinApp, which is a descendant of CWinApp and 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 windows client area color as well as these two messagehandler 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 CMainWnd class. 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, ON_WM_SIZE() ON_EN_UPDATE(IDC_BUDDY1, ON_EN_UPDATE(IDC_BUDDY2, ON_EN_UPDATE(IDC_BUDDY3, END_MESSAGE_MAP() CMainFrame) OnBuddyUpdate) OnBuddyUpdate) OnBuddyUpdate)

Notice that the CEdit buddy 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 RGB macro (that macro is used to change the frame windows client area color later):
// Set scroll ranges m_pSpin1->SetRange(0, 255);
71 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 RGB macro. Three CString local 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 CBrush object is declared and initialized using these integers in an RGB macro 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 windows client area is retrieved into a CRect object 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:
72 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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, CSliderCtrl is derived directly from CWnd and inherits all the functionality of CWnd. A slider control can be created as a windows 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 CDialogderived 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 controls 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 controls 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.8 The Window Styles Defined for a Slider Control Style Macro TBS_AUTOTICKS Meaning Gives a slider tick marks for each increment in its range of values. Tick marks are automatically
73 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

TBS_BOTH TBS_BOTTOM TBS_ENABLESELRANGE TBS_HORZ TBS_LEFT TBS_NOTICKS TBS_RIGHT TBS_TOP TBS_VERT

created with a call to the SetRange() method. Puts tick marks on both sides of a slider control, no matter what its orientation. Puts tick marks on the bottom of a horizontal slider control. Gives a slider tick marks in the shape of triangles that indicate the starting and ending positions of a selected range. Orients a slider horizontally (the default). Puts tick marks on the left side of a vertical slider control. A slider wont have tick marks. Puts tick marks on the right side of a vertical slider control. Puts tick marks on the top of a horizontal slider control. Orients a slider vertically.

Note: These styles all have the TBS_* prefix. Like the spin button controls 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 StylesTBS). 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_HSCROLL and 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 controls parent class. Table 3.9 shows the message map entries for slider control messages. Table 3.9 The Two Message Map Entries for Slider Control Messages Message Map Entry ON_WM_HSCROLL ON_WM_VSCROLL Meaning Sent by a slider control with the TBS_HORZ style when the arrow buttons are clicked. Sent by a slider control with the TBS_VERT style (the default) when the arrow buttons are clicked.

74 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

But there is more to the messaging than a few simple WM_* messages. The ON_WM_HSCROLL and ON_WM_VSCROLL messages 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 sliders 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 controls range of possible values. Heres 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.10 The Notification Codes Used by the Slider Control in the OnHScroll() and OnVScroll() Methods Code TB_BOTTOM TB_ENDTRACK TB_LINEDOWN TB_LINEUP TB_PAGEDOWN TB_PAGEUP TB_THUMBPOSITION TB_THUMBTRACK TB_TOP Meaning A user pressed the End key on the keyboard. A user released a key, causing some virtual key code to be sent (WM_KEYUP). A user pressed the down-arrow or right-arrow key on the keyboard. A user pressed the up-arrow or left-arrow key on the keyboard. A user clicked the channel below or to the right of the slider or pressed the PageDown key. A user clicked the channel above or to the left of the slider or pressed the PageUp key. A user released the left mouse button (WM_LBUTTONUP) after dragging the slider (TB_THUMBTRACK). A user dragged the slider. 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_TOP codes are sent. Likewise, the TB_THUMBPOSITION and TB_THUMBTRACK codes 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_THUMBPOSITION or SB_THUMBTRACK. If the nSBCode parameter is anything other than these two codes, nPos isnt used.
75 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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

CSliderCtrl Class Methods The CSliderCtrl class 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 CSliderCtrl class method declarations and detailed descriptions of their parameters, detailed descriptions arent given here, but I do provide an overview of each method so that you know what to look for when you need it. The CSliderCtrl constructor, 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.11 CSliderCtrl Class Methods That Deal with Attributes Method GetChannelRect() GetLineSize() GetNumTics() GetPageSize() GetPos() GetRange() GetRangeMax() GetRangeMin() GetSelection() GetThumbRect() GetTic() GetTicArray() GetTicPos() SetLineSize() SetPageSize() SetPos() SetRange() Description Gets the size of the slider controls channel. Gets the line size of a slider control. Gets the number of tick marks in a slider control. Gets the page size of a slider control. Gets the current position of the slider. Gets the minimum and maximum positions for a slider. Gets the maximum position for a slider. Gets the minimum position for a slider. Gets the range of the current selection. Gets the size of the slider controls thumb. Gets the position of the specified tick mark. Gets the array of tick mark positions for a slider control. Gets the position of the specified tick mark, in client coordinates. Sets the line size of a slider control. Sets the page size of a slider control. Sets the current position of the slider. Sets the minimum and maximum positions for a slider.
76 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

SetRangeMax() SetRangeMin() SetSelection() SetTic() SetTicFreq()

Sets the maximum position for a slider. Sets the minimum position for a slider. Sets the selection range for a slider. Sets the position of the specified tick mark. 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.12 CSliderCtrl Class Methods That Deal with Operations Method ClearSel() ClearTics() VerifyPos() Description Clears the current selection from a slider control. Removes the current tick marks from a slider control. Verifies that the position of a slider control is zero.

Creating and Initializing a Slider Control A CSliderCtrl object, 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 CSliderCtrl object by calling the constructor CSliderCtrl::CSliderCtrl() using the C++ keyword new. 2. Initialize the CSliderCtrl object and attach a Windows slider common control to it with the CSliderCtrl::Create() method to set slider parameters and styles. For example, a CSliderCtrl object is allocated, and a pointer to the CSliderCtrl object is returned with this code:
CSliderCtrl* pMySlider = new CSliderCtrl;

The pointer pMySlider must 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 pParentWnd is a pointer to the owner of the control, and nID is 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
77 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 dont 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 programs 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,
78 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

CScrollBar* pScrollBar) { // *Much* simpler than a scroll bar! // Change to the new color UpdateClientColor(); // call inherited handler CMainFrame::OnHScroll(nSBCode, nPos, pScrollBar); }

If youve ever written scrollbar code, youll see instantly that the slider control is a lot more user-friendly than a scroll barand makes for much easier coding! Sample Program: SLIDER1 Next lets 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 its 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: CSliderApp and CMainWnd. The application class CSliderApp is almost exactly like class CspinApp, which you saw earlier. The CMainWnd class, 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 windows 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.
79 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Implementing the SLIDER1 Program (SLIDER1.CPP) The first order of business is setting up the message map for the CMainWnd class. 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 RGB macro (that macro is used to change the frame windows 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 its done. Listing 3.1 Resizing 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;
80 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 nRed nGreen nBlue the current scroll position = m_pSlider1->GetPos(); = m_pSlider2->GetPos(); = 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 control is 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 operations progress, the progress bar is typically for output only. Like a slider control, a progress bar control has a range and 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
81 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

bar. MFC provides the services of a Windows progress bar common control in the class CProgressCtrl, which is derived directly from CWnd and 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 CProgressCtrl class offers a minimal set of methods for manipulating the control and its data. The constructor, CProgressCtrl::CProgressCtrl(), allocates a CProgressCtrl object that is initialized with the CProgressCtrl::Create() method. Table 3.13 describes the classs methods. Table 3.13 CProgressCtrl Class Methods Method OffsetPos() SetPos() SetRange() SetStep() StepIt() Description Advances the current position of a progress bar control by a specified increment and redraws the bar to show the new position. Sets the current position for a progress bar control and redraws the bar to show the new position. Sets the minimum and maximum ranges for a progress bar control and redraws the bar to show the new ranges. Specifies the step increment for a progress bar control. 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 CProgressCtrl object, you use the two-step construction process typical of MFC: 1. Call the class constructor CProgressCtrl::CProgressCtrl() to allocate the object. 2. Initialize the CProgressCtrl object 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: dwStyle Specifies the window style for the control. This can be any combination of the general window styles. rect The rectangle specifying the size and position of the control. pParentWnd A pointer to the owner of the control. nID The control ID used by the parent to communicate with the control.
82 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Using a Progress Control The only necessary settings for a progress control are the range and current position. The range represents the entire duration of the operation. The current position represents 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, doesnt wrap back around to the minimum valueinstead, the new position is adjusted to remain within the controls 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 list maintains 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
83 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 lists 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 CImageList class offers a complete set of methods for manipulating the images stored in a control. The CImageList constructor, CImageList::CImageList(), allocates a CImageList object thats initialized with the CImageList::Create() method. Table 3.14 describes the methods provided by CImageList. Table 3.14 CImageList Class Methods Method Add() Attach() BeginDrag() DeleteImageList() Detach() DragEnter() DragLeave() DragMove() DragShowNolock() Draw() EndDrag() ExtractIcon() GetBkColor() GetDragImage() GetImageCount() GetImageInfo() GetSafeHandle() Read() Description Adds an image (or multiple images) to an image list. Attaches an image list to a CImageList object. Begins dragging an image. Deletes an image list. Detaches an image list object from a CImageList object and returns a handle to an image list. Locks the window to prevent updates during a drag operation and displays a drag image at the specified location. Unlocks the window and hides the drag image so that the window can be updated. Moves the image being dragged during a drag-anddrop operation. Shows or hides the drag image during a drag operation without locking the window. Draws the image being dragged during a drag-anddrop operation. Ends a drag operation. Creates an icon based on an image and mask in an image list. Gets the current background color for an image list. Gets the temporary image list used for dragging. Gets the number of images in an image list. Gets information about an image. Gets the underlying Windows image list stored in m_hImageList. Reads an image list from an archive.
84 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Remove() Replace() SetBkColor() SetDragCursorImage() SetOverlayImage() Write()

Removes an image from an image list. Replaces an image in an image list with a new image. Sets the background color for an image list. Creates a new drag image. Adds the zero-based index of an image to the list of images to be used as overlay masks. Writes an image list to an archive.

Creating and Initializing a CImageList Control To create a CImageList object, you use the two-step construction process typical of MFC: 1. Call the class constructor CImageList::CImageList() to allocate the object. 2. Initialize the CImageList object 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.15 The Parameters Used by the CImageList Control Parameter cx, cy dx, dy bMask nInitial nGrow nBitmapID Description Width and height of each image, in pixels. Width and height of each image, in pixels (same as cx and cy). A Boolean flag that specifies whether an image contains a monochrome mask. The number of images initially contained in an image list. The number of new images a resized image list can contain. The resource ID of a bitmap to be associated with an
85 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

crMask lpszBitmapID imagelist1 nImage1 imagelist2 nImage2

image list. The color used to generate an image mask. Each pixel of this color in the specified bitmap is changed to black. A string that contains the resource IDs of all images stored in an image list. A pointer to another CImageList object. The number of images contained in imagelist1. A pointer to another CImageList object. The number of images contained in imagelist2.

List View Controls: Class CListCtrl A list view control is 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 controls current window style. Table 3.16 describes the four view styles provided by the list view control. Table 3.16 The Four Types of Views Supported by the List View Common Control View Icon view Description In this view, list items are displayed as full-sized icons with labels below them, as specified by the LVS_ICON window style. In this view, a user can drag list items to any location in the list view window. In this view, list items are displayed as small icons with the labels to their right, as specified by the LVS_SMALLICON window style. In this view, a user can drag list items to any location in the list view window. In this view, list items are displayed as small icons with labels to their right, as specified by the LVS_LIST window style. In this view, items are arranged and fixed in columns; they cant be dragged to any other list view location. In this view, list items are displayed each on its own line, with information arranged in columns as specified by the LVS_REPORT window 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_NOCOLUMNHEADER window style is also specified.

Small icon view

List view

Report view

As youll see later in this chapter, other window styles enable you to manage a list view controls visual and functional aspects. Figure 3.6 shows four instances of Explorer, using the four view styles.
86 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 CWnd and 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 controls 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 controls 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().

87 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Table 3.17 The Window Styles Defined for a List View Common Control Style Macro LVS_ALIGNLEFT LVS_ALIGNTOP LVS_AUTOARRANGE LVS_EDITLABELS LVS_ICON LVS_LIST LVS_NOCOLUMNHEADER LVS_NOLABELWRAP LVS_NOSCROLL LVS_NOSORTHEADER LVS_OWNERDRAWFIXED LVS_REPORT LVS_SHAREIMAGELISTS LVS_SINGLESEL LVS_SMALLICON LVS_SORTASCENDING LVS_SORTDESCENDING Meaning Items are left-aligned in icon and small icon view. Items are aligned with the top of the control in icon and small icon view. Icons are automatically arranged in icon view and small icon view. Allows item text to be edited in place. Icon view. List view. No column header is displayed in report view. Item text is displayed on a single line in icon view. Disables scrolling. Column headers dont function as buttons. Enables the owner window to paint items as desired while in report view. Report view. Enables image lists to be used by multiple list view controls. Allows only one item at a time to be selected. Small icon view. Sorts items in ascending order based on item text. 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.18 Windows Common Control Window Styles Used by a List View Control Style Macro CCS_BOTTOM CCS_NODIVIDER CCS_NOHILITE CCS_NOMOVEY CCS_NOPARENTALIGN CCS_NORESIZE CCS_TOP Meaning The control aligns itself at the bottom of the parent windows client area and sizes itself to the width of its parent windows client area. Prevents a two-pixel highlight from being drawn at the top of the control. Prevents a one-pixel highlight from being drawn at the top of the control. Causes the control to resize and move itself horizontally (but not vertically) in response to a WM_SIZE message (default). Prevents the control from automatically aligning to the top or bottom of the parent window. Forces a control to use the width and height specified when created or resized. The control aligns itself at the top of the parent windows client area and sizes itself to the width of
88 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

its parent windows 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_ICON list view style. Small image list An image list that contains the images of the small icons used by views that dont have the LVS_ICON list view style. State image list An image list that can contain state images that can appear next to an items 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 CListCtrl methods 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_ITEM structure, which looks like
89 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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: mask A set of bit flags specifying the members of the LV_ITEM structure that contain valid data or that need to be filled in (see Table 3.19). iItem The zero-based index of an item. iSubItem The one-based index of a subitem. state The current state of an item. stateMask Specifies the bits of the state member that are valid. pszText A pointer to a string that contains the item text if the structure specifies item attributes. cchTextMax The size of the buffer pointed to by the pszText member. iImage The index of an items icon in the icon and small icon image lists. lParam An application-defined 32-bit value to associate with the item. Table 3.19 The Bit Flags Used for the LV_ITEM mask Member Value LVIF_TEXT LVIF_IMAGE LVIF_PARAM LVIF_STATE LVIF_DI_SETITEM Meaning The pszText member is valid. The iImage member is valid. The lParam member is valid. The state member is valid. Windows should store the requested list item information.

List View Notification Messages Like most Windows common controls, a list view control sends WM_NOTIFY notification messages to its parent window. These messages can be trapped and handled by writing message handlers in the list view controls parent class. Table 3.20 shows the notifications used by list view controls, as defined in COMMCTRL.H. Table 3.20 Notification Messages Defined for List View Controls Message Map Entry LVN_BEGINDRAG Meaning A drag-and-drop operation involving the left mouse
90 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

LVN_BEGINLABELEDIT LVN_BEGINRDRAG LVN_COLUMNCLICK LVN_DELETEALLITEMS LVN_DELETEITEM LVN_ENDLABELEDIT LVN_GETDISPINFO LVN_INSERTITEM LVN_ITEMCHANGED LVN_ITEMCHANGING LVN_KEYDOWN LVN_PEN LVN_SETDISPINFO

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

Creating and Initializing a CListCtrl Object To create a CListCtrl object, you use the two-step construction process typical of MFC: 1. Call the class constructor CListCtrl::CListCtrl() to allocate the object. 2. Initialize the CListCtrl object 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: dwStyle Specifies the combination of styles used by a list control. rect Specifies a list controls size and position. pParentWnd Specifies the list controls parent window. nID Specifies the control identifier for a list control.

The dwStyle parameter 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 CListView object to a CListCtrl object at runtime.
91 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Using the List View Control Using the list view control generally requires several steps, including these: 1. 2. 3. 4. Attaching the image list(s). Adding columns for report view. Adding items to a CListCtrl object. Overriding the inherited OnChildNotify() method to handle WM_NOTIFY messages.

The following sections look at each of these items a little more closely. Attaching the Image List(s) If the list view control youre creating uses the LVS_ICON style, youll need image lists for the list view items. Use the CImageList class 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_REPORT style, 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_COLUMN structure and call the InsertColumn() method to create each desired column. An LV_COLUMN structure is defined as follows:
typedef struct _LV_COLUMN { UINT mask; // int fmt; // int cx; // LPTSTR pszText; // int cchTextMax; // int iSubItem; // } LV_COLUMN;

Specifies valid data members Column alignment specifier Width of column, in pixels Column heading Character size of pszText Index of a subitem

The members for this structure are described following: mask Specifies 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. fmt Specifies 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). cx Specifies the pixel width of a column. pszText A pointer to a columns heading text string. cchTextMax Specifies the number of characters in the buffer pointed to by the pszText member. iSubItem Specifies the index of subitem associated with the column. Table 3.21 The Possible Values for the LV_COLUMN mask Member Value LVCF_FMT Meaning The fmt member is valid.
92 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

LVCF_SUBITEM LVCF_TEXT LVCF_WIDTH

The iSubItem member is valid. The pszText member is valid. The cx member is valid.

Note: The LV_COLUMN structure is used with the LVM_GETCOLUMN, LVM_SETCOLUMN, LVM_INSERTCOLUMN, and LVM_DELETECOLUMN list view control messages. The CListCtrl class 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 thats 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.2 Adding 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 childs 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); } } }
93 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 CWnd and 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 controls 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 controls styles determine its appearance and operation. Style bits are typically set when the control is initialized with the CTreeCtrl::Create() method. Table 3.22 The Window Styles Defined for a Tree View Control Style Macro TVS_HASLINES TVS_LINESATROOT TVS_HASBUTTONS TVS_EDITLABELS TVS_SHOWSELALWAYS TVS_DISABLEDRAGDROP Meaning Child items have lines linking them to corresponding parent items. Child items have lines linking them to the root of the tree. The tree has a button to the left of each parent item. Tree view item labels can be edited. A selected item will remain selected, even if the tree view control loses the input focus. Prevents the tree view control from sending TVN_BEGINDRAG notification messages.

Tree View Notification Messages Like most of the Windows common controls, a tree view control sends WM_NOTIFY notification 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 controls parent class. Table 3.23 shows the notification messages defined in COMMCTRL.H.
94 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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

CTreeCtrl Class Methods The CTreeCtrl class offers a full set of methods for manipulating the control and its data. The CTreeCtrl constructor, CTreeCtrl::CTreeCtrl(), allocates a CTreeCtrl object 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.24 CTreeCtrl Class Methods Method CreateDragImage() DeleteAllItems() DeleteItem() EditLabel() EnsureVisible() Expand() GetChildItem() GetCount() GetDropHilightItem() GetEditControl() Description Creates a dragging bitmap for the specified tree view item. Deletes all items in a tree view control. Deletes a new item in a tree view control. Edits a specified tree view item in place. Ensures that a tree view item is visible in its tree view control. Expands or collapses the child items of the specified tree view item. Gets the child of a specified tree view item. Gets the number of tree items associated with a tree view control. Gets the target of a drag-and-drop operation. Gets the handle of the edit control used to edit the
95 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

GetFirstVisibleItem() GetImageList() GetIndent() GetItem() GetItemData() GetItemImage() GetItemRect() GetItemState() GetItemText() GetNextItem() GetNextSiblingItem() GetNextVisibleItem() GetParentItem() GetPrevSiblingItem() GetPrevVisibleItem() GetRootItem() GetSelectedItem() GetVisibleCount() HitTest() InsertItem() ItemHasChildren() Select() SelectDropTarget() SelectItem() SetImageList() SetIndent() SetItem() SetItemData() SetItemImage() SetItemState() SetItemText() SortChildren() SortChildrenCB()

specified tree view item. Gets the first visible item of the specified tree view item. Gets the handle of the image list associated with a tree view control. Gets the offset (in pixels) of a tree view item from its parent. Gets the attributes of a specified tree view item. Gets the 32-bit application-specific value associated with an item. Gets the images associated with an item. Gets the bounding rectangle of a tree view item. Gets the state of an item. Gets the text of an item. Gets the next tree view item that matches a specified relationship. Gets the next sibling of the specified tree view item. Gets the next visible item of the specified tree view item. Gets the parent of the specified tree view item. Gets the previous sibling of the specified tree view item. Gets the previous visible item of the specified tree view item. Gets the root of the specified tree view item. Gets the currently selected tree view item. Gets the number of visible tree items associated with a tree view control. Gets the current position of the cursor related to the CTreeCtrl object. Inserts a new item in a tree view control. Determines whether an item has child items. Selects, scrolls into view, or redraws a specified tree view item. Redraws the tree item as the target of a drag-anddrop operation. Selects a specified tree view item. Sets the handle of the image list associated with a tree view control. Sets the offset (in pixels) of a tree view item from its parent. Sets the attributes of a specified tree view item. Sets the 32-bit application-specific value associated with an item. Associates images with an item. Sets the state of an item. Sets the text of an item. Sorts the children of a given parent item. Sorts the children of a given parent item using an
96 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

application-defined sort function.

Creating and Initializing a Tree View Control To create a CTreeCtrl object, you use the two-step construction process typical of MFC: 1. Call the class constructor CTreeCtrl::CTreeCtrl() to allocate the object. 2. Initialize the CTreeCtrl object 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: dwStyle Specifies 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. rect The rectangle specifying the size and position of the control. pParentWnd A pointer to the owner of the control. nID The 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_NOTIFY macro in the message map entry for control notifications in the parent class, or make the class more reusable by placing an ON_NOTIFY_REFLECT macro in the control windows message map to let it handle its own notifications. The tree controls notification messages are listed in Table 3.23, earlier in this chapter. Sample Program: TREELIST.EXE On the companion CD-ROM, youll find the program TREELIST.EXE. This program uses the tree view and list view control classes (see Figure 3.7).

97 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Figure 3.7 The TREELIST program. The TREELIST program uses a CTreeCtrl object and a CListCtrl object; 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 control is 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:


98 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 CWnd and 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 CDialogderived 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 controls 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 controls 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.25 The Window Styles Designed for Use with Tab Controls Style Macro TCS_BUTTONS TCS_FIXEDWIDTH TCS_FOCUSNEVER TCS_FOCUSONBUTTONDOWN TCS_FORCEICONLEFT TCS_FORCELABELLEFT TCS_MULTILINE TCS_OWNERDRAWFIXED TCS_RAGGEDRIGHT TCS_RIGHTJUSTIFY TCS_SHAREIMAGELISTS TCS_SINGLELINE TCS_TABS Meaning Makes tabs appear as standard pushbuttons. Makes all tabs the same width. Specifies that a tab never receives the input focus. A tab will receive the input focus when clicked (typically used only with the TCS_BUTTONS style). Forces a tabs icon to the left but leaves the tab label centered. Left-aligns both the icon and label. A tab control displays multiple rows of tabs to ensure that all tabs can be displayed at once. The parent window draws the tabs for the control. A default style that doesnt force each row of tabs to fill the width of the control. Right-justifies tabs. A tab controls image lists arent destroyed with the control. This allows multiple controls to use the same image lists. Displays all tabs in a single row. The standard tab style; specifies that tabs appear as tabs (as opposed to buttons) and that a border is
99 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

TCS_TOOLTIPS

drawn around the display area. The tab control uses a ToolTip control.

Tab Control Notification Messages Like most Windows common controls, a tab control sends WM_NOTIFY notification 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.26 Notification Messages Used by the Tab Control Notification TCN_KEYDOWN TCN_SELCHANGE TCN_SELCHANGING Meaning A key was pressed. The currently selected tab has changed. The currently selected tab is about to change. By returning TRUE in 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 CTabCtrl class offers a complete set of methods for manipulating the control and its data. The CTabCtrl constructor, CTabCtrl::CTabCtrl(), allocates a CTabCtrl object that is initialized with the CTabCtrl::Create() method to set attributes and ownership. The CTabCtrl class methods are described in Table 3.27. Table 3.27 CTabCtrl Class Methods Method AdjustRect() Description Calculates a tab controls display area given a window rectangle; alternatively, calculates the window rectangle that corresponds to a given display area. Removes all items from a tab control. Removes an item from a tab control. Draws a specified tab control item. Gets the tab control tab that has the input focus. Gets the currently selected tab control tab. Gets the image list associated with a tab control.
100 / 1166

DeleteAllItems() DeleteItem() DrawItem() GetCurFocus() GetCurSel() GetImageList()

MFC Programming with Visual C++ 6 Unleashed

Summary

GetItem() GetItemCount() GetItemRect() GetRowCount() GetTooltips() HitTest() InsertItem() RemoveImage() SetCurSel() SetImageList() SetItem() SetItemSize() SetPadding() SetTooltips()

Gets information about a tab in a tab control. Gets the number of tabs in the tab control. Gets the bounding rectangle for a tab in a tab control. Gets the current number of rows of tabs in a tab control. Gets the handle of the ToolTip control associated with a tab control. Determines which tab (if any) is located at a specified screen position. Inserts a new tab into a tab control. Removes an image from a tab controls image list. Selects one of a tab controls tabs. Assigns an image list to a tab control. Sets some or all of a tabs attributes. Sets the width and height of an item. Sets the amount of padding around each tabs icon and label in a tab control. 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_ITEM structure has the following form:
typedef struct _TC_ITEM { UINT mask; UINT lpReserved1; UINT lpReserved2; LPSTR pszText; int cchTextMax; int iImage; LPARAM lParam; } TC_ITEM;

// reserved; do not use // reserved; do not use

The members of this structure are as follows: mask A 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. pszText A pointer to a string containing the tab text. cchTextMax The size of the buffer pointed to by the pszText member. iImage The index into the tab controls image list, or 1 if the tab has no image. lParam Application-defined data associated with the tab. Table 3.28 Tab Item Structure mask Values Value TCIF_IMAGE Description The iImage member is valid.
101 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

TCIF_PARAM TCIF_RTLREADING TCIF_TEXT

The lParam member is valid. Displays the text pointed to by pszText using right-toleft reading order when running on Hebrew or Arabic systems. The pszText member is valid.

Creating and Initializing a Tab Control To create a CTabCtrl object, you use the two-step construction process typical of MFC: 1. Call the class constructor CTabCtrl::CTabCtrl() to allocate the object. 2. Initialize the CTabCtrl object 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: dwStyle Specifies the combination of styles used by the control. rect Specifies a controls size and position. pParentWnd Specifies a controls parent window. nID Specifies 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 CTabCtrl object 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 CTabCtrl object, add tabs as needed by preparing a TC_ITEM structure and calling the CTabCtrl::InsertItem() method. Pass the TC_ITEM structure 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;
102 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

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 CString variable str uses the LPCTSTR operator to get a const pointer to the character string contained in the string object. The const is then cast away to the LPSTR expected 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 a class data member; this control is assumed to contain two tab items. Listing 3.3 A Simple Tab Notification Message Handler
/////////////////////////////////////////////////////////////////// // CMainWnd::OnClickTabCtrl() void { // Set the return code *pResult = 0; // Get the currently active tab int nCurTab = m_ctlTab.GetCurSel(); // Perform some action in response to the tab becoming active switch (nCurTab) { case 0: MessageBeep(MB_ICONASTERISK); AfxMessageBox(You activated Tab 1!, MB_OK | MB_ICONINFORMATION); break; case 1: MessageBeep(MB_ICONASTERISK); AfxMessageBox(You activated Tab 2!, MB_OK | MB_ICONINFORMATION); } } CMainWnd::OnClickTabCtrl(NMHDR* pNotifyStruct, LRESULT* pResult)

The corresponding handler in the classs message map looks something like this (where IDC_TABCTRL is assumed to be a valid control identifier for the tab control in question):
BEGIN_MESSAGE_MAP(CMainWnd, CMainFrame) // ...Other possible entries... ON_NOTIFY(NM_CLICK, IDC_TABCTRL, OnClickTabCtrl) END_MESSAGE_MAP()
103 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Animate Controls: Class CAnimateCtrl An animation control is a rectangular window that displays an animation in Audio Video Interleaved (AVI) format, which is the standard video for Windows file format. Viewed simplistically, an AVI file is composed of a series of animation frames; each frame is a bitmap image. Figure 3.9 shows three frames from the AVI clip SPINTORI.AVI, which shows a spinning red object (run the program TAB1.EXE on the companion CD-ROM to see this in action). Note: Nine bitmaps were used to create the SPINTORI.AVI file, and all were generated using the Persistence of Vision ray tracing tool kit (POVRAY). The POVRAY source files for the spinning tori animation are also included on the CD-ROM.

Figure 3.9 Three bitmaps from the SPINTORI.AVI animation file. Animation controls can play only simple AVI clips, and they dont support sound. In fact, the types of AVIs an animate control can play are quite limited, and must meet the following specifications: There must be only one video stream containing at least one frame. In addition to a single video stream, an AVI can also have a single audio stream, although audio is ignored by an animate control. The only type of data compression allowed for use with animate controls is Microsofts RLE8 compression. Uncompressed AVIs work well with the animate control, but can be quite large. A single palette must be used throughout the video stream. The animate control allows full multithreading in your applications, which makes the control useful for keeping users entertained during lengthy operationsor at least to assure them
104 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

that their system hasnt locked up. In this capacity, the animation acts as a reassuring dont worry, were still working on it element for the user. The animate control is a nice alternative to the progress bar control, especially when the remaining duration of an operation is unknown. MFC provides the services of a Windows animate common control in the class CAnimateCtrl, which is derived directly from CWnd and inherits all the functionality of CWnd. An animate control can be created as a child control of any window by writing code; it can also be defined in a dialog resource template. Animate Control Styles Like all windows, animate controls can use the general window styles available to CWnd. In addition, they can use the animate control styles listed in Table 3.29 (as defined in COMMCTRL.H). An animate controls styles determine its appearance and operations. Style bits are typically set when the control is initialized with the CAnimateCtrl::Create() method. Table 3.29 The Window Styles Specific to an Animate Control Style ACS_AUTOPLAY ACS_CENTER ACS_TRANSPARENT Description Tells the control to play an AVI clip when its opened and to automatically loop the video playback indefinitely. Centers the AVI clip in the control window. The background color specified in the AVI clip is drawn as transparent.

Note: If the ACS_CENTER style isnt specified, the animate control is resized to the size of the images in the video clip when the file is opened for reading.

Animate Control Notification Messages Like most Windows common controls, an animate control sends WM_NOTIFY notification messages to its parent window. These messages can be trapped and handled by writing message map entries and message-handler methods implemented in the animate controls owner class for each message. Table 3.30 shows the notification messages used by an animate control. Table 3.30 Message Map Entries for Animate Control Notification Messages Notification ACN_START Meaning An animation control has started playing an AVI clip.
105 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

ACN_STOP

An animation control has either finished playing or stopped playing an AVI clip.

CAnimateCtrl Class Methods The CAnimateCtrl class offers a minimal set of methods. These methods are described in Table 3.31. Table 3.31 CAnimate Class Methods Method Close Open Play Seek Stop Description Closes an open AVI clip. Opens an AVI clip from a file or resource and displays the first frame. Plays an AVI clip, leaving out any audio tracks that might be present (audio is ignored). Displays a selected single frame of an AVI clip. Stops playing an AVI clip.

Creating and Initializing an Animate Control To create a CAnimateCtrl object, you use the two-step construction process typical of MFC: 1. Call the class constructor CAnimateCtrl::CAnimateCtrl() to allocate the object. 2. Initialize the CAnimateCtrl object and attach an actual Windows animate common control to it with a call to the CAnimateCtrl::Create() method. The prototype for the CAnimateCtrl::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: dwStyle Specifies the combination of styles used by a control. rect Specifies a controls size and position. pParentWnd Specifies the controls parent window. nID Specifies the control identifier for a control.

Using an Animate Control After the CAnimateCtrl object is constructed, you open and play an AVI clip by performing the following steps: Open the AVI clip by calling the CAnimateCtrl::Open() method. Play the AVI by calling the CAnimateCtrl::Play() method. For example, the TAB1 program plays an AVI clip like this, where m_ctlAnim is a CAnimateCtrl object:
106 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

// Open and play the AVI if (m_ctlAnim.Open((LPCTSTR)spintori.avi)) { m_ctlAnim.ShowWindow(SW_SHOWNORMAL); m_ctlAnim.Play(0, (UINT)-1, (UINT)-1); }

The following section looks at the big brother of the hearty edit control: the rich edit control. Rich Edit Controls: Class CRichEditCtrl A rich edit control is a window that, at first glance, looks very similar to a standard edit control. But a rich edit control has the additional benefits of letting users perform character and paragraph formatting, as well as embed OLE objects. Although the rich edit control provides the functionality, your application must implement the user interface components that allow users to perform formatting operations on the text. The MFC classes CRichEditDoc, CRichEditView, and CRichEditCntrItem encapsulate the functionality of a rich edit control into full-featured classes for use in your document/view programs. Figure 3.10 shows the WordPad application that ships with Windows 95. Note that this application is mainly just a rich edit control with some GUI trappings (toolbar, status bar, and so on) wrapped in a nice document/view framework. A rich edit control provides support for changing the character attributes of selected text, such as whether a character is displayed as bold, italicized, or with a certain font family and point size. A rich edit control also provides support for setting paragraph attributes, such as justification, margin size, and tab-stop values. Note: Although the rich edit control provides the means for the formatting, your application must provide the user interface controls that users manipulate to actually format the text.

107 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Figure 3.10 A typical rich edit control makes the WordPad application possible. MFC provides the services of a Windows rich edit common control in the class CRichEditCtrl, which is derived directly from CWnd and inherits all the functionality of CWnd. Although the CRichEditCtrl class isnt derived from CEdit, a CRichEditCtrl object supports most of the operations and notification messages used for multiple-line edit controls. In fact, the default style for a rich edit control is single line, just as it is for an edit control; you must set the ES_MULTILINE window style to create a multiple-line rich edit control. This style makes it easy for applications that already make use of edit controls to be easily modified to use rich edit controls. Figure 3.11 shows the TAB1 program displaying rich text on the second tab.

108 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Figure 3.11 Displaying rich text in the TAB1 program. Note: Because the rich edit control is specific to Win32, the CRichEditCtrl class is available only to programs running under Windows 95, Windows NT version 3.51 or later, and Windows 3.1x with Win32s 1.3 or later. A rich edit 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 rich edit 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 rich edit controls parent class.

Rich Edit Control Window Styles Like all windows, rich edit controls can use the general window styles available to CWnd. Unlike other controls, the rich edit control defines no additional window styles. The Character Format Structure (CHARFORMAT) The CRichEditCtrl classs GetDefaultCharFormat(), GetSelectionCharFormat(), SetDefaultCharFormat(), SetSelectionCharFormat(), and SetWordCharFormat() methods all take a parameter of type CHARFORMAT. This is a Win32 datatype (see Listing 3.4)
109 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

that contains information about character formatting in a rich edit control. Listing 3.4 The CHARFORMAT Structure
typedef struct _charformat { UINT cbSize; _WPAD _wPad1; DWORD dwMask; DWORD dwEffects; LONG yHeight; LONG yOffset; COLORREF crTextColor; BYTE bCharSet; BYTE bPitchAndFamily; TCHAR szFaceName[LF_FACESIZE]; _WPAD _wPad2; } CHARFORMAT;

The data members for this structure are as follows: cbSize Size in bytes of this structure. Must be set before passing the structure to the rich edit control. dwMask Members containing valid information or attributes to set. This member can be zero or one or more of the values listed in Table 3.32. dwEffects Character effects. This member can be a combination of the values listed in Table 3.33. yHeight Character height. yOffset Character offset from the baseline. If this member is positive, the character is a superscript; if it is negative, the character is a subscript. crTextColor Text color. This member is ignored if the CFE_AUTOCOLOR character effect is specified. bCharSet Character set value. Can be one of the values specified for the lfCharSet member of the LOGFONT structure. bPitchAndFamily Font family and pitch. This member is the same as the lfPitchAndFamily member of the LOGFONT structure. szFaceName NULL-terminated character array specifying the font face name. Table 3.32 Possible CHARFORMAT dwMask Values Value CFM_BOLD CFM_COLOR CFM_FACE CFM_ITALIC CFM_OFFSET CFM_PROTECTED Meaning The CFE_BOLD value of the dwEffects member is valid. The crTextColor member and the CFE_AUTOCOLOR value of the dwEffects member are valid. The szFaceName member is valid. The CFE_ITALIC value of the dwEffects member is valid. The yOffset member is valid. The CFE_PROTECTED value of the dwEffects member is valid.
110 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

CFM_SIZE CFM_STRIKEOUT CFM_UNDERLINE.

The yHeight member is valid. The CFE_STRIKEOUT value of the dwEffects member is valid. The CFE_UNDERLINE value of the dwEffects member is valid.

Table 3.33 Possible Rich Edit Control Character Effects Values Value CFE_AUTOCOLOR CFE_BOLD CFE_ITALIC CFE_STRIKEOUT CFE_UNDERLINE CFE_PROTECTED Meaning The text color is the return value of GetSysColor (COLOR_WINDOWTEXT). Characters are bold. Characters are italic. Characters are struck out. Characters are underlined. Characters are protected; an attempt to modify them causes an EN_PROTECTED notification message.

The Paragraph Format Structure (PARAFORMAT) Another Win32 structure, called PARAFORMAT, is used as a parameter with the GetParaFormat() and SetParaFormat() methods. The PARAFORMAT structure contains information about formatting attributes in a rich edit control and is shown in Listing 3.5. Listing 3.5 The PARAFORMAT Structure
typedef struct _paraformat { UINT cbSize; _WPAD _wPad1; DWORD dwMask; WORD wNumbering; WORD wReserved; LONG dxStartIndent; LONG dxRightIndent; LONG dxOffset; WORD wAlignment; SHORT cTabCount; LONG rgxTabs[MAX_TAB_STOPS]; } PARAFORMAT;

The data members for this structure are as follows: cbSize Size in bytes of this structure. Must be filled before passing to the rich edit
111 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

control. dwMask Members containing valid information or attributes to set. This parameter can be zero or one or more of the values shown in Table 3.34. wNumbering Value specifying numbering options. This member can be zero or PFN_BULLET. dxStartIndent Indentation of the first line in the paragraph. If the paragraph formatting is being set and PFM_OFFSETINDENT is specified, this member is treated as a relative value that is added to the starting indentation of each affected paragraph. dxRightIndent Size of the right indentation, relative to the right margin. dxOffset Indentation of the second and subsequent lines, relative to the starting indentation. The first line is indented if this member is negative; it is outdented if this member is positive. wAlignment Value specifying the paragraph alignment. This member can be one of the values listed in Table 3.35. cTabCount Number of tab stops. rgxTabs Array of absolute tab-stop positions. Table 3.34 Rich Edit Control Paragraph dwMask Values Value PFM_ALIGNMENT PFM_NUMBERING PFM_OFFSET PFM_OFFSETINDENT PFM_RIGHTINDENT PFM_STARTINDENT PFM_TABSTOPS Meaning The wAlignment member is valid. The wNumbering member is valid. The dxOffset member is valid. The dxStartIndent member is valid and specifies a relative value. The dxRightIndent member is valid. The dxStartIndent member is valid. The cTabStobs and rgxTabStops members are valid.

Table 3.35 Possible Rich Edit Control Paragraph Alignment Values Value PFA_LEFT PFA_RIGHT PFA_CENTER Meaning Paragraphs are aligned with the left margin. Paragraphs are aligned with the right margin. Paragraphs are centered.

CRichEditCtrl Class Methods The CRichEditCtrl class offers a full set of methods for manipulating the control and its data. The CRichEditCtrl class is complex, and its methods can be broken out into several categories: Line-related methods Text-selection methods Text-formatting methods Editing methods Clipboard methods
112 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

General-purpose methods The following sections look at descriptions for all these CRichEditCtrl methods. CRichEditCtrl Line-Related Methods The CRichEditCtrl methods related to manipulating lines of text are described in Table 3.36. Table 3.36 CRichEditCtrl Line-Related Methods Method GetLineCount() GetLine() GetFirstVisibleLine() LineIndex() LineFromChar() LineLength() LineScroll() Description Retrieves the number of lines in this CRichEditCtrl object. Retrieves a line of text from this CRichEditCtrl object. Determines the topmost visible line in this CRichEditCtrl object. Retrieves the character index of a given line in this CRichEditCtrl object. Determines which line contains the given character. Retrieves the length of a given line in this CRichEditCtrl object. Scrolls the text in this CRichEditCtrl object.

CRichEditCtrl Text-Selection Methods The CRichEditCtrl methods related to selecting text, clearing or getting selected text, and so on, are described in Table 3.37. Table 3.37 CRichEditCtrl Text-Selection Methods Method Clear() GetSel() GetSelectionType() GetSelText() HideSelection() ReplaceSel() SetSel() Description Clears the current selection. Gets the starting and ending positions of the current selection. Retrieves the type of contents in the current selection. Gets the text of the current selection. Shows or hides the current selection. Replaces the current selection with specified text. Sets the selection.

CRichEditCtrl Formatting Methods


113 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

The CRichEditCtrl methods related to formatting text are described in Table 3.38. Table 3.38 CRichEditCtrl Formatting Methods Method GetDefaultCharFormat() GetParaFormat() GetSelectionCharFormat() SetDefaultCharFormat() SetParaFormat() SetSelectionCharFormat() SetWordCharFormat() Description Gets the current default character formatting attributes. Gets the paragraph formatting attributes in the current selection. Gets the character formatting attributes in the current selection. Sets the current default character formatting attributes. the paragraph formatting attributes in the current selection. Sets the character formatting attributes in the current selection. Sets the character formatting attributes in the current word.

CRichEditCtrl Editing Methods The CRichEditCtrl methods related to editing text are described in Table 3.39. Table 3.39 CRichEditCtrl Editing Methods Method CanUndo() EmptyUndoBuffer() StreamIn() StreamOut() Undo() Description Determines whether an editing operation can be undone. Resets a CRichEditCtrl objects undo flag. Inserts text from an input stream. Stores text from a CRichEditCtrl object in an output stream. Undoes the last editing operation.

CRichEditCtrl Clipboard Methods The CRichEditCtrl methods that allow a rich edit control to interact with the Windows Clipboard are described in Table 3.40. Table 3.40 CRichEditCtrl Clipboard Methods Method CanPaste() Description Checks to see whether the contents of the Clipboard can be pasted into a rich edit control.
114 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Copy() Cut() Paste() PasteSpecial()

Copies the current selection to the Clipboard. Cuts the current selection to the Clipboard. Inserts the contents of the Clipboard into a rich edit control. Inserts the contents of the Clipboard into a rich edit control using the specified data format.

CRichEditCtrl General-Purpose Methods General-purpose CRichEditCtrl methods are described in Table 3.41. Table 3.41 General-Purpose Methods Method DisplayBand() FindText() FormatRange() GetCharPos() Description

Displays a portion of the contents of a CRichEditCtrl object. Locates text within a CRichEditCtrl object. Formats a range of text for the target output device. Gets the location of a given character within this CRichEditCtrl object. GetEventMask() Gets the event mask for a CRichEditCtrl object. GetLimitText() Gets the limit on the amount of text a user can enter into a CRichEditCtrl object. GetModify() Determines whether the contents of a CRichEditCtrl object have been modified since last saved. GetRect() Gets the formatting rectangle for a CRichEditCtrl object. GetTextLength() Gets the length of the text in a CRichEditCtrl object. LimitText() Limits the amount of text a user can enter into the CRichEditCtrl object. RequestResize() Forces a CRichEditCtrl object to send notifications to its parent window requesting that it be resized. SetBackgroundColor() Sets the background color in a CRichEditCtrl object. SetEventMask() Sets the event mask for a CRichEditCtrl object. SetModify() Sets or clears the modification flag for a CRichEditCtrl object. SetOptions() Sets the options for a CRichEditCtrl object. SetReadOnly() Sets the read-only option for a CRichEditCtrl object. SetRect() Sets the formatting rectangle for a CRichEditCtrl object. SetTargetDevice() Sets the target output device for a CRichEditCtrl object.

Creating and Initializing a Rich Edit Control To create a CRichEditCtrl object, you use the two-step construction process typical of MFC:
115 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

1. Call the class constructor CRichEditCtrl::CRichEditCtrl() to allocate the object. 2. Initialize the CRichEditCtrl object and attach an actual Windows rich edit common control to it with a call to the CRichEditCtrl::Create() method. The prototype for the CRichEditCtrl::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: dwStyle Specifies the combination of styles used by a control. rect Specifies a controls size and position. pParentWnd Specifies the controls parent window. nID Specifies the control identifier for a control.

Using a Rich Edit Control After constructing the CRichEditCtrl object, you add text and format it as desired by doing the following: 1. Prepare PARAFORMAT and CHARFORMAT structures to define paragraph and character formatting specifics. 2. Call on the CRichEditCtrl methods that use these structures to perform their magic. For example, the sample program TAB1 (located on the companion CD-ROM) uses the code shown in Listing 3.6 to set the character formatting specifics for a given CHARFORMAT structure. Listing 3.6 Initializing a CHARFORMAT Structure
///////////////////////////////////////////////////////////////////// // CMainWnd::SetStyleHeading1() void CMainWnd::SetStyleHeading1(CHARFORMAT& cf) { cf.cbSize = sizeof(CHARFORMAT); cf.dwMask = CFM_COLOR | CFM_FACE | CFM_SIZE | CFM_ITALIC | CFM_BOLD; cf.dwEffects = CFE_BOLD | CFE_ITALIC; cf.yHeight = 500; cf.crTextColor = crRed; // from colors.h cf.bCharSet = ANSI_CHARSET; cf.bPitchAndFamily = FF_ROMAN; lstrcpy(cf.szFaceName, Times New Roman); }

As you can see, you can easily control the characteristics of a display font with the CHARFORMAT structure. With a little extra work you can make various formatting changes an interactive experience for the usertry creating various functions and attaching them to user-interface elements such as buttons on a toolbar. Summary
116 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

In this chapter youve examined several MFC classes that encapsulate the Win32 common controls. Youve explored the messages and methods of these classes, and seen how to use them in an application context. Next, youll take a closer look at graphics programming in MFC.

117 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Chapter 4 Painting, Device Contexts, Bitmaps, and Fonts by Rob McGregor In This Chapter Device Contexts 148 The Graphics Device Interface 149 MFC Device Context Classes 150 Windows Graphic Objects 158 GDI Coordinate Systems 165 Vector Graphics 167 Fonts and Text 184 Sample Program: Vector Graphics and Text Methods (VECTEXT1.EXE) 194 Raster Graphics 196 Bitmap Resources 203 Sample Program: Exploring Bitmap Resources (BITMAP1) 206

Graphics are extremely important in todays GUI OS world, and Windows is certainly no exception. Because Windows provides a graphical user interface (GUI) to the underlying operating system (OS) and hardware, graphics are the mainstay of Windows programs. This chapter explains how MFC packages and exposes device contexts and graphics objects. This chapter also introduces the basic concepts of creating, drawing, and using graphics in Windows. Note: Four sample programs (VECTEXT1, RASTER1, BEZIER1, and BITMAP1) included on this books accompanying CD-ROM demonstrate all of the techniques described in this chapter, and more, with fully commented source code. Everythingeven textpaints to the screen as graphics under Windows. The logical representation of a physical devices drawing surface is packaged into a complex data structure called a device context (DC). Windows provides several special datatypes and structures to represent and describe each of the fundamental Windows graphic objects (including pens, brushes, fonts, and bitmaps). Device Contexts When a Windows program (including Windows itself) draws text and graphics to a display or some other device (such as a printer), it usually doesnt draw directly to the hardware as DOS programs do. In fact, applications that write directly to hardware are considered taboo in the world of Windows. Applications use a device context (DC) to represent a logical version of the physical device, be it a monitor, a printer, a plotter, or some other physical device. A DC contains information about the pen, brush, font, and bitmap currently selected for use on a device. MFC provides classes for several different types of DCs, and an application must explicitly ask for a DC before drawing anything to a device, even simply writing some text on the video display. Device contexts arent limited to physical devices, however; DCs can refer to logical devices as well. An example of a logical device is a metafile, which is a collection of
118 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

structures that stores a picture in a device-independent format. Another example is a bitmap, a collection of pixels that represents some graphic image. You can draw on a bitmap or a metafile as easily as you can draw on a display or a printer. Four types of device contexts are supplied by the Win32 API: Display contextsSupport graphics operations on a video display. Information contextsProvide for the retrieval of device data. Memory contextsSupport graphics operations on a bitmap. Printer contextsSupport graphics operations on a printer or plotter.

The Graphics Device Interface Device contexts are used extensively by the Graphics Device Interface (GDI), a main component of the Windows architecture. Non-MFC Windows programs written in Standard C/C++ use a device context to give Windows the specifics of the device it should draw on. This device context is sent as a parameter to any of several GDI function calls provided by the Windows API. The GDI provides all the basic drawing function-ality for Windows; the DC represents the device, providing a layer of abstraction that insulates your applications from the nastiness of drawing directly to hardware. (Figure 4.1 shows this hardware abstraction.) The GDI provides this insulation by calling the appropriate device driver in response to Windows graphics function calls. This abstraction frees you from having to write low-level driver code to support each device youll draw toWindows includes drivers for and already knows how to draw hundreds of devices.

Figure 4.1 The layers of insulation between the hardware and an MFC application. MFC Wrapping When writing MFC programs, you get an added benefit when using DCs and the GDI functions: The GDI functions are built right into the MFC DC classes as DC methods. This makes using the GDI functions very convenient, especially with the Intellisense pop-up lists provided by Microsoft Visual C++ 6.0 (see Figure 4.2).

119 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Figure 4.2 Visual C++ Intellisense pop-up lists make using device context methods easier than ever. MFC Device Context Classes MFC encapsulates the various types of device contexts provided by Windows into distinct DC classes that wrap a handle to a device context (HDC) within a C++ class. The class effectively contains information about the drawing attributes of a device. All drawing done in Windows is done on a device context, and all drawing methods are nicely wrapped into a DC object. The following DC classes are the predefined MFC base classes for device contexts; they provide drawing and painting capabilities to MFC applications: CDC CPaintDC CClientDC CWindowDC CMetaFileDC

Figure 4.3 shows the relation of these classes in the MFC class hierarchy.

Figure 4.3 The hierarchy of MFC device context classes.


120 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

The Base Class: CDC As you can see in Figure 4.3, the CDC class is the base class for the other DC classes. The CDC base class defines device context objects and provides methods for drawing on a display, a printer, or a windows client area. All graphic output should be rendered using the class methods provided by CDC. These class methods provide services for using drawing tools, manipulating device contexts, type-safe GDI object selection, manipulating color and palettes, coordinate mapping and conversion, working with polygons and regions, drawing shapes, drawing text, working with fonts, handling metafiles, and more. The CDC class is a monster that encapsulates the GDI functions that use device contexts. It might surprise you to know that a CDC object actually contains not one, but two device contexts. These DCs are stored as the class members described in Table 4.1; they allow a CDC object to point to two different devices simultaneously. It is this very useful property of an extra DC within a CDC object that makes advanced MFC features such as print preview so easy to achieve.

Table 4.1 Class Members of a CDC Object Class Member m_hDC m_hAttribDC Description The output device context for a CDC object. Most CDC GDI calls that create output go to this member DC. The attribute device context for this CDC object. Most CDC GDI calls requesting information from a CDC object are directed to this member DC.

Note: The two class member DCs initially refer to the same device, but they can be manipulated to refer to different devices at any time. Setting and releasing these DCs is accomplished through the CDC methods SetAttribDC(), SetOutputDC(), ReleaseAttribDC(), and ReleaseOutputDC(). The CDC::SetAttribDC() method sets m_hAttribDC (the attribute device context); SetOutputDC sets m_hDC (the output device context). Likewise, the CDC::ReleaseAttribDC() method releases m_hAttribDC, and the CDC::ReleaseOutputDC() method releases m_hDC. The CDC methods are broken into several categories of functionality; these categories are reflected in other MFC device context classes, as described later in this chapter. The main categories of functionality are listed in Table 4.2. Table 4.2 Functional Categories of CDC Methods Category Purpose
121 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Bitmap methods Clipping methods Color and color palette methods Coordinate methods Device context methods Drawing attribute methods Drawing tool methods

Methods for manipulating bitmaps and pixels. Functions that define and manipulate clipping bounds for a device. Methods for dealing with palette selection and realization and mapping logical colors. Methods for converting between device and logical units. Methods that retrieve information about a DC and its attributes. Methods for getting and setting colors and modes for a DC. Methods for manipulating brush origins and enumerating the pens and brushes available in a DC. Methods for drawing ellipses and polygons.

Ellipse and polygon methods Font methods Methods for retrieving font attributes. Initialization methods Methods for creating and setting DC properties and retrieving information about the graphic objects within the DC. Line output methods Methods for drawing lines on a DC. Mapping methods Methods to set, retrieve, and manipulate origins and extents for windows and viewports, and to get and set mapping modes for a DC. Metafile methods Methods to record and play metafiles. Path methods Methods for manipulating paths in a DC. (Paths are created using GDI functions that generate curves and polygons.) Printer escape Methods that access and manipulate printers and print jobs. methods Region methods Methods for filling regions and manipulating region colors. Simple drawing Methods that provide simple rectangle and icon drawing features. methods Text methods Methods that output text and retrieve information about the font currently selected for the DC. Type-safe selection Methods for selecting graphic objects or Windows stock objects. methods The CDC class is very large, containing over 170 methods and data members. The following sections look at the basics of creating and using a DC. Painting with Class CPaintDC A CPaintDC object represents a drawing surface for a window. To fully understand the purpose of CPaintDC, lets go back to the API level and see how things are done in SDKland. Traditional C programs, as described in the Microsoft Win32 Software Development Kit (SDK), get a device context by using the Win32 API functions BeginPaint() and EndPaint() within the message handler for the Windows message WM_PAINT. The BeginPaint() function prepares a specified window for painting, fills a special PAINTSTRUCT structure with painting information, and returns a handle to a display device context (or painting DC). The EndPaint() function is always paired with a call to BeginPaint() to signify the end of painting in the specified window. The C code looks like this within the window procedure:
122 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

HDC PAINTSTRUCT

hDC; paintstruct;

switch (msg) case WM_PAINT: { hDC = BeginPaint(hwnd, &paintstruct); // // Perform graphics operations using hDC // EndPaint(hwnd, &paintstruct); return 0; }

A CPaintDC object performs these same stepsit just wraps them in an MFC class, and this makes your job considerably easier. The basic steps to follow when using a CPaintDC object are given here: 1. Create a CPaintDC object. 2. Draw on the CPaintDC object. 3. Destroy the CPaintDC object. The CPaintDC constructor automatically calls BeginPaint() and returns the paint DC to use for painting. Likewise, the destructor automatically calls EndPaint(). If you use the automatic storage class for your DC objects, you can forget about step 3 from the preceding list, making your job easier still. Note: MFC removes much of the tedium involved in using CPaintDC by providing View classes that automate the process. A View class receives a CPaintDC object as a parameter of the CView::OnDraw() method. This CPaintDC is used for graphics methods and is destroyed by MFC when OnDraw() returns (the device contexts wrapped within the CPaintDC are released to Windows at this time). Listing 4.1 gives a simple example of using a CPaintDC to draw graphics in a windowin this case, an ellipse. The result is shown in Figure 4.4. The window receives WM_PAINT messages from the MFC-provided window procedure in the windows OnPaint() message handler. This member function must be added to the windows message map to enable WM_PAINT message reception, like this:
// Message map for CMainWnd BEGIN_MESSAGE_MAP(CMainWnd, CFrameWnd) ON_WM_ PAINT() END_MESSAGE_MAP()

Listing 4.1 Using a CPaintDC Object to Draw an Ellipse in Response to WM_PAINT Messages in the Windows OnPaint() Message Handler
/////////////////////////////////////////////////////////////////// // CMainWnd::OnPaint()
123 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

void CMainWnd::OnPaint() { // Create a paint DC CPaintDC dc(this); // Draw an ellipse on the DC CRect rc; GetClientRect(&rc); dc.Ellipse(rc); }

Figure 4.4 The ellipse resulting from the call to a CPaintDC objects Ellipse() method. Managing Client Areas with Class CClientDC MFC supplies the CClientDC class (which is directly derived from CDC) to automate the process of calling and releasing a device context representing the client area of a window. The Win32 API function GetDC() is called in the CClientDC constructor; the corresponding API function ReleaseDC() is called in the destructor. Listing 4.2 gives a simple example of using a CClientDC to draw graphics in a windowin this case, a diamond (see Figure 4.5).

124 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Figure 4.5 The diamond resulting from four successive calls to a CClientDC objects CDC::LineTo() method. Listing 4.2 Using a CClientDC Object to Draw a Diamond in Response to WM_LMBUTTONDOWN Messages in the Windows OnLButtonDown() Message Handler
/////////////////////////////////////////////////////////////////// // CMainWnd::OnLButtonDown() void CMainWnd::OnLButtonDown(UINT nFlags, CPoint point) { // Create a client DC to draw on CClientDC dc(this); // Draw a diamond on the DC CRect rc; GetClientRect(&rc); dc.MoveTo(0, (rc.bottom + rc.top) / 2); dc.LineTo((rc.right + rc.left) / 2, 0); dc.LineTo(rc.right, (rc.bottom + rc.top) / 2); dc.LineTo((rc.right + rc.left) / 2, rc.bottom); dc.LineTo(0, (rc.bottom + rc.top) / 2); }

Managing Frame Windows with Class CWindowDC MFC supplies the CWindowDC class (which is directly derived from CDC) to automate the process of calling and releasing a device context representing the entire surface of a window, including both the client and nonclient areas. The Win32 API function GetDC() is called in the CWindowDC constructor; the corresponding API function ReleaseDC() is called in the destructor. Listing 4.3 gives a simple example of using a CWindowDC to draw graphics in a window in this case, a series of ellipses in the title bar (see Figure 4.6). The ellipses clip
125 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

everything in their path as they draw, obliterating all nonclient area components (System menu, window caption, minimize, maximize, and close buttons).

Figure 4.6 The series of circles across the main nonclient area results from successive calls to a CWindowDC objects Ellipse() method. Listing 4.3 Using a CWindowDC Object to Draw a Series of Circles in Response to WM_RMBUTTONDOWN Messages in the Windows OnRButtonDown() Message Handler
/////////////////////////////////////////////////////////////////// // CMainWnd::OnRButtonDown() void CMainWnd::OnRButtonDown(UINT nFlags, CPoint point) { // Create a window DC to draw on CWindowDC dc(this); // Draw a series of ellipses on the DC, // in the windows NC title bar area CRect rc; GetWindowRect(&rc); // Get the title bar height int cyCaption = GetSystemMetrics(SM_CYCAPTION); // Define the ellipses bounding rect CRect rcEllipse(0, 0, cyCaption, cyCaption); // Draw the ellipses while (rcEllipse.right < rc.right) { dc.Ellipse(rcEllipse); rcEllipse.left += cyCaption; rcEllipse.right += cyCaption; } }
126 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Windows Graphic Objects The display device contexts provided by Windows define logical display surfaces; the GDI provides drawing tools used to draw on the DC. MFC defines several types of graphic objects that correspond to the Windows drawing tools, including the following: Pens Brushes Fonts Bitmaps Palettes Regions

These Windows drawing tools are encapsulated by MFC graphic object classes. These classes are all derived from a common base class called CGdiObject (see Figure 4.7).

Figure 4.7 The location of graphic objects in the MFC class hierarchy. The relation of the standard SDK drawing tool datatypes to the MFC graphic object classes is shown in Table 4.3. Table 4.3 The Relation of the Standard SDK Drawing Tool Datatypes to MFC Graphic Object Classes SDK Drawing Tool Pen Brush Font Bitmap Palette Region MFC Class CPen CBrush CFont CBitmap CPalette CRgn Windows Datatype HPEN HBRUSH HFONT HBITMAP HPALETTE HRGN

To use a graphic object in an MFC program, youll usually follow these three steps: 1. Define a graphic object within a code block and initialize the object with a corresponding Create*() method. For example, to create a CPalette object, use CreatePalette(). 2. Select the new object into the current device context, typically by using the
127 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

CDC::SelectObject() method. This method returns a pointer to the object being replaced (a pointer to a CGdiObject-derived object). 3. When the graphic object has finished its task, use the CDC::SelectObject() method again to select the replaced graphic object back into the device context, leaving things as they were originally. Note: A graphic object declared on the stack is automatically deleted when the object goes out of scope. Graphic objects declared as pointers and allocated with the new operator must be explicitly deleted after restoring the DCs previous state. The following sections look at each of these tools and how theyre used. Note: Class CGdiObject provides the interface to the raw Win32 API in terms of graphics object handles and so on. You never create a CGdiObject object directly; you create an object from one of its derived classes (for example, CPen or CBrush). Pens: Class CPen A CPen object encapsulates a Windows GDI pen and provides several methods for working with CPen objects (see Table 4.4). Table 4.4 Methods for Creating and Using CPen Objects Method CreatePen() CreatePenIndirect() FromHandle() GetExtLogPen() GetLogPen() operator HPEN Description Creates a logical pen with the specified style, width, and brush attributes and attaches it to a CPen object. Creates a pen with the style, width, and color defined in a LOGPEN structure and attaches it to a CPen object. Returns a pointer to a CPen object from a Windows HPEN. Gets a pens underlying EXTLOGPEN structure. Gets a pens underlying LOGPEN structure. Returns the handle of the Windows pen currently attached to the CPen object.

You create a pen to use when drawing in a device context. Pens come in several styles and a rainbow of colors. A Windows pen is typically created and attached to a CPen object with the CPen::CreatePen() method, which uses this prototype:
BOOL CreatePen(int nPenStyle, int nWidth, COLORREF crColor);

The nPenStyle parameter can be any of the values shown in Table 4.5; the nWidth parameter is the width of the pen; the crColor parameter specifies the color of the pen. Table 4.5 These Pen Styles Defined by the GDI Are Used for CPen Object Classes
128 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Pen Style PS_SOLID PS_DASH PS_DOT PS_DASHDOT PS_DASHDOTDOT PS_NULL PS_INSIDEFRAME

Description Creates a solid pen. When the pen width is 1, creates a dashed pen. When the pen width is 1, creates a dotted pen. When the pen width is 1, creates a pen with alternating dashes and dots. When the pen width is 1, creates a pen with alternating dashes and double dots. Creates a NULL (invisible) pen. Creates a pen that draws inside the bounding rectangle of a Windows GDI shape such as an ellipse or a rectangle.

For example, to create and use a red, dashed pen, you would do this:
// Create a new red-dashed pen CPen penRed; penRed.CreatePen(PS_DASH, 1, RGB(255, 0, 0));

To select it into a device context, you would use the SelectObject() method of the DC, like this:
// Select the new pen into the device context, and save // the old pen to restore on clean up... CPen* ppenOld; ppenOld = dc.SelectObject(&penRed);

Now any lines drawn in the window will have the red dashed style you just put into the DC. When youre done drawing, restore the original pen by selecting it back into the DC, like this:
dc.SelectObject(ppenOld);

Brushes: Class CBrush A CBrush object encapsulates a Windows GDI brush and provides several methods for working with CBrush objects (see Table 4.6). Table 4.6 The Methods Provided by MFC for CBrush Objects Method CreateBrushIndirect() Description

Creates a brush with the style, color, and pattern specified in a LOGBRUSH structure and attaches it to a CBrush object. CreateDIBPatternBrush() Creates a brush with a pattern specified by a deviceindependent bitmap (DIB) and attaches it to a CBrush object. CreateHatchBrush() Creates a brush with the specified hatched pattern and color and attaches it to a CBrush object. CreatePatternBrush() Creates a brush with a pattern specified by a bitmap and attaches it to a CBrush object. CreateSolidBrush() Creates a brush with the specified solid color and attaches it to
129 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

a CBrush object. CreateSysColorBrush() Creates a brush that is the default system color and attaches it to a CBrush object. FromHandle() Returns a pointer to a CBrush object from a Windows HBRUSH object. GetLogBrush() Gets the underlying LOGBRUSH structure from a CBrush object. operator HBRUSH Returns the Windows handle attached to a CBrush object.

You create a brush to use when painting in a device context. Like pens, brushes come in several styles and a rainbow of colors. A Windows brush is most conveniently created and attached to a CBrush object with the constructor, CBrush::CBrush(), which uses these overloaded prototypes:
CBrush(); CBrush(COLORREF crColor); CBrush(int nIndex, COLORREF crColor); CBrush(CBitmap* pBitmap);

The crColor parameter specifies the color of the brush. The nIndex parameter specifies the pattern of the brush, which can be any of the values shown in Table 4.7. The pBitmap parameter is a pointer to a CBitmap object that contains a bitmap to use as a brush pattern. Table 4.7 Hatch Styles Defined for GDI Brushes Are Used for CBrush Objects Brush Style HS_BDIAGONAL HS_CROSS HS_DIAGCROSS HS_FDIAGONAL HS_HORIZONTAL HS_VERTICAL Description Downward hatch (left to right) at 45 degrees Horizontal and vertical crosshatch Crosshatch at 45 degrees Upward hatch (left to right) at 45 degrees Horizontal hatch Vertical hatch

Fonts: Class CFont A CFont object encapsulates a Windows GDI font and provides several methods for working with CFont objects (see Table 4.8). Table 4.8 CFont Class Methods for Creating and Manipulating Fonts Method CreateFontIndirect() Description Creates a CFont object with the characteristics given in a LOGFONT structure.
130 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

CreateFont() CreatePointFont()

Creates a CFont object with the specified characteristics. Creates a CFont object with the specified height (measured in tenths of a point) and typeface. CreatePointFontIndirect() Creates a CFont object with the characteristics given in a LOGFONT structure; the font height is measured in tenths of a point instead of logical units. FromHandle() Returns a pointer to a CFont object from a Windows HFONT. operator HFONT() Returns the underlying Windows GDI font handle attached to a CFont object. GetLogFont() Fills a LOGFONT with information about the logical font attached to a CFont object. You create a font to use when drawing text in a device context. Fonts come in thousands of typefaces and styles. A Windows GDI font is typically created and attached to a CFont object with one of the first four methods listed in Table 4.8. Bitmaps: Class CBitmap A bitmap is an array of pixels that defines a graphic imagea picture or a pattern. The colors of the pixels are described by the data in the bitmap bits. The MFC class CBitmap encapsulates a handle to a Windows bitmap and provides some 14 methods and operators to create and manipulate bitmap objects. Palettes: Class CPalette A palette is a Windows GDI object that stores color information. A GDI palette object is basically a color lookup table used by the system to determine which colors to display on a 256-color palette display device. Windows defines 20 palette colors for the system, leaving 236 for applications to use for their own purposes. MFC encapsulates a Windows color palette within the CPalette class. Windows uses both logical palettes (which are lists of desired colors) and the system palette (which defines color slots currently available in the hardware palette). The CPalette class provides nine methods and one operator for creating and manipulating CPalette objects. Note: The terms hardware palette and system palette are used interchangeably even though, technically, the system palette is really a copy of the values found in the hardware palette. The Windows Palette Manager uses the system palette values. Regions: Class CRgn A region is a Windows GDI object that stores polygonal or elliptical information about regions on a display device. The CRgn class encapsulates a GDI region object and provides several methods for creating and working with regions. These methods are described in Table 4.9. Table 4.9 Class CRgn Methods for Creating and Using Regions Method Description
131 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

CombineRgn() Creates a union of two specified CRgn objects. CopyRgn() Copies one CRgn object into another. CreateEllipticRgn() Creates a CRgn object with an elliptical region. CreateEllipticRgnIndirect() Creates a CRgn object with an elliptical region defined by a RECT structure as its bounding box. CreateFromData() Creates a region based on a given region and geometrical transformation data. CreateFromPath() Creates a region based on a path thats selected into a given device context. CreatePolygonRgn() Creates a CRgn object with a polygonal region. Polygon regions are automatically closed, if necessary, by connecting the last vertex of the polygon to the first. CreatePolyPolygonRgn() Creates a CRgn object with a region made up of a series of closed polygons. CreateRectRgn() Creates a CRgn object with a rectangular region. CreateRectRgnIndirect() Creates a CRgn object with a rectangular region defined by a RECT structure. CreateRoundRectRgn() Creates a CRgn object with a rounded corner rectangular region. EqualRgn() Creates two CRgn objects to determine whether they are equivalent. FromHandle() Returns a pointer to a CRgn object from a handle to a Windows region. GetRegionData() Fills a buffer with data that describes a given region. GetRgnBox() Gets the coordinates of a CRgn objects bounding rectangle. OffsetRgn() Moves a CRgn object by the specified offsets. PtInRegion() Determines whether a specified point lies within a region. RectInRegion() Determines whether any part of a specified rectangle lies within the boundaries of a region. SetRectRgn() Sets an existing CRgn object to a specified rectangular region. operator HRGN Returns the Windows handle wrapped in the CRgn object.

GDI Coordinate Systems The GDI supports two types of coordinate systems: physical and logical. The physical coordinate system is that of the physical device, such as a video display. A window on the display starts at the originlocation (0,0)at the upper-left corner of the display, with the x-axis increasing going to the right and the y-axis increasing going down. The lower-right corner of the window corresponds with the lower-right corner of the display (see Figure 4.8), but the actual numbers for a video display depend on the current video resolution. Typical locations are (640, 480), (800, 600), or (1024, 768).

132 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Figure 4.8 The physical (device) coordinate system. Tip: Use the Win32 API function GetSystemMetrics() to find the actual display size in pixels. Use GetSystemMetrics(SM_CXSCREEN) to get the screen width; use GetSystemMetrics(SM_CYSCREEN) to get the screen height. There are several logical coordinate systems, and Windows maps coordinates on the current logical display to the physical device before displaying any graphical output. Each of the GDI functionsand therefore the MFC wrapper methods in class CDCuse logical coordinates. The resulting output can vary depending on a device contexts current mapping mode . Mapping modes allow an application to send GDI graphics output to a logical window. The GDI then maps the output to the physical window or some other device (such as a printer). Logical Mapping Modes Windows uses mapping modes to project a logical coordinate system into the physical coordinate system. The origin of a logical window defines the upper-left corner of the window. All other points are referenced from the origin. The mapping mode defines the length of a logical unit of measure used when converting logical units to physical device units. The mapping mode also determines the orientation of the devices x and y axes. The GDI uses a DCs current mapping mode to convert logical coordinates into the corresponding device coordinates. Table 4.10 lists the logical mapping modes supported by Windows. Table 4.10 The Logical Mapping Modes Supported by Windows Mapping Mode Description

MM_ANISOTROPIC The mapping between logical and physical coordinates uses variably scaled axes; images in the logical window can be stretched in any direction when redrawn. This mode doesnt change the current window or viewport settings. Use the CDC::SetWindowExt() and CDC::SetViewportExt() methods to change units, orientation, and scaling. The positive x-axis is to the right and the positive y-axis goes up. MM_HIENGLISH Each unit in the logical window is 0.001 inch long. The positive xaxis is to the right and the positive y-axis goes up. MM_HIMETRIC Each unit in the logical window is 0.01 millimeter long. The positive x-axis is to the right and the positive y-axis goes up. MM_ISOTROPIC The mapping between logical and physical coordinates uses equally scaled axes; images in the logical window can be resized in any direction but retain the same aspect ratio when redrawn. To
133 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

MM_LOENGLISH MM_LOMETRIC MM_TEXT MM_TWIPS

make sure that both x and y units remain the same size, the GDI adjusts sizes as necessary. Setting the mapping mode to MM_ISOTROPIC doesnt change the current window or viewport settings. Use the SetWindowExt() and SetViewportExt() methods to change units, orientation, and scaling. The positive x-axis is to the right and the positive y-axis goes up. Each logical unit represents 0.01 millimeters. The positive x-axis is to the right and the positive y-axis goes up. Each logical unit represents 0.1 millimeter. The positive x-axis is to the right and the positive y-axis goes up. Each logical unit represents 1 device pixel. The positive x-axis is to the right and the positive y-axis goes down. Each logical unit represents 1/20 of a point. Because a point is 1/72 inch, that makes a twip 1/1440 inch. The positive x-axis is to the right and the positive y-axis goes up.

Tip: Its a good idea to clearly distinguish between logical and physical devices when using Win32 API functions and their MFC wrapper methods. In general, logical coordinates are used for most drawing functions; physical coordinates are used in window management functions (such as SetWindowPos()). Vector Graphics Many types of vector graphic output functions are available for your applications from the Win32 GDI. These functions fall into two basic categories: Lines and curves Closed figures These categories can further be broken into the following, more detailed, categories of Win32 GDI functions, which are reflected in CDC class methods: Lines Ellipses Rectangles and regions Polygons Bzier curves Metafiles Fonts and text output

Drawing Modes Windows provides several drawing modes. A drawing mode specifies how the colors combine between the current pen and objects already present on the display surface. The drawing modes represent all possible Boolean combinations of two variables. They use the binary operators AND, OR, and XOR, and the unary operation NOT in the binary raster-operation codes listed in Table 4.11. The drawing mode is set using the CDC::SetRop2() method, which has the following prototype:
134 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

int SetROP2( int nDrawMode );

The nDrawMode parameter specifies the new drawing mode, which can be any of the values in Table 4.11. This method returns the previous drawing mode.

Table 4.11 The Drawing Modes Defined by Win32 Represent All Possible Boolean Combinations of Two Variables Mode Identifier R2_BLACK R2_COPYPEN R2_MASKNOTPEN Description

The pixel color is always black. The pixel is the color of the pen. The pixel color is a combination of the colors common to both the screen and the inverse of the pen (final pixel = [NOT pen] AND screen pixel). R2_MASKPEN The pixel color is a combination of the colors common to both the pen and the screen (final pixel = pen AND screen pixel). R2_MASKPENNOT The pixel color is a combination of the colors common to both the pen and the inverse of the screen (final pixel = [NOT screen pixel] AND pen). R2_MERGENOTPEN The pixel color is a combination of the screen color and the inverse of the pen color (final pixel = [NOT pen] OR screen pixel). R2_MERGEPEN The pixel color is a combination of the pen color and the screen color (final pixel = pen OR screen pixel). R2_MERGEPENNOT The pixel color is a combination of the pen color and the inverse of the screen color (final pixel = [NOT screen pixel] OR pen). R2_NOP The pixel color stays the same. R2_NOT The pixel color is the inverse of the screen color. R2_NOTCOPYPEN The pixel color is the inverse of the pen color. R2_NOTMASKPEN The pixel color is the inverse of the R2_MASKPEN color (final pixel = NOT[pen AND screen pixel]). R2_NOTMERGEPEN The pixel color is the inverse of the R2_MERGEPEN color (final pixel = NOT[pen OR screen pixel]). R2_NOTXORPEN The pixel color is the inverse of the R2_XORPEN color (final pixel = NOT[pen XOR screen pixel]). R2_WHITE The pixel color is always white. R2_XORPEN The pixel color is a combination of the colors that are in the pen or in the screen, but not in both (final pixel = pen XOR screen pixel).

Note: The program RASTER1.EXE and its source code on the CD-ROM show how to use each of these raster operations and the results of using each. Figure 4.9 shows the results of using each of the named raster operations in the sample
135 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

program RASTER1.EXE.

Figure 4.9 The results of using each of the named raster operations to combine two ellipses into a single image. Points Windows drawing functions typically take location parameters as x and y coordinates defining a point. Windows defines a point in the POINT structure (see windef.h) that looks like this:
typedef struct tagPOINT { LONG x; LONG y; } POINT, *PPOINT, NEAR *NPPOINT, FAR *LPPOINT;

MFC wraps the POINT structure with the CPoint class structure that provides the methods shown in Table 4.12. Note that the CPoint class isnt derived from CObject like most other MFC classes, but from the tagPOINT typedef structure just shown. Table 4.12 CPoint Class Methods and Operators Method Offset() operator == operator != operator += operator = Description Adds values to the x and y members of the CPoint object. Checks two points for equality. Checks two points for inequality. Offsets a CPoint object by adding a size or point. Offsets a CPoint object by subtracting a size or point.
136 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

operator + operator -

Returns the sum of a CPoint object and a size or point, or returns a CRect offset by a size. Returns the difference of a CPoint object and a size (or the negation of a point), or returns a CRect offset by a negative size, or returns the size difference between two points.

As you can see, the CPoint class offers numerous operators to make point calculations easier.

Drawing Points To draw a single point on a device context, call the CDC::SetPixel() method using this prototype:
COLORREF SetPixel(POINT point, COLORREF crColor);

In this syntax, point specifies the raster display pixel to set, and crColor specifies the color of pixel. Lines and Polylines Lines are drawn on a DC using the CDC methods MoveTo(), LineTo(), and PolyLine(). These methods all use the pen currently selected into the DC for their drawing. Note: In MFC, the CPen object currently selected into a device context is the one that controls line styles, colors, and thickness. Windows defines the following six pen styles in WINGDI.H:
#define #define #define #define #define #define PS_SOLID PS_DASH PS_DOT PS_DASHDOT PS_DASHDOTDOT PS_NULL 0 1 2 3 4 5 // // // // // _______ ------....... _._._._ _.._.._

Setting the Current Drawing Position The CDC::MoveTo() method sets the current drawing position (current position ). CDC provides two overloaded prototypes for this method to make setting the current position easy:
CPoint MoveTo(int x, int y); CPoint MoveTo(POINT point);

This means that you can pass the x and y coordinates for the new current position separately or in a POINT structure (or in a CPoint object). Drawing a Single Line
137 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

The CDC::LineTo() method draws a line from the current position up to, but not including, the point specified by either x and y, or by a point. For each case, CDC again provides overloaded methods:
BOOL LineTo(int x, int y); BOOL LineTo(POINT point);

The code fragment in Listing 4.4 shows how the VECTEXT1 program displays all six pen line-styles (pDC is assumed to be a valid pointer to a CClientDC). Figure 4.10 shows the result of this code; note that the sixth style is PS_NULL, the invisible pen, and doesnt appear at all.

Figure 4.10 Drawing all six line-styles in the VECTEXT1 program. Listing 4.4 Drawing Lines Using All Six Pen Line-styles
// Six different pen styles int cy = 25; for (int nLineStyle = 0; nLineStyle < 6; nLineStyle++) { // Create a new pen CPen penBlue; // Pick the current pen style penBlue.CreatePen(nLineStyle, 1, crBlue); // Select the new pen into the device context, and save // the old pen to restore on clean up... CPen* ppenOld; ppenOld = pDC->SelectObject(&penBlue);
138 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

// Draw on the DC pDC->MoveTo(10, cy); pDC->LineTo(10 + nLineLength, cy); cy += 20; // Leave things as you found them (clean up) pDC->SelectObject(ppenOld); }

Drawing a Polyline The CDC::PolyLine() method draws a set of line segments connecting the points specified in an array of points. The lines use the current pen, but unlike the CDC::LineTo() method, the CDC::PolyLine() method doesnt use or update the current position. The prototype for CDC::PolyLine() is shown here:
BOOL Polyline(LPPOINT lpPoints, int nCount);

In this syntax, lpPoints is a pointer to an array of points, and nCount is the number of points in the array. In the VECTEXT1 program, class CMainWnd defines an array of CPoint objects as the member array variable m_apt. This array is initialized to the following values:
// Define an m_apt[0].x = m_apt[1].x = m_apt[2].x = m_apt[3].x = m_apt[4].x = m_apt[5].x = m_apt[6].x = m_apt[7].x = m_apt[8].x = m_apt[9].x = array of points 200; m_apt[0].y 200; m_apt[1].y 375; m_apt[2].y 480; m_apt[3].y 590; m_apt[4].y 465; m_apt[5].y 320; m_apt[6].y 205; m_apt[7].y 115; m_apt[8].y 100; m_apt[9].y = = = = = = = = = = 420; 200; 300; 310; 350; 200; 150; 100; 150; 175;

The DoPolyLine() method in Listing 4.5 shows how the VECTEXT1 program draws a polyline using the CMainWnd class member array of points: m_apt[10] (pDC is assumed to be a valid pointer to a CClientDC). This code produces the result shown in Figure 4.11.

139 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Figure 4.11 A polyline from 10 points in the VECTEXT1 program. Listing 4.5 Drawing a Polyline with Visible Vertices: the VECTEXT1 CMainWnd::DoPolyLine() Method
void CMainWnd::DoPolyLine(CClientDC* pDC) { // Create a label CString str = A polyline with 10 vertices:; pDC->TextOut(5, 5, str); // Draw points for (int i = 0; i < 10; i++) pDC->Ellipse(m_apt.x - 4, m_apt.y - 4, m_apt.x + 4, m_apt.y + 4); // Create a new pen CPen penRed; penRed.CreatePen(PS_SOLID, 1, crRed); // Select a new pen into the device context, and save // the old pen to restore on clean up... CPen* ppenOld; ppenOld = pDC->SelectObject(&penRed); // Draw lines with red pen to connect vertex points pDC->Polyline(m_apt, 10); // Leave things as you found them (clean up) pDC->SelectObject(ppenOld);
140 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Rectangles Like the POINT structure, the rectangle structure (RECT) is one of the most important in Windows. RECT structures are used to store window coordinates and sizes, and as parameters for many MFC methods and Win32 API functions. The following RECT structure defines the position and size of a logical rectangle:
typedef struct tagRECT { LONG left; LONG top; LONG right; LONG bottom; } RECT, *PRECT, NEAR *NPRECT, FAR *LPRECT;

MFC wraps the RECT structure within the CRect class that provides the methods and operators listed in Table 4.13. Table 4.13 CRect Class Methods and Operators Method BottomRight() CenterPoint() CopyRect() DeflateRect() EqualRect() Height() InflateRect() IntersectRect() IsRectEmpty() IsRectNull() NormalizeRect() OffsetRect() PtInRect() SetRect() SetRectEmpty() Size() SubtractRect() TopLeft() UnionRect() Width() Description Returns the bottom-right point of a CRect object. Returns the center point of a CRect object. Copies the dimensions of a source rectangle to a CRect object. Decreases the width and height of a CRect object. Compares the coordinates of a CRect object and a given rectangle for equality. Returns the height of a CRect object. Increases the width and height of a CRect object. Returns a CRect object defined by the area of intersection between two rectangles. Determines whether a CRect object is empty (if width or height are zero). Determines whether the top, bottom, left, and right member variables all equal zero (NULL). Normalizes the height and width of a CRect object. Moves a CRect object by the specified offset values. Tests whether a specified point lies within a CRect object. Sets the size of a CRect object. Sets all coordinates of a CRect object to zero, making an empty rectangle. Returns the size of a CRect object. Subtracts one rectangle from another. Returns the top-left point of a CRect object. Returns a CRect object defined by the area of union of two rectangles. Calculates the width of a CRect object.
141 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

operator LPCRECT operator LPRECT operator = operator == operator != operator += operator = operator &= operator |= operator + operator operator & operator |

Converts a CRect object to an LPCRECT. Converts a CRect object to an LPRECT. Copies the size and position of a rectangle to a CRect object. Tests the bounding coordinates of two CRect objects to see whether they are the same. Tests the bounding coordinates of two CRect objects to see whether they are not the same. Adds the specified offset values to a CRect object or inflates the object. Subtracts the specified offset values from a CRect object or deflates the object. Sets a CRect object equal to the intersection of the object and a rectangle. Sets a CRect object equal to the union of the object and a rectangle. Adds the given offset values to a CRect object or inflates the object and returns the resulting CRect. Subtracts the given offset values from a CRect object or deflates the object and returns the resulting CRect. Creates the intersection of a CRect object and a rectangle and returns the resulting CRect. Creates the union of a CRect object and a rectangle and returns the resulting CRect.

As you can see, the CRect class offers many useful methods that return the length, width, and coordinates of a rectangle and that allow you to move, resize, intersect, and combine rectangles. There are also many operators for working with CRect objects. Drawing Rectangles Regular rectangles (those with square corners) are typically drawn on a DC using the CDC methods Rectangle() and FillRect(). The CDC::Rectangle() method uses overloaded prototypes to specify the rectangle to draw in two ways:
BOOL Rectangle(int x1, int y1, int x2, int y2); BOOL Rectangle(LPCRECT lpRect);

In this syntax, x1, y1, x2, and y2 are the coordinates of a rectangle, and lpRect is a pointer to a rectangle. This method draws the outline of a rectangle with the current pen and fills it with the current brush. The CDC::FillRect() method doesnt draw a rectangle border, it just fills the rectangle with the current brush. This brush can be of any type (bitmap, dithered, and so on). The prototype for this method is shown here:
void FillRect(LPCRECT lpRect, CBrush* pBrush);

In this syntax, lpRect is a pointer to a rectangle, and pBrush is a pointer to a brush. To fill a rectangle with a solid COLORREF color, call the CDC::FillSolidRect() method using this
142 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

prototype:
void FillSolidRect(LPCRECT lpRect, COLORREF clr);

In addition to the regular square-cornered rectangles, Windows provides the rounded rectanglea rectangle with rounded corners. To draw a rectangle with rounded corners, call the CDC::RoundRect() method using this prototype:
BOOL RoundRect(LPCRECT lpRect, POINT point);

In this syntax, lpRect is a pointer to a rectangle, and point is the offset from the upper-left corner that defines the amount of fillet (rounding) applied to the rounded corners. Figure 4.12 shows the result of using a point offset of CPoint(50, 50).
// Draw a rounded rect on the DC pDC->RoundRect(arcSection[x][y], CPoint(50, 50));

Figure 4.12 Rounded rectangles created with CDC::RoundRect() in the VECTEXT1 program. Regions A region is a Windows graphic object that represents an area of the device contexts work space. This area can be composed of rectangles, ellipses, and polygons. Regions can be filled with the current brush or used to determine the clipping region of the DC (the area where drawing takes place). The clipping region can be any shape derived from the union, difference, or intersection of rectangles, ellipses, and polygons. A region is created with one of the CDC region-creation methods such as CDC::CreateRectRgn() or CDC::CreatePolygonRgn(). A region can be displayed using the methods CDC::FillRgn(), CDC::FrameRgn(), CDC::InvertRgn(), and CDC::PaintRgn() and
143 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

must be destroyed using the CDC::DeleteObject() method after youre done using it. The CDC methods for creating regions are listed in Table 4.14. Table 4.14 The Methods Provided by Class CDC for Creating Regions Method CombineRgn() Description

Creates a union of two specified CRgn objects and assigns the result of the union to a third CRgn object. CopyRgn() Copies a CRgn object and assigns the result to a second CRgn object. CreateEllipticRgn() Creates an elliptical region and attaches it to a CRgn object. CreateEllipticRgnIndirect() Creates an elliptical region defined by a RECT structure and attaches it to a CRgn object. CreateFromData() Creates a region from the given region and transformation data. CreateFromPath() Creates a region from the path that is selected into the given device context. CreatePolygonRgn() Creates a polygonal region and attaches it to a CRgn object. If needed, Windows automatically closes the polygon by drawing a line from the last vertex to the first. CreatePolyPolygonRgn() Creates a region consisting of a series of closed polygons and attaches it to a CRgn object. The polygons can be disjointed, or they can overlap. CreateRectRgn() Creates a rectangular region and attaches it to a CRgn object. CreateRectRgnIndirect() Creates a rectangular region defined by a RECT structure and attaches it to a CRgn object. CreateRoundRectRgn() Creates a rectangular region with rounded corners and attaches it to a CRgn object. The CDC methods for working with regions are listed in Table 4.15. Table 4.15 The Methods Provided by Class CDC for Working with Regions Method EqualRgn() FromHandle() GetRegionData() GetRgnBox() OffsetRgn() PtInRegion() RectInRegion() SetRectRgn() Description Tests two CRgn objects for equivalency. Returns a pointer to a CRgn object from a handle to an existing Windows region. Fills a buffer with data describing the given region. Gets the coordinates of the bounding rectangle of a CRgn object. Moves a CRgn object according to the specified offsets. Determines whether a specified point lies within the region. Determines whether any part of a specified rectangle lies within the bounding rectangle of a CRgn object. Sets an existing CRgn object to a specified rectangular region.

144 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

The CRgn::CombineRgn() method uses Boolean operations to create a new region. The prototype for this method is as follows:
int CombineRgn(CRgn* pRgn1, CRgn* pRgn2, int nCombineMode);

In this syntax, the parameters pRgn1 and pRgn2 are pointers to existing CRgn objects, and the nCombineMode parameter is one of the Boolean operations listed in Table 4.16. These operation values are defined by the Win32 API for combining regions. Table 4.16 The Boolean Operations Provided by the Win32 API and the MFC Class CRgn Boolean Value RGN_AND RGN_COPY RGN_DIFF RGN_OR RGN_XOR Description Uses overlapping areas of both regions (intersection). Creates a copy of the first region. Creates a region consisting of the areas of region 1 (identified by pRgn1) that are not part of region 2 (identified by pRgn2). Combines both regions in their entirety (union). Combines both regions but removes overlapping areas.

These operations are explored a bit in the VECTEXT1 program. Polygons The CDC::Polygon() method draws a closed polygon with the current pen, filled by the current brush. The points specified in an array of points are connected to create the polygon, and the last vertex is connected to the first if necessary. The prototype for CDC::Polygon() is as follows:
BOOL Polygon(LPPOINT lpPoints, int nCount);

In this syntax, lpPoints is a pointer to an array of points, and nCount is the number of points in the array. The DoPolygon() method in the VECTEXT1 program is very similar to the DoPolyLine() method; the difference is a call to CDC::Polygon() instead of CDC::Polyline(). Figure 4.13 shows the result of using the array of points in the class member m_apt[10] to draw the polygon with 10 vertices.

145 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Figure 4.13 Drawing a closed polygon in the VECTEXT1 program. Ellipses Ellipses use rectangles as bounding boxes, and the CDC::Ellipse() method takes a rectangle as a parameter. The CMainWnd::DoEllipses() method from the VECTEXT1 program (shown in Listing 4.6) divides the client area into six equal logical rectangles and uses them as bounding boxes for six ellipses. Each ellipse has one of the six different hatch brush styles. The result of this code is shown in Figure 4.14. The CDC::Ellipse() method draws an ellipse with the current pen, filled by the current brush. There are two overloaded prototypes for CDC::Ellipse():
BOOL Ellipse(int x1, int y1, int x2, int y2); BOOL Ellipse(LPCRECT lpRect);

In this syntax, x1, y1, x2, and y2 are the corners of a bounding rectangle for the ellipse. The lpRect parameter is a pointer to a bounding rectangle for the ellipse. The DoEllipses() method in the VECTEXT1 program uses various hatch brush styles to fill ellipses in the client area. Figure 4.14 shows the result of using these hatch brushes.

146 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Figure 4.14 Drawing ellipses with various hatch brush styles in the VECTEXT1 program. Listing 4.6 The Code to Draw Six Ellipses with the Six Different Hatch Brush Styles
/////////////////////////////////////////////////////////////////// // CMainWnd::DoEllipses() void CMainWnd::DoEllipses(CClientDC* pDC) { // Create a label CString str = Ellipses filled with hatch brushes:; pDC->TextOut(5, 5, str); // Subdivide the display into 6 sections CRect rc; CRect arcSection[4][3]; GetClientRect(&rc); int cx = rc.right / 3; int cy = (rc.bottom - 25) / 2; int nHeight = cy; for (int x = 0; x < 2; x++) { int nWidth = cx; for (int y = 0; y < 3; y++) { arcSection[x][y].left = nWidth - cx; arcSection[x][y].top = nHeight - cy + 25; arcSection[x][y].right = arcSection[x][y].left + cx;
147 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

arcSection[x][y].bottom = arcSection[x][y].top + cy; nWidth += cx; } nHeight += cy; } // Six different hatch brush styles cy = 25; int nHatchStyle = 0; for (x = 0; x < 2; x++) { for (int y = 0; y < 3; y++) { /*================================================== From WINGDI.H: // Hatch Styles #define HS_HORIZONTAL 0 // ----#define HS_VERTICAL 1 // ||||| #define HS_FDIAGONAL 2 // \\\\\ #define HS_BDIAGONAL 3 // ///// #define HS_CROSS 4 // +++++ #define HS_DIAGCROSS 5 // xxxxx ===================================================*/ // Create a hatch brush CBrush br(nHatchStyle, crRed); // Select the new brush into the device context, and save // the old brush to restore on clean up... CBrush* pbrOld; pbrOld = pDC->SelectObject(&br); // Draw an ellipse on the DC pDC->Ellipse(arcSection[x][y]); nHatchStyle++; // Leave things as you found them (clean up) pDC->SelectObject(pbrOld); } } }

Bzier Curves A Bzier curve is a parametric curve (or spline curve) defined by a set of control points. Blending functions are applied to groups of control points to calculate the curve. MFC provides a third-order Bzier blending function in the form of the CDC::PolyBezier() and CDC::PolyBezierTo() methods. The prototype for the CDC::PolyBezier() method is as follows:
BOOL PolyBezier(const POINT* lpPoints, int nCount);

In this syntax, lpPoints is a pointer to an array of POINT data structures (or CPoint objects) that contains the endpoints and control points of the splines, and nCount
148 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

specifies the number of points in the lpPoints array. Note: The value must be one more than three times the number of splines to be drawn, because each Bzier spline requires two control points and an endpoint, and the initial spline requires an additional starting point. The code in Listing 4.7 shows how the VECTEXT1 program uses the array of points in the m_apt[10] class member to draw a static Bzier curve composed of three splines. The result of the DoBezier() method can be seen in Figure 4.15.

Figure 4.15 Drawing a static Bzier curve in the VECTEXT1 program.

Listing 4.7 The DoBezier() Method from VECTEXT1 Draws a Series of Bzier Curves and Control Points
void CMainWnd::DoBezier(CClientDC* pDC) { // Create a label CString str = Bezier with ten control points:; pDC->TextOut(5, 5, str); // Draw points for (int i = 0; i < 10; i++)
149 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

pDC->Ellipse(m_apt.x - 4, m_apt.y - 4, m_apt.x + 4, m_apt.y + 4); // Create new pens CPen penBlue; CPen penRed; penBlue.CreatePen(PS_DOT, 1, crBlue); penRed.CreatePen(PS_SOLID, 1, crRed); // Select a new pen into the device context, and save // the old pen to restore on clean up... CPen* ppenOld; ppenOld = pDC->SelectObject(&penBlue); // Draw lines with blue pen to connect control points pDC->Polyline(m_apt, 10); // Draw Bezier curve with red pen pDC->SelectObject(&penRed); pDC->PolyBezier(m_apt, 10); // Leave things as you found them (clean up) pDC->SelectObject(ppenOld); }

Fonts and Text Text is very important to users of computer programs, even users of programs created for a GUI OS like Windows. In fact, text output is one of the most important features of the operating system. Since the days of Windows 3.1, Windows programs have been able to access a wonderful technology called the TrueType font (TTF). TrueType fonts are vector fonts scaleable to virtually any size with no degradation of image quality. Note: To get you started working with fonts, some font terminology should be explained. The term typeface refers to the style of lettering and the visual appearance of the text. The term font refers to a set of characters in a given typeface (for example, Arial ). In font terminology, a point is 1/72 of an inch and is used to measure the height of font characters. Font Characteristics Two main categories of fonts are used in Windows: fixed width (or monospace) and variable width. Monospace font characters are all the same width; variable-width font characters each take up as much space as they need. Windows defines three different types of fonts: raster fonts (bitmapped fonts), vector fonts (fonts composed of a series of line segments), and TrueType (fonts that use lines and spline curves to define character outlines). Font Families
150 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

All typefaces are grouped into font families that represent the style (or mood) of the font. Identifiers for the default families are found in the header file WINGDI.H; these font family (FF_*) identifiers are listed in Table 4.17. Table 4.17 Font Families Provided by Windows Family Name FF_DECORATIVE FF_DONTCARE FF_MODERN FF_ROMAN FF_SCRIPT FF_SWISS Description This is a novelty font family. One example is Viking Runes. This is a generic family name used when no information about a font is needed. This is a monospace font with or without serifs. Monospace fonts are usually modern; examples include Pica, Elite, and Courier New. Specifies a proportional font with serifs. An example is Times New Roman. Specifies a font designed to look like handwriting; examples include Script and Cursive. Specifies a proportional font without serifs; an example is Arial.

These IDs are used when creating, selecting, or getting information about a font. Fonts can have various point sizes and styles (like bold or italics). This book is concerned mainly with the TrueType variety because its the most versatile and is relatively easy to use (even if it is somewhat complex). The TEXTMETRIC Structure To output text, Windows uses a bounding rectangle and paints a representation of the current character to the display pixels as a bitmap. There are Win32 API functions and MFC methods that use a Windows TEXTMETRIC structure to get information about text characters on the display. The TEXTMETRIC structure contains information about a display font and gives size information in logical units that depend on the current mapping mode. The TEXTMETRIC structure is shown in Listing 4.8. Listing 4.8 The Windows TEXTMETRIC Structure
typedef struct tagTEXTMETRIC { LONG tmHeight; LONG tmAscent; LONG tmDescent; LONG tmInternalLeading; LONG tmExternalLeading; LONG tmAveCharWidth; LONG tmMaxCharWidth; LONG tmWeight; LONG tmOverhang; LONG tmDigitizedAspectX; LONG tmDigitizedAspectY; BCHAR tmFirstChar; BCHAR tmLastChar;

// // // // // // // // // // // // //

Character height (ascent + descent) Character ascent above base line Character descent below base line Extra space inside tmHeight Extra space between text rows Average width of font characters Width of the widest font character The weight of the font Extra width padding Horizontal aspect of target device Vertical aspect of target device Char value of first font character Char value of last font character
151 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

BCHAR tmDefaultChar; BCHAR tmBreakChar; BYTE tmItalic; BYTE tmUnderlined; BYTE tmStruckOut; BYTE tmPitchAndFamily; BYTE tmCharSet; } TEXTMETRIC;

// // // // // // //

Default substitution character Char value for word break formats An italic font if its nonzero An underlined font if its nonzero A strikeout font if its nonzero Font pitch and family information Character set of the font

Note: In addition to the TEXTMETRIC structure, there is a NEWTEXTMETRIC structure defined to hold additional information about TrueType fonts. To get basic information about the font currently selected into a device context, you can use the CDC::GetTextMetrics() method. The prototype for this method is shown following:
BOOL GetTextMetrics(LPTEXTMETRIC lpMetrics) const;

In this syntax, lpMetrics is a pointer to a TEXTMETRIC structure that receives the text metrics. For example, to find the average width of the font currently in a given device context, simply call GetTextMetrics(), as in this code fragment:
TEXTMETRIC tm; if (dc.GetTextMetrics(&tm)) int nAveCharWidth = tm.tmAveCharWidth;

The VECTEXT1 program displays complete TEXTMETRIC information about the Windows default system font by calling the CMainWnd::DoTextSystem() method (see Listing 4.9). Figure 4.16 shows the result of this method.

152 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Figure 4.16 Displaying complete TEXTMETRIC information about the Windows default system font. Listing 4.9 Displaying System Font TEXTMETRIC Information in the VECTEXT1 programs CMainWnd::DoTextSystem() Method
void CMainWnd::DoTextSystem(CClientDC* pDC) { // Create a label CString strLbl = Generic text information on the system font:; pDC->TextOut(5, 5, strLbl); // Select the system font into the DC HFONT hFont = (HFONT)::GetStockObject(SYSTEM_FONT); CFont fnt; CFont* pFont = fnt.FromHandle(hFont); CFont* pfntOld = pDC->SelectObject(pFont); // Get some info about the current font TEXTMETRIC tm; pDC->GetTextMetrics(&tm); UINT nCurLineHeight = 40; // Create an array of strings to display the TEXTMETRIC data CString str[42]; // Label strings str[0] = Height; // // etc. for all label strings
153 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

// str[20] = CharSet; // Data strings str[21].Format( = %d, tm.tmHeight); // // etc. for all data strings // str[41].Format( = %d, tm.tmCharSet); // Display strings 0-20 for (int i = 0; i < 21; i++) { pDC->TextOut(20, nCurLineHeight, str); nCurLineHeight += tm.tmHeight; } // Reset line height nCurLineHeight = 40; // // // // // // // Now you need to determine the distance that the data strings should move to the right to align the data evenly. str[9] is the longest string, so you calculate the width + 2 to space the = signs nicely.

UINT nLeft = 20 + tm.tmAveCharWidth * (str[9].GetLength() + 2); // Display strings 21-41 for (i = 21; i < 42; i++) { pDC->TextOut(nLeft, nCurLineHeight, str); nCurLineHeight += tm.tmHeight; } if (pfntOld) pDC->SelectObject(pfntOld); }

The LOGFONT Structure If you need more information about a font or want to create a custom font, you have to understand logical fonts , which are defined with the Windows LOGFONT structure (see Listing 4.10). A logical font is an abstract font description that defines the fonts characteristics (bold, italic, size, and so on). A logical font must be selected into a device context before it can be used. After being selected into a device context, the logical font becomes a physical font . Listing 4.10 The LOGFONT Structure Defines a Logical Font
typedef { LONG LONG LONG struct tagLOGFONT lfHeight; lfWidth; lfEscapement;
154 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

LONG lfOrientation; LONG lfWeight; BYTE lfItalic; BYTE lfUnderline; BYTE lfStrikeOut; BYTE lfCharSet; BYTE lfOutPrecision; BYTE lfClipPrecision; BYTE lfQuality; BYTE lfPitchAndFamily; TCHAR lfFaceName[LF_FACESIZE]; } LOGFONT;

The members of this structure are described in Table 4.18. Table 4.18 The Members of the LOGFONT Structure Member lfHeight lfWidth lfEscapement lfOrientation lfWeight lfItalic lfUnderline lfStrikeOut lfCharSet lfOutPrecision lfClipPrecision lfQuality lfPitchAndFamily lfFaceName Description The logical height of a character cell or character. The average logical width of characters in the font. The angle, in tenths of degrees, between the escapement vector (parallel to the base line of a row of text) and the x-axis of the device. The angle, in tenths of degrees, between each characters base line and the x-axis of the device. The weight of the font (ranging from 0 to 1000). An italic font if TRUE. An underlined font if TRUE. A strikeout font if TRUE. The fonts character set. The output precision, which defines how closely the output must match a requested fonts height, width, character orientation, escapement, pitch, and font type. The clipping precision, defining how to clip characters that lie on clipping boundaries. The output quality, defining how well the GDI must match logicalfont attributes to physical font attributes. The pitch and family of a font. A string, limited to 32 characters, that denotes the typeface name of the font.

The LOGFONT structure is used when creating a logical font for use in a device context; the following section explains how. Font Creation MFC wraps logical fonts and methods into the CFont class. To create a logical font for use in your applications, you can use any of the initialization methods provided by the CFont class. Table 4.19 lists the creation methods class CFont provides.
155 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Table 4.19 Class CFont Creation Methods Method Description

CreateFontIndirect() Initializes a CFont object as defined by a LOGFONT structure. CreateFont() Initializes a CFont object with the specified characteristics. CreatePointFont() Initializes a CFont object with the specified height and typeface. CreatePointFontIndirect() Initializes a CFont object as defined by a LOGFONT structure. Font height is measured in tenths of a point instead of logical units.

As you do with most MFC objects, you use two-step construction when creating a CFont object. The VECTEXT1 programs CMainWnd::DisplayLogFont() method in Listing 4.11 shows how to create a logical font. This method also uses text metrics and logical font members to display the font in various locations and sizes. Calling the method like this produces the image in Figure 4.17:
DisplayLogFont(pDC, Arial);

Figure 4.17 The result of using CMainWnd::DisplayLogFont() to display various sizes of text. Listing 4.11 Creating a Logical Font and Displaying 16 Sizes from 2 Points to 32 Points

156 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

/////////////////////////////////////////////////////////////////// // CMainWnd::DisplayLogFont() void CMainWnd::DisplayLogFont(CClientDC* pDC, CString sFont) { // Draw various sizes of fonts UINT uSize = 2; // Starting point size int cyDrawHere = 30; // Start vertical drawing text here CFont* pfntOld = 0; // 16 lines of text, font sizes from 2 to 32 points for (int i = 0; i < 16; i++) { // Create a new font and init a LOGFONT structure CFont fnt; LOGFONT lf; TEXTMETRIC tm; memset(&lf, 0, sizeof(LOGFONT)); // Set initial font typeface name and font size lstrcpy(lf.lfFaceName, sFont); int cyPixels = pDC->GetDeviceCaps(LOGPIXELSY); lf.lfHeight = -MulDiv(uSize, cyPixels, 72); // Create the new font fnt.CreateFontIndirect(&lf); // Get the text metrics pDC->GetTextMetrics(&tm); cyDrawHere += abs(lf.lfHeight) + tm.tmExternalLeading; // Make the new font current in the DC pfntOld = pDC->SelectObject(&fnt); // Draw a line of text CString str; str.Format(Font name: %s, size: %d points, lf.lfFaceName, uSize); pDC->TextOut(5, cyDrawHere, str); uSize += 2; } // Restore the previous font to the DC pDC->SelectObject(pfntOld); }

The CDC::TextOut() method used in Listing 4.11 is only one of class CDCs text output methodstext can be drawn in several ways. Drawing Text Text is drawn using a device contexts selected font, text color, and background color. You can set the text color by calling the CDC::SetTextColor() method; you can set the background color by calling the CDC::SetBkColor() and CDC::SetBkMode() methods.
157 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

To change the text in a DC to blue and make the text background transparent (so that whats behind the text shows through), simply use this code (assuming that pDC is a pointer to CDC-derived object):
#define crBlue RGB(0, 0, 255) pDC->SetTextColor(crBlue); pDC->SetBkMode(TRANSPARENT); // macro for naming a color

MFC provides several text output methods in class CDC. The most often used of these are DrawText(), TextOut(), and TabbedTextOut(). The DrawText() Method The DrawText() method formats text within a given rectangle using many advanced formatting features, including expanding tabs into spaces; justifying text to the left, right, or center of the rectangle; and automatically breaking text into lines to fit within the rectangle. When using the CDC::DrawText() method, you usually use this overloaded prototype:
int DrawText(const CString& str, LPRECT lpRect, UINT nFormat);

The str parameter is a CString object that contains the text to draw; the lpRect parameter points to a RECT or CRect object that forms the bounding box in which the text is to be formatted; the nFormat parameter specifies how the text is formatted. The TextOut() Method The TextOut() method draws a text string at a specified location using the currently selected font and colors. The overloaded prototype you most often use is shown here:
BOOL TextOut(int x, int y, const CString& str);

In this syntax, x and y specify the logical coordinate where the text begins drawing, and str is a CString object that contains the text to draw. The TabbedTextOut() Method The TabbedTextOut() method draws a text string at the specified location and expands tabs to values specified in an array of tab-stop positions. The overloaded prototype you most often use is as follows:
CSize TabbedTextOut(int x, int y, const CString& str, int nTabPositions, LPINT lpnTabStopPositions, int nTabOrigin );

The parameters for this method are listed in Table 4.20. Table 4.20 The Parameters of the CDC::TabbedTextOut() Method Parameter x, y str nTabPositions lpnTabStopPositions Meaning The logical x and y coordinates of the starting point of the string. A CString object that contains the specified characters. The number of values in the array of tab-stop positions. A pointer to an array containing the tab-stop positions in logical
158 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

nTabOrigin

units. Specifies the logical x coordinate of the tab expansion starting position.

Sample Program: Vector Graphics and Text Methods (VECTEXT1.EXE) The VECTEXT1 program on the companion CD-ROM provides examples of many CDC graphics and text methods. The program creates a frame window with a non-sizable, dialog-style window border that handles mouse clicks (left and right) in its client area. The functionality for the program is mostly in the frame window class (CMainWnd). Many protected methods are provided as examples of many common graphics needs. These methods are listed and described in Table 4.21. Note: The VECTEXT1 program is much too long to list in this book, but the full source code is on the CD-ROM. Table 4.21 The Graphics and Helper Methods Found in the VECTEXT1 Programs CMainWnd Class Method DoBezier() DoEllipses() DoLines() DoMetafile() Description

Displays three Bzier curves defined by ten control points. Displays six ellipses with various hatch brush styles. Displays randomly generated lines. Creates an enhanced metafile, records lines with random colors and thicknesses at random locations, and plays back the finished metafile on the CMainWnd client area. DoPixels() Displays a random number of pixels with random locations and colors. DoPoints() Displays 44-pixel-bounded ellipses with random locations and colors (just like DoPixels(), but with small 44 ellipses). DoPolygon() Displays a filled polygon using the CMainWnd class array of points: m_apt[10]. DoPolyLine() Displays a polyline and its vertices using the CMainWnd class array of points: m_apt[10]. DoRects() Displays six rectangles with various hatch brush styles. DoRegions() Displays two overlapping regions, one an elliptical region, the other a rectangular region. DoRegionsUnion() Displays the result of the union of two overlapping regions, one an elliptical region, the other a rectangular region. DoRegionsDifference() Displays the result of the difference of two overlapping regions, one an elliptical region, the other a rectangular region. DoRegionsIntersect() Displays the result of the intersection of two overlapping regions, one an elliptical region, the other a rectangular region. DoRoundRects() Displays six rounded rectangles filled with randomly colored solid brushes.
159 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

DoTextArial() DoTextRoman() DoTextSystem()

Specifies, creates, and displays the standard Arial TrueType font in sizes from 2 points to 32 points. Specifies, creates, and displays the standard Times New Roman TrueType font in sizes from 2 points to 32 points. Displays complete TEXTMETRIC information about the standard system font.

The VECTEXT1 program uses the concept of graphics pages that change with each mouse click in the client area. The current page is determined by the CMainWnd member variable m_CurPage; each click triggers a call to one of the methods listed in Table 4.21 (within the CMainWnd::ProcessMouseClick() method), changing the page. When all the pages have been displayed, they start over from the beginning. The source code for this program should be enough to get you well on your way to mastering GDI graphics, MFCstyle! Raster Graphics Vector graphics use mathematical formulas to describe images on a device context. Raster graphics differ from vector graphics in that they are based on information displayed in an array of pixels. Vector graphics are size-independent; they can be stretched to virtually any size without losing clarity or becoming distorted. Raster graphics, on the other hand, are constrained by the pixels that represent them. Resizing raster images most often results in distortion of the image, with a jagged, pixelized, staircase effect. Named Raster Operations (ROPs) Windows supports 256 ternary raster operations for use with the GDI bit block transfer operations (BitBlt() and related functions). These raster operations (ROPs) provide logical operations that combine the source, destination, and current brush pattern. Although there are 256 of these operations, in practice only a small number of them are used; only 15 are important enough to have earned common names for themselves. These 15 named ternary ROPs are described in Table 4.22. Table 4.22 The 15 Named Ternary Raster Operations (ROPs) Used by GDI Bit Block Transfer Functions ROP BLACKNESS DSTINVERT MERGECOPY MERGEPAINT NOTSRCCOPY NOTSRCERASE Description Creates black output in the destination bitmap. Inverts the destination bitmap. Combines a pattern bitmap with a source bitmap using the Boolean AND operator. Inverts the source bitmap and combines it with the destination bitmap using the Boolean OR operator. Inverts the source bitmap and copies it to the destination. Combines the destination and source bitmaps using the Boolean OR operator and then inverts the result.
160 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

PATCOPY PATINVERT PATPAINT SRCAND SRCCOPY SRCERASE SRCINVERT SRCPAINT WHITENESS

Copies a pattern to the destination bitmap. Combines the destination bitmap with a pattern using the Boolean XOR operator. Inverts the source bitmap, combines it with a pattern using the Boolean OR operator, and combines the result with the destination bitmap using the Boolean OR operator. Combines the destination and source bitmaps using the Boolean AND operator. Copies the source bitmap to the destination bitmap. Combines the inverted destination bitmap with the source bitmap using the Boolean AND operator. Combines the destination and source bitmaps using the Boolean XOR operator. Combines the destination and source bitmaps using the Boolean OR operator. Creates white output in the destination bitmap.

Raster operation codes define how GDI combines the bits from a source bitmap with the bits in a destination bitmap. The ROP codes used for bit block transfer operations are called ternary ROPs because they use three operands: A source bitmap A brush A destination bitmap Bitmaps A bitmap is an array of bits that form an image of some kind. All raster devices, including video displays, use bitmaps to display images. The video display uses pixels to show the bitmap bits onscreen. Bitmaps come in various bit depths (or color depths) and flavors. The two types of bitmaps used by Windows are device-dependent bitmaps (DDBs) and device-independent bitmaps (DIBs). Both of these bitmap types can have different color depths. The color depth of an image is directly related to the number of bits used to store the color data in a bitmapped image. A 1-bit image is called monochrome and can display two colors (black and white by default). A 4-bit image can display 16 colors (this is the standard for VGA video adapters). An 8-bit image can display up to 256 colors (the minimum for super VGA video adapters). A 16-bit image (also called a hicolor image ) doesnt need a palette to describe its colors; it can display 32,768 colors. A 24-bit image (also called a true color image ) doesnt use palettes because it can display the full spectrum of 16.8 million colors visible to the human eye. Note: Bitmaps with color-depths ranging from 1 bit to 8 bits use palette information to specify the color table of the bitmapped image. Images with higher bit depths dont need palettes. Device-Dependent Bitmaps
161 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Bitmaps that rely on the hardware palette are called device-dependent bitmaps (DDBs). DDBs were the only bitmap format available to Windows programmers before the release of Windows 3.0. DDBs are currently supported mainly for compatibility with older applications written for those early Windows versions. A developer writing a new application, or porting an application written for a previous version of Windows to the Win32 platform, should use DIBs. A DDB is defined by a Windows BITMAP structure and comes in two types: Discardable bitmapsWindows can discard these from memory if the bitmap isnt selected into a device context and system memory is running low. Nondiscardable bitmapsWindows must retain these bitmaps in memory. The BITMAP Structure A DDB is defined by a BITMAP structure that holds the data for a bitmap. The BITMAP structure is defined as follows:
typedef struct tagBITMAP { LONG bmType; LONG bmWidth; LONG bmHeight; LONG bmWidthBytes; WORD bmPlanes; WORD bmBitsPixel; LPVOID bmBits; } BITMAP;

The data members of this structure are described in Table 4.23. Table 4.23 The Data Members of the BITMAP Structure Member bmType bmWidth bmHeight bmWidthBytes bmPlanes bmBitsPixel bmBits Description Specifies the bitmap type. Always set this member to zero. The width, in pixels, of the bitmap. The height, in pixels, of the bitmap. The number of bytes per scan line. Because Windows expects the bit values of a bitmap to form a word-aligned array, this value must be divisible by 2. The number of color planes in the bitmap. The number of bits needed to describe the color of a pixel. A pointer to the array of character values that make up the image data.

Device-Independent Bitmaps (DIBs) Windows 3.0 introduced the device-independent bitmap (DIB), which was designed to
162 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

solve some of the device dependency inherent in DDBs. DIBs are much more useful than DDBs for storing bitmap data in disk files because they contain more useful information about the image. Here are the main features of a DIB: Contains information about the color format of the device on which the DIB image was created. Contains information about the resolution of the device on which the DIB image was created. Contains information about the palette for the device on which the image was created. Contains an array of bits used to map the red, green, blue (RGB) color components of the palette to pixels in the DIB image. Contains a data-compression identifier that indicates which data compression scheme (if any) is used to compact the size of the file on disk. The CBitmap Class The CBitmap class encapsulates GDI bitmaps into a convenient MFC object wrapper that makes it easier to work with bitmaps in MFC than it is in the SDK. Figure 4.18 shows the location of CBitmap in the MFC class hierarchy.

Figure 4.18 The location of the CBitmap class in the MFC class hierarchy. CBitmap Class Methods The CBitmap class provides several class methods that wrap GDI bitmap-related functions. Table 4.24 describes these CBitmap methods. Table 4.24 CBitmap Class Methods Method CreateBitmap() Description

Creates a device-dependent memory bitmap with the specified width, height, and bit pattern. CreateBitmapIndirect() Creates a bitmap with the width, height, and bit pattern defined in a BITMAP structure. CreateCompatibleBitmap() Creates a bitmap compatible with a specified device context. CreateDiscardableBitmap() Creates a discardable bitmap compatible with a specified device context. FromHandle() Gets a pointer to a CBitmap object from a specified Windows bitmap handle (HBITMAP). GetBitmap() Gets a pointer to a specified CBitmap object. GetBitmapBits() Copies a bitmaps bits into a specified buffer. GetBitmapDimension() Gets the width and height of a bitmap that have been set previously by the SetBitmapDimension() method. LoadBitmap() Loads a bitmap from resource data and attaches it to a CBitmap object. LoadMappedBitmap() Loads a bitmap from resource data and maps colors to current system colors.
163 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

LoadOEMBitmap() SetBitmapBits() SetBitmapDimension()

Loads a predefined Windows bitmap and attaches it to a CBitmap object. Sets the bits of a bitmap to the specified bit values. Designates a width and height to a bitmap using 0.1-millimeter units.

Class CBitmap also provides the operator HBITMAP, which returns the underlying Windows handle attached to the CBitmap object. Transferring and Contorting Bitmaps To transfer bitmap data from one place to anotherthat is, from one DC to anotheryou must transfer blocks of bits from one DC to another. This activity is commonly referred to as a bit block transfer (BBT) , or BitBlt (pronounced bit-blit). When transferring bit blocks, you must use a memory DC as a bufferyou cant blit directly to the display. Transferring Bits with BitBlt() The CDC class provides methods to blast bits from one DC to another; the most useful of these is CDC::BitBlt(). The prototype for this method is complex, taking eight parameters as shown here:
BOOL BitBlt( int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, DWORD dwRop); // // // // // upper left corner of destination DC width and height of destination DC the source DC upper left corner of source DC the ternary raster operation code

The parameters for this method are described following: x and yThese values specify the logical coordinates of the upper-left corner of the destination rectangle. nWidth and nHeightThese values specify the logical height of the destination rectangle and source bitmap. pSrcDCPointer to a CDC object that identifies the device context from which the bitmap will be copied. It must be NULL if dwRop specifies a raster operation that does not include a source. xSrc and ySrcThese values specify the logical coordinates of the upper-left corner of the source bitmap. dwRopThis value specifies the ternary raster operation to be performed (refer to Table 4.22, earlier in this chapter). Note: Not all device contexts support bit block transfers. You can test to see whether a DC conforms by calling the CDC::GetDeviceCaps() method, specifying the RASTERCAPS index as the sole parameter, and checking for the presence of the RC_BITBLT bit in the return value. Consider this example:
164 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

if ((dc.GetDeviceCaps(RASTERCAPS) & RC_BITBLT) == 0) { // device doesnt support BitBlt ; } else { // device does support BitBlt ; }

For more information about CDC::GetDeviceCaps() and the index values and bits used, see the MFC documentation for the Win32 API function of the same name. Stretching Bits to Fit with StretchBlt() The CDC class provides a method similar to BitBlt(), but one that stretches or compresses bits to fit a specified destination rectangle: CDC::StretchBlt(). The prototype for this method is even more complex than the one for BitBlt()StretchBlt() takes ten parameters as shown following:
BOOL StretchBlt( int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, DWORD dwRop); // // // // // // // upper left corner of destination DC width and height of destination DC the source DC upper left corner of source DC width of the source bitmap height of the source bitmap the ternary raster operation code

The parameters for this method are the same as those used for BitBlt() with the exception of the two additional nSrcWidth and nSrcHeight values. These two values define the width and height of the source bitmap. Note: To determine how to stretch or compress the bitmap, StretchBlt() uses the current stretching mode of the destination device context. This mode is set using the CDC::SetStretchBltMode() method. Again, not all device contexts support StretchBlt() bit block transfers. You can test to see whether a DC conforms by calling the CDC::GetDeviceCaps() method, specifying the RASTERCAPS index as the sole parameter. As the final step, check the return value for the presence of the RC_STRETCHBLT flag. Consider this example:
if ((dc.GetDeviceCaps(RASTERCAPS) & RC_STRETCHBLT) == 0) { // device doesnt support StretchBlt ; } else { // device does support StretchBlt ; }
165 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Bitmap Resources Windows applications make use of several different kinds of resources. Icons, menus, and bitmaps are some examples of resources. Resources are clusters of binary data tacked on to the end of an applications executable file during the linking process. When Windows fires up a program, it loads into memory only what it needs at that time. Resources typically stay on disk (floppy, hard, or compact) until Windows calls for them. Most resources are discardable read-only data Windows can load or unload from memory at any time as current system memory requirements change. Even Windows itself uses resources to display its message boxes, file copy animations, toolbar bitmaps, and more. Next youll see how to create and use embedded bitmap resources in MFC programs, retrieving them on-the-fly at runtime with the relevant MFC methods. Tacking Resources onto an Executable File So how do you go about putting resources into a Windows executable file? After creating a resource, you must compile it, using a resource compiler, into a special resource file (a binary file with a .RES file extension). The resource file is then linked into your compiled executable to attach the resource data to your program. To tell the resource compiler what to include in the resource file, you must create a resource script: a text file with an .RC file extension that describes the resource. If external files are used (as they are with image resources), these files are named in the script. After you create a bitmap file for the image resource, you must add a statement to your applications resource script to identify it. For resources that reference external files, the syntax generally looks like this:
ImageName IMAGETYPE DISCARDABLE FileName

For example, a resource statement for an icon with the resource identifier IDR_BMP (some integer value) and stored on disk in a bitmap file named MYIMAGE.BMP would look like this:
IDR_BMP BITMAP DISCARDABLE myimage.bmp

Visual C++ does all of this for you automatically when you insert a bitmap resource using the Insert, Resource menu commands. Getting Image Resources out of an Executable File After a resource is embedded into an executable, the real trick is to successfully get it back out and use it. To retrieve resource data for use in your application at runtime, you can use a variety of functions provided by MFC and the Win32 API. The functions you use depend on the type of resource data. For image resources (icons, cursors, and bitmaps), you typically use the LoadImage() Win32 API function. The LoadImage() function prototype is as follows:
HANDLE LoadImage( HINSTANCE hinst, LPCTSTR lpszName, UINT uType, int cxDesired, int cyDesired, // // // // // instance handle containing the image name or identifier of image type of image desired width desired height
166 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

UINT );

fuLoad

// load flags

This function is used in all the sample programs in this chapter to load image resources. As you can see, the function returns a generic handle to your icon, cursor, or bitmap resource. Most of the parameters should look familiar, but some of them need a little explanation: The utype parameter can be any of three macros that specify the type of image to load: IMAGE_BITMAP loads a bitmap, IMAGE_CURSOR loads a cursor, and IMAGE_ICON loads an icon. The fuLoad parameter is any combination of the flags in Table 4.25.

Table 4.25 The LoadImage() Load Flags Value Meaning

LR_DEFAULTCOLOR A default flag that just means not LR_MONOCHROME. LR_CREATEDIBSECTION If the uType parameter specifies IMAGE_BITMAP, the function returns a DIB section bitmap instead of a DCcompatible bitmap (the default). This flag is useful for loading a bitmap without mapping its colors to a display device and gives you access to its palette information. LR_DEFAULTSIZE Uses the width or height specified by the system metric values for cursors and icons if cxDesired or cyDesired are zero. If this flag isnt specified and cxDesired and cyDesired are zero, the resource is loaded at its actual size. If the resource contains multiple images, the size of the first image is used. LR_LOADFROMFILE Loads an image from the file specified by the lpszName parameter. If this flag isnt specified, lpszName is the name of the resource. LR_LOADMAP3DCOLORS Searches the images color table and replaces the various shades of gray with a corresponding Windows or Windows NT 4.0 3D system color. LR_LOADTRANSPARENT Causes all pixels with the same color as the first pixel in the image to become the default window color (COLOR_WINDOW). LR_MONOCHROME Loads an image in black and white. LR_SHARED Shares the image handle if the image is loaded multiple times. If LR_SHARED is not set, each call to LoadImage() for the same resource loads the image again and returns a different handle each time. Almost all the bitmaps you see attached to various Windows dialog boxes, animations, and image lists are bitmap resources. In the BITMAP1 program, the bitmap resources are loaded using the more convenient CBitmap::LoadBitmap() method.
167 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Sample Program: Exploring Bitmap Resources (BITMAP1) Now lets look at a program that performs the remarkable feat of lighting up a bitmapped icon area when the mouse passes over it. The BITMAP1 program makes use of five bitmap resources: one ray-traced image for the background bitmap, two for the unlit versions of the icon bitmaps, and two for the lit versions of the icon bitmaps (see Figure 4.19). The BITMAP1 program is shown in Figure 4.20 at runtime with the BTN1LIT.BMP displayed.

Figure 4.19 The BITMAP1 program uses these four bitmap resources to simulate light-up bitmap areas.

Figure 4.20 The BITMAP1 program at runtime with the BTN1LIT.BMP displayed as the cursor passes over it. The five bitmaps are compiled into a resource file (BITMAP1.RES) by telling the resource compiler about them. This is done in the resource script (BITMAP1.RC) for the program. The bitmap resource IDs are given in the header file RESOURCE.H, which is used by both the resource script (BITMAP1.RC) and the programs sole source file (BITMAP1.CPP). The resulting resource file is linked into the BITMAP1 executable to provide access to the five bitmaps. Examining the BITMAP1 Program Lets take a look at the important sections of code that perform the magic for the program. Listing 4.12 shows the CMainWnd class declaration for the BITMAP1 program. All the important goings-on within the program happen here. Listing 4.12 The Declaration of Class CMainWnd in the BITMAP1 Program
168 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

/////////////////////////////////////////////////////////////////// // Class CMainWnd - derived from MFCs CFrameWnd class CMainWnd : public CFrameWnd { protected: CBitmap m_bmpBack; // resource CBitmap m_bmpBtn1; // resource CBitmap m_bmpBtn1Lit; // resource CBitmap m_bmpBtn2; // resource CBitmap m_bmpBtn2Lit; // resource CRect m_rcTop; CRect m_rcBtm; CRect m_rcClient;

IDR_BMPBACKGROUND IDR_BMPBTN1 IDR_BMPBTN1LIT IDR_BMPBTN2 IDR_BMPBTN2LIT

// rect for drawing // rect for drawing // rect for drawing

void BlitBitmap(CRect& rc, CBitmap& bm); void ShowBitmaps(); public: // Helper method called by MFC virtual BOOL PreCreateWindow(CREATESTRUCT& cs); void PositionRects(); // Message handler afx_msg BOOL OnEraseBkgnd(CDC* pDC); afx_msg void OnMouseMove(UINT nFlags, CPoint point); DECLARE_MESSAGE_MAP(); };

The message map for CMainWnd is as follows:


BEGIN_MESSAGE_MAP(CMainWnd, CFrameWnd) ON_WM_ERASEBKGND() ON_WM_MOUSEMOVE() END_MESSAGE_MAP()

As you can see from the message map macros and the class declaration, the CMainWnd::OnEraseBkGnd() and CMainWnd::OnMouseMove() methods have some special importance. In the CMainWnd::PreCreateWindow() method, the bitmap resources are loaded with several calls to CBitmap::LoadBitmap(), one for each bitmap resource:
// Get the bitmaps from the resource data // m_bmpBack.LoadBitmap(IDR_BMPBACKGROUND); m_bmpBtn1.LoadBitmap(IDR_BMPBTN1); m_bmpBtn1Lit.LoadBitmap(IDR_BMPBTN1LIT); m_bmpBtn2.LoadBitmap(IDR_BMPBTN2); m_bmpBtn2Lit.LoadBitmap(IDR_BMPBTN2LIT);

169 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Now that the bitmap handles are safely stored in the member variables, the CMainWnd::OnEraseBkgnd() method makes use of them to draw the bitmaps on the client area of the window with the CMainWnd::BlitBitmap() method:
PositionRects(); // draw the background bitmap BlitBitmap(m_rcClient, m_bmpBack); // Blit the unlit versions to the screen BlitBitmap(m_rcTop, m_bmpBtn1); BlitBitmap(m_rcBtm, m_bmpBtn2);

The PositionRects() method does just thatpositions the two rectangles in which the four small bitmaps are drawn:
m_rcTop.left = 523; m_rcTop.top = 70; m_rcTop.right = 598; m_rcTop.bottom = 145; m_rcBtm.left = 523; m_rcBtm.top = 168; m_rcBtm.right = 598; m_rcBtm.bottom = 243;

The CMainWnd::BlitBitmap() method is the most important part of the program. It performs the actual work of drawing the bitmaps on the client area and is worth a closer look. The entire method is shown in Listing 4.13. Listing 4.13 The BlitBitmap() Method Handles the Bitmap Drawing Chores for the BITMAP1 Program
/////////////////////////////////////////////////////////////////// // CMainWnd::BlitBitmap() void CMainWnd::BlitBitmap(CRect& rc, CBitmap& bm) { CClientDC dc(this); CDC dcMem; HBITMAP hbmpOld; // Get compatible memory DCs dcMem.CreateCompatibleDC(&dc); // Select bitmaps into the DCs hbmpOld = (HBITMAP)dcMem.SelectObject(bm); // Blit the bitmap to the screen // GetDC()->BitBlt(rc.left, rc.top, rc.right, rc.bottom, &dcMem, 0, 0, SRCCOPY); // Clean up dcMem.SelectObject(hbmpOld); }

The goal here is to put the bitmap data into a memory device context, and then do a bit
170 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

block transfer (bit blit) to move the bitmap image to the display. Note: The positions of the rectangles used for image blitting were determined during the creation of the large bitmap file used for the background bitmap resource. A client device context (DC) is obtained for the frame window, and another device context is created for the memory DC (dcMem). The memory DC creates a memory device context compatible with the display surface by calling CDC::CreateCompatibleDC(). Then the handle to the bitmap you want to draw (the hbm method parameter) is passed to the Win32 API function SelectObject() along with dcMem. This copies the bitmap into the memory device context (which is compatible with the device), displacing the object currently in the DC. A handle to this object (which you store for later cleanup duties) is returned from SelectObject(). You then blast the bitmap bits to the display with a call to the CDC::BitBlt() method. CDC::BitBlt() must know the destination rectangle for the image blit; you use the rc parameter sent to BlitBitmap() to define the destination drawing area. When the new bitmap is safely on the display, you select the original object back into the client DC, to leave things as you found them before the swap. The final piece of the puzzle is to make the icon areas on the window appear to glow when the mouse passes over one of them. The mechanism for displaying the bitmaps has been explained, but the events leading up to the blitting frenzy havent. The controlling factor in all this is the position of the cursor on the display. The CMainWnd::OnMouseMove() method tracks the cursor position at all times. To determine whether to swap bitmaps or not, use the CPoint::PtInRect() method youve seen before. The following code snippet should make it all clear:
void CMainWnd::OnMouseMove(UINT nFlags, CPoint point) { // check to see if the pointer is over a bitmap area // if (m_rcTop.PtInRect(point)) BlitBitmap(m_rcTop, m_hbmpBtn1Lit); // light the top image else if (m_rcBtm.PtInRect(point)) BlitBitmap(m_rcBtm, m_hbmpBtn2Lit); // light the bottom image else // turn the lights off { // Blit the unlit versions to the screen BlitBitmap(m_rcTop, m_hbmpBtn1); BlitBitmap(m_rcBtm, m_hbmpBtn2); } // Call the inherited method CMainFrame::OnMouseMove(nFlags, point); }

There you have ithow to light up bitmaps using embedded bitmap resources! Summary Windows provides a Graphical User Interface (GUI) to the underlying operating system and hardware, and provides hardware information to applications via device contexts:
171 / 1166

MFC with Visual C++ 6 Unleashed and Programming hardware, and provides hardware information to applications via device contexts:

Summary

data structures that are logical representations of physical devices or virtual devices. MFC provides device context classes for general devices (CDC), window client areas (CClientDC), total window area (CWindowDC), and for Windows metafiles (CMetaFileDC). MFC also provides graphic objects that are used to draw on these device contexts. These graphic objects are pens (CPen), brushes (CBrush), fonts (CFont), bitmaps (CBitmap), and regions (CRgn). Vector graphics are mathematical descriptions of graphical imagery, and in this chapter youve looked at the vector graphics methods of the MFC device context classes. Vector images typically consist of lines, polygons, rectangles, regions, polygons, metafiles, fonts, and curves. Youve looked at the TEXTMETRIC structure that holds basic text information as it applies to the display. Youve also seen that fonts are generally defined by a LOGFONT structure that holds information about font construction. Text output methods were presented, as was sample code from the sample program VECTEXT1 located on the companion CDROM. Working with the GDI graphics functions and CDC font and text methods does take some getting used to. This chapter has also provided a look at the usage of bitmaps as resources stored as data within executable files, and along the way youve learned how to retrieve the image data using the Win32 API, and how display and manipulate them. All of this may seem complex at first, but with time, experimentation, and experience, youll be an old hand with graphics and text manipulation in no time!

172 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Chapter 5 Custom Control Development by Ed Harris In This Chapter Window Classes Versus C++ Classes 214 A Validating Edit Control 215 The Clock Static Control 217 The Hyperlink Control 225 Advanced Custom Control Topics 237

Custom controls, or widgets, are useful when the standard input and display controls cannot provide the user with the most optimal input mechanism. Not too long ago, Windows featured a bare-minimum control palettestatic text control, button, edit box, and list box. The combo box, now considered a basic staple of user interface, was a welcome addition to Windows 3. Now, of course, there are dozens of built-in controls, and hundreds of third-party add-ons. This chapter focuses on the whens, whys, and hows of extending stock controls and developing brand new controls of your own. Note: This chapter covers the development techniques needed to create new window behavior. What it doesnt cover is the wisdomand the usability testing required to determine whether new window behavior is warranted. Sure, your new widget may display editable information to the user in a highly efficient manner. However, if the user cant understand how to manipulate the control without breaking out the documentation, your application becomes unusable. Make sure that the user has the proper affordances to use your new widget. (Affordance is GUI usability-speak for intuitive understanding.) Generally, its a good idea not to stray too far from the beaten path. Window Classes Versus C++ Classes One of the confusing aspects of custom control development is the overloaded use of the word class. When Windows was introduced, class was an underused term, and C++ was merely a toy used by AT&T labs. The Windows architects chose to use the term class to represent the collective behavior of a set of windows. Window class attributes include the class name (such as Edit), icon, background color, cursor, and window procedure. Object-oriented languages use the term class in a similar way, to identify the set of behavior that a family of code (the class) provides. These two uses overlap in the area of custom control development because the programmatic class is used to implement the behavior of the window class. The term subclass, in object parlance, means a new class (a child) derived from one or more existing classes. In this naming scheme, the parent class is called the superclass. In the Windows world, subclassing is the action of modifying the behavior of an existing window. Subclassing is done on a window-by-window basis. Superclassing is the act of creating a new breed of window based on the behavior of an existing type (class) of window. As you will see, subclassing is done extensively by MFC. Although superclassing
173 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

is possible with MFC, it has some significant drawbacks. In pure Windows-API based development, window class behavior is provided by the message procedure (message proc). Each message destined for a particular window is sent to a single function, where it is routed to code specific to that message. In the early days of Windows development, message procedures sometimes grew to be thousands of lines long. MFC replaced the message procedure with the concept of a message map. When an MFC-owned window receives a message, the framework looks up the message map for that window, and routes it to the most-derived handler for that message. MFC uses a single window procedure (AfxWndProc) to receive all messages and route them to the appropriate code. When you call CreateWindow, CreateDialog, or any other API that causes a window to be instantiated, you are subclassing that window procedure with AfxWndProc. From that point on, all messages from the system to that window are routed through the message map for processing. This simplifies subclassing enormously and removes the most tedious and error-prone parts of the process. When creating a custom class, consider whether an existing class provides any of the functionality you need. For example, if the class requires textual input, subclassing the standard edit class might be appropriate. Extending an existing class is generally much easier than creating a new one from scratch. A Validating Edit Control Perhaps the seminal subclass example is an edit control that validates or reformats its contents. This control, CZipEdit, will format zip codes into ZIP+4 format when focus leaves the control. The control contains one message map handler, for the WM_KILLFOCUS (OnKillFocus) method.
class CZipEdit : public CEdit { // Implementation public: virtual void FormatContents // Message Map public: //{{AFX_MSG(CZipEdit) afx_msg void OnKillFocus //}}AFX_MSG DECLARE_MESSAGE_MAP() };

(const TCHAR* pszText);

(CWnd* pwndNew);

void CZipEdit::OnKillFocus (CWnd* pwndNew) { CString strText; GetWindowText (strText); FormatContents (strText); CEdit::OnKillFocus (pwndNew); }

The kill focus message handler retrieves the window text, passes it to the FormatContents method, and then invokes the default CEdit kill focus handler. The processing is done prior to the default code so that the new edit control contents (if any) are available to the application when the EN_KILLFOCUS notification is received.
174 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

The function FormatContents is responsible for taking a character string, doing any necessary transformations, and then setting the resulting text into the edit control. It has public scope; this allows it to be used as a formatting replacement for SetWindowText. The implementation for the zip code is rather trivial. It verifies that the first five characters of the zip code are digits, and then inserts a single dash at the fifth character.
void CZipEdit::FormatContents (const TCHAR* pszText) { CString strOutput (pszText); if (strOutput.GetLength() >= 9) { BOOL bValidZip = TRUE; for (int nDigit = 0; nDigit < 5; nDigit++) { if (!isdigit (strOutput[nDigit])) bValidZip = FALSE; } if (bValidZip && strOutput[5] != _T(-)) { if (!ispunct (strOutput[5]) && !isspace (strOutput[5])) strOutput = strOutput.Left (5) + _T(-) + strOutput.Mid (5); else strOutput.SetAt (5, _T(-)); } } SetWindowText (strOutput); }

To convert a dialog box edit control into a formatting edit control, use ClassWizard to bind a CEdit object to an edit control. Then, in your dialog box header file, replace the CEdit object with a CZipEdit object. When MFC binds the control to the window, it subclasses the control and the formatting behavior is enabled. The Clock Static Control The second custom control, CClockCtrl, displays an analog clock or stopwatch face (see Figure 5.1). Because it is a display-only control, accepting neither keyboard nor mouse input, it is derived from CStatic. Patterned on the analog face of the Windows clock accessory application, this control has the following features: The control will shrink or grow to fill the entire window area. The window will contain colored studs at each of the 12 face points. If the control is large enough, each minute mark will have a smaller stud as well. The control will dynamically determine whether the client area is large enough to contain the minute marks. The control will display up to three hands: an hour hand, a minute hand, and a second hand. The hands will be drawn in such a way that there is no discernible flicker if the control is used to display real time. The control will not have any timing built in. The user of the clock will need to set the time as appropriate. Thus, the control can be used as a countdown timer, count-up timer, clock, or static display.
175 / 1166

MFC Programming with Visual C++ 6 Unleashed timer, clock, or static display.

Summary

Figure 5.1 An analog clock face created with the CClockCtrl class. Here is the class definition for the complete clock control:
class CClockCtrl : public CStatic { // Internal constants enum HANDTYPE { HOUR_HAND, MINUTE_HAND, SECOND_HAND }; // Ctor / dtor public: CClockCtrl(); // Members protected: COLORREF m_rgbHands; COLORREF m_rgbPoints; CPoint m_ptMiddle; int m_nPointWidth; int m_nRadius; // of window int m_nHour; int m_nMinute; int m_nSecond; // API public: BOOL CreateFromStatic void SetTime

// // // // //

Hand color Point color Center point of window Width of face point Radius of circular portion

// Hour hand value // Minute hand value // Second hand value

(CWnd* pwndParent, UINT wID); (int nHour, int nMinute, int nSecond);

// Members protected: void RecalcLayout CPoint ComputeFacePoint int nFaceLength) const; void GetHandPoints CPoint* pptHand); void DrawFacePoint BOOL bMajor); void DrawHand HANDTYPE typeHand, BOOL

(void); (int nMinute, (int nFaceValue, HANDTYPE typeHand, (CDC& dc, const CPoint& ptFace, (CDC& dc, int nFaceValue, bDraw);
176 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

// Message Map public: //{{AFX_MSG(CClockCtrl) afx_msg void OnSize afx_msg void OnPaint //}}AFX_MSG DECLARE_MESSAGE_MAP() };

(UINT nType, int cx, int cy); (void);

Control Metrics One of the stated requirements for the clock control is that it grow to fill its window area. Because my trigonometry isnt that good, and because I dont enjoy lopsided clocks, the control squares itself up within the window. All of the metrics are set in a single function, RecalcLayout. For the clock control, you track three items: the center of the clock face, the maximum radius that can be inscribed in the circle, and the face point width. The need for the center point and radius should be reasonably apparent; the point width is the width of one of the hash marks on the outside of the clock.
void CClockCtrl::RecalcLayout (void) { CRect rectClient; GetClientRect (&rectClient); // Square off the control and determine radius m_ptMiddle.x = rectClient.Width() / 2; m_ptMiddle.y = rectClient.Height() / 2; m_nRadius = min (m_ptMiddle.x, m_ptMiddle.y); // Point width is used to determine hash-mark widths m_nPointWidth = (int) m_nRadius / 20; if (m_nPointWidth < 2) m_nPointWidth = 2; Invalidate (TRUE); return; }

To limit the proliferation of math throughout the control, the trigonometric calculations have been contained in a single routine. This routine, ComputeFacePoint, takes a minute value and a face length (generally a fraction of the radius). It returns a CPoint, in client window coordinates, of the specified point. Because the control is being used for time display, it was easier to accept a minute value than an angle in degrees.
CPoint CClockCtrl::ComputeFacePoint (int nMinute, int nFaceLength) const { CPoint ptCalc; // Convert minutes to degrees double fDegrees = 180 + ((15 + nMinute) % 60) * 6; // Begin conversion to radians double fAngle = fDegrees / 180; ptCalc.x = m_ptMiddle.x + (int) (cos (fAngle * pi) * nFaceLength); ptCalc.y = m_ptMiddle.y + (int) (sin (fAngle * pi) * nFaceLength); return (ptCalc); }
177 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Painting the Face With the basic control math out of the way, you can go ahead and draw the surface of the clock. This is done in the OnPaint handler. The painting routine is pretty straightforward: You allocate a device context and brush for the face points, and then inscribe each face point from 12 oclock clockwise through 11 oclock. Each face point is located 90 percent out from the midpoint, using a function DrawFacePoint. DrawFacePoint accepts a device context (DC), the face-point location, and a Boolean indicating whether the point should be drawn in large format (the oclock points) or small format (the minute hashes).
void CClockCtrl::OnPaint (void) { // Force initialized if (m_nRadius == -1) RecalcLayout (); CPoint CPaintDC ptFace; dc (this);

CBrush brPoint (m_rgbPoints); CBrush* pbrOld = dc.SelectObject (&brPoint); // The face points go 90% out from the radius int nFaceLength = MulDiv (m_nRadius, 9, 10); // Inscribe a circle from 12 Oclock clockwise in radians for (int nMinute = 0; nMinute < 60; nMinute++) { ptFace = ComputeFacePoint (nMinute, nFaceLength); DrawFacePoint (dc, ptFace, ((nMinute % 5) == 0) ? TRUE : FALSE); } DrawHand (dc, m_nHour, HOUR_HAND, TRUE); DrawHand (dc, m_nMinute, MINUTE_HAND, TRUE); DrawHand (dc, m_nSecond, SECOND_HAND, TRUE); (void) dc.SelectObject (pbrOld); return; } void CClockCtrl::DrawFacePoint (CDC& dc, const CPoint& ptFace, BOOL bMajor) { CRect rectPoint (ptFace.x, ptFace.y, ptFace.x, ptFace.y); if (bMajor) { rectPoint.InflateRect ((m_nPointWidth / 2) + 1, (m_nPointWidth / 2) + 1); dc.Rectangle (&rectPoint); dc.Draw3dRect (&rectPoint, GetSysColor (COLOR_BTNHIGHLIGHT), GetSysColor (COLOR_BTNSHADOW)); } else { if (m_nPointWidth > 2) { rectPoint.InflateRect (1, 1);
178 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

dc.Draw3dRect (&rectPoint, GetSysColor (COLOR_BTNHIGHLIGHT), GetSysColor (COLOR_BTNSHADOW)); } } return; }

DrawFacePoint uses the previously calculated point width value to determine the width of the hashmarks. The function CDC::Draw3dRect is used to draw a bevel around the hashpoint: a highlight on the upper-left side of the hash and a shadow on the lower-right side. This single function saves dozens of lines of code allocating pens and computing start and stop points for the beveling. Locating the Hands The final drawing-related code for the clock control is the hand-painting scheme. After looking at several clocks, I decided that the hands should be a simple polygon extending from either side of the midpoint. Because the control needs to scale to different sizes, the entire math is done as a multiple of the radius and the point width. The hour hand extends to 50 percent of the radius, the minute hand extends to 70 percent, and the second hand 80 percent. The approach I took in drawing the hour and minute hands leverages the face point location math. Each of the four vertices of the hand is calculated as the hand angle plus a constant. Consider a hand pointing at the 12 oclock position. To draw the hand, you start from the 6 oclock position, a little behind the midpoint. From there, you draw a line to the 3 oclock position, parallel to the midpoint. After that, you go to the 12 oclock position (the actual point of the hand). The last point is at the 9 oclock position (again, parallel to the midpoint). This strategy simplified the hand drawing tremendously. Not to be left out, the second (or sweep) hand is drawn as a thin (single-pixel) line extending out from the midpoint.
void CClockCtrl::GetHandPoints (int nValue, HANDTYPE typeHand, CPoint* pptHand) { int nLength = 0; switch (typeHand) { case HOUR_HAND: nLength = MulDiv (m_nRadius, 50, 100); // 50% of radius // Convert the hour value (0-11) to a minute value (0-59) // for drawing, then adjust for a gradual transition from // hour to hour. nValue *= 5; nValue += (m_nMinute / 12); break; case MINUTE_HAND: nLength = MulDiv (m_nRadius, 70, 100); // 70% of radius break; case SECOND_HAND: nLength = MulDiv (m_nRadius, 80, 100); // 80% of radius
179 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

break; default: ASSERT (FALSE); } if (typeHand == HOUR_HAND || typeHand == MINUTE_HAND) { // Compute the hand points. First point is the back side, // second point is the right, third point is the tip, // and fourth is left. pptHand[0] = ComputeFacePoint (nValue + 30, m_nPointWidth * 2); pptHand[1] = ComputeFacePoint (nValue + 15, m_nPointWidth); pptHand[2] = ComputeFacePoint (nValue, nLength); pptHand[3] = ComputeFacePoint (nValue - 15, m_nPointWidth); } else { pptHand[0] = m_ptMiddle; pptHand[1] = ComputeFacePoint (nValue, nLength); } }

Painting the Hands The first attempt at painting the hands was pretty simplistic. I invalidated the area underneath the hands, and then waited for a WM_PAINT to be generated to update the window. Unfortunately, this caused far too many flickers in the window, as each paint message caused the middle of the face to be filled with the background, and then painted. To remove the flicker, the control needed to take care of both the erasing and redrawing of the hands. For draw operations, the hand color (currently a light blue) is used to fill the hand polygon; for erase operations, the hands are drawn in the background color. The sweep hand is drawn using a NOT XOR raster operation (ROP) code. The NOT XOR ROP causes the hand to be visible as a combination of the line and the existing background. An additional call with this ropcode erases the line. This was key to being able to update the control quickly, as the sweep hand generally moves every second.
void CClockCtrl::DrawHand (CDC& dc, int nValue, HANDTYPE typeHand, BOOL bDraw) { COLORREF rgbBrush; COLORREF rgbPen; CPoint ptHand[4]; if (nValue == HIDE_HAND) return; GetHandPoints (nValue, typeHand, ptHand); if (typeHand == HOUR_HAND || typeHand == MINUTE_HAND) { DrawHand (dc, m_nSecond, SECOND_HAND, FALSE); rgbBrush = (bDraw) ? m_rgbHands : GetSysColor (COLOR_BTNFACE); rgbPen = (bDraw) ? RGB (0, 0, 0) : GetSysColor (COLOR_BTNFACE); CBrush CPen brHand (rgbBrush); penHand (PS_SOLID, 1, rgbPen); = dc.SelectObject (&brHand);
180 / 1166

CBrush* pbrOld

MFC Programming with Visual C++ 6 Unleashed

Summary

CPen*

ppenOld = dc.SelectObject (&penHand);

dc.Polygon (ptHand, 4); (void) dc.SelectObject (pbrOld); (void) dc.SelectObject (ppenOld); DrawHand (dc, m_nSecond, SECOND_HAND, TRUE); } else { int noldROP = dc.SetROP2 (R2_NOTXORPEN); dc.MoveTo (ptHand[0]); dc.LineTo (ptHand[1]); (void) dc.SetROP2 (noldROP); } return; }

Setting the Time The last function in the clock control is used to set the time displayed by the hands. The hour, minute, and second values are set independently of each othereither as a numeric value or as the constant HIDE_HAND. To avoid flicker, remove the old hands, and then redraw the new ones.
void CClockCtrl::SetTime (int nHour, int nMinute, int nSecond) { CClientDC dc (this); // Hour or minute is changing. Erase both if (m_nHour != nHour || m_nMinute != nMinute) { DrawHand (dc, m_nSecond, SECOND_HAND, FALSE); m_nSecond = -1; // Inhibit second hand drawing DrawHand (dc, m_nMinute, MINUTE_HAND, FALSE); DrawHand (dc, m_nHour, HOUR_HAND, FALSE); // Update the internals m_nHour = nHour % 12; m_nMinute = nMinute % 60; DrawHand (dc, m_nHour, HOUR_HAND, TRUE); DrawHand (dc, m_nMinute, MINUTE_HAND, TRUE); m_nSecond = nSecond % 60; DrawHand (dc, m_nSecond, SECOND_HAND, TRUE); } else { DrawHand (dc, m_nSecond, SECOND_HAND, TRUE); m_nSecond = nSecond % 60; DrawHand (dc, m_nSecond, SECOND_HAND, TRUE); } ValidateRect (NULL); }
181 / 1166

MFC } Programming with Visual C++ 6 Unleashed

Summary

Pitfalls of Subclassing Standard Controls One of the problems of subclassing existing controls is that you must understand the default behavior very thoroughly. In some cases, you might need to intercept messages prior to the default processing. In others, you might need to do additional processing after default processing. Finally, in some cases you might need to eat messages entirely, substitute additional ones, or both. The best tool for understanding existing behavior is Spy++. Spy++ displays the messages a window receives and even provides user decodes to common messages. If no close match exists, subclass CWnd instead. When you subclass CWnd, you inherit no behaviors other than those provided by Windows. Its like starting with a blank sheet of paper.

The Hyperlink Control The second custom control is the CHyperlink control. The CHyperlink appears like a static window with underlined text, as shown in Figure 5.2. Unlike a static window, however, the hyperlink control accepts keyboard and mouse input. If the user clicks on the hyperlink or hits Enter while the hyperlink has focus, the text of the link is launched via the shell. The hyperlink control derives directly from CWnd.

Figure 5.2 An example of the CHyperLink control with three fonts. The first step of the custom control process is defining the observable behavior of the control: All text in the control will be displayed with an underlined version of the current font. If the user passes a font to the control through the SetFont() API, the control creates an underlined version. This simplifies using the class because you wont have to make your own underlined copies of fonts. By default, the link is displayed in light blue. The color is not changed if the user clicks on the link (link tracking is left as an exercise for the reader). The user can control the display color programmatically. When the mouse is over the textual part of the control, a hand cursor is displayed. At this point, clicking on the underlined text causes the appropriate registered application to be launched via the shell. If the mouse is not over the textual portion, the arrow cursor is displayed. Because the control accepts mouse input, the Enter key will trigger the hyperlink as well. This implies that the control accepts focus. The parent window of the control will receive a notification after the link is launched. The control will obey left, right, and center justify styles (SS_LEFT, SS_RIGHT, and
182 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

SS_CENTER). For the hyperlink control, youll need to modify stock window behavior for fonts, painting, cursor display, keyboard input, and mouse clicks:
class CHyperlink: public CWnd { public: CHyperlink(); ~CHyperlink(); // Attributes public: COLORREF m_rgbURLColor; CFont m_fontControl; CRect m_rectText; HCURSOR m_hcHand;

// // // //

link display color link display font Text position Cursor for selecting

// User API public: HINSTANCE ExecuteLink (void); inline COLORREF GetURLColor (void) const { return (m_rgbURLColor); } inline void SetURLColor (COLORREF rgb) { m_rgbURLColor = rgb; RecalcLayout(); } // Implementation protected: CFont* GetCorrectFont (void); void RecalcLayout (BOOL bRedraw = TRUE); void SendParentNotify(WORD wID, WORD wNotify) const; // Message Map public: //{{AFX_MSG(CHyperlink) afx_msg LRESULT OnSetFont (WPARAM wParam, LPARAM lParam); afx_msg LRESULT OnSetText (WPARAM wParam, LPARAM lParam); afx_msg void OnSize (UINT nType, int cx, int cy); afx_msg UINT OnGetDlgCode(void); afx_msg void OnPaint (void); afx_msg UINT OnNcHitTest (CPoint ptScreen); afx_msg BOOL OnSetCursor (CWnd* pWnd, UINT nHitTest, UINT message); afx_msg void OnSetFocus (CWnd* pwndOld); afx_msg void OnKillFocus (CWnd* pwndNew); afx_msg void OnLButtonDown(UINT /* nFlags */, CPoint ptMouse); afx_msg void OnLButtonUp (UINT /* nFlags */, CPoint ptMouse); afx_msg void OnCancelMode (void); afx_msg void OnKeyDown (UINT nChar, UINT nRepCnt, UINT nFlags); //}}AFX_MSG DECLARE_MESSAGE_MAP() };

Implementation Strategy Before diving into the messages to process, take a look at the internal design of the class. The overall implementation strategy is to leave as much data and processing as possible to Windows default behavior. For this reason, you do not cache the text of the window, but rather use the GetWindowText API wherever needed. Youll monitor for specific messages that change the controls display: text changes
183 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

(WM_SETTEXT), font changes (WM_SETFONT), and sizing (WM_SIZE). If any of these messages are received, you use a catchall function, RecalcLayout, to recompute display metrics and optionally force the control to repaint. RecalcLayout (re)sets the text bounding rectangle (m_rectText) to the region of the control that contains the text. Note how this example handles the static styles (SS_LEFT, SS_RIGHT, and SS_CENTER) during rectangle calculation.
void CHyperlink::RecalcLayout (BOOL bRedraw) { CString strText; CRect rectClient; CWindowDC dc (this); DWORD dwWindowStyle = GetStyle(); // Compute the size of the text using the current font CFont* pfontControl = GetCorrectFont(); CFont* pfontOld = dc.SelectObject (pfontControl); CSize sizeText = dc.GetTextExtent (strText); dc.SelectObject (pfontOld); GetClientRect (&rectClient); GetWindowText (strText); if (dwWindowStyle & SS_CENTER) { m_rectText.SetRect (rectClient.Width()/2 - sizeText.cx / 2, 0, rectClient.Width()/2 + sizeText.cx / 2, sizeText.cy); } else if (dwWindowStyle & SS_RIGHT) { m_rectText.SetRect (rectClient.right - sizeText.cx, 0, rectClient.right, sizeText.cy); } else // SS_LEFT is equal to zero { m_rectText.SetRect (0, 0, sizeText.cx, sizeText.cy); } if (bRedraw) Invalidate (TRUE); }

Font Processing The hyperlink control should fit into the overall layout of its containing dialog box as gracefully as possible. To do this, the control captures the message WM_SETFONT. Note that MFC does not provide a message-map entry for this message, so the raw-form ON_MESSAGE (WM_SETFONT, OnSetFont) syntax is used. Upon receipt of WM_SETFONT, the control retrieves the font characteristics, adds the underline style, and creates a new font. If the WM_SETFONT message is not received by the control, the control synthesizes a font from the font of its parent window.
CFont* CHyperlink::GetCorrectFont (void) { // If the internal font is valid, skip processing if (m_fontControl.GetSafeHandle() == 0) { // Get parent window font for modification CWnd* pwndParent = GetParent();
184 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

if (pwndParent) { LOGFONT logFont; CFont* pfontParent = pwndParent->GetFont(); pfontParent->GetObject (sizeof (logFont),&logFont); logFont.lfWidth = 0; logFont.lfQuality = PROOF_QUALITY; logFont.lfUnderline = TRUE; m_fontControl.CreateFontIndirect (&logFont); } } return (&m_fontControl); } LRESULT CHyperlink::OnSetFont (WPARAM wParam, LPARAM lParam) { LOGFONT logFont; HFONT hFont = (HFONT) wParam; Default(); // Pass message to default handler CFont* pfontDisplay = CFont::FromHandle (hFont); pfontDisplay->GetObject (sizeof (logFont), &logFont); logFont.lfUnderline = TRUE; logFont.lfWidth = 0; logFont.lfQuality = PROOF_QUALITY; m_fontControl.DeleteObject(); VERIFY (m_fontControl.CreateFontIndirect (&logFont)); RecalcLayout(); return (0L); }

Notice how the RecalcLayout function is used to reset control metrics prior to exiting the WM_SETFONT message handler.

Painting the Window Given the bounding rectangle for the text and the font, painting the control is rather trivial. The control allocates a paint DC, sets the font and text color, paints the text with a single DrawText call, and then resets the DC settings. When the control has the focus, DrawFocusRect is used to display a dotted rectangle around the text:
void CHyperlink::OnPaint() { CString strText; CPaintDC dc (this); // If we presently have the focus, remove it prior to painting if (GetFocus() == this) dc.DrawFocusRect (m_rectText);
185 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

if (m_rectText.IsRectNull()) RecalcLayout (FALSE); GetWindowText (strText); CFont* pfontControl = GetCorrectFont(); CFont* pfontOld = dc.SelectObject (pfontControl); COLORREF rgbOldBk = dc.SetBkColor (GetSysColor (COLOR_BTNFACE)); COLORREF rgbOldText = dc.SetTextColor (m_rgbURLColor); CRect rectClient; GetClientRect (rectClient); CBrush brushBack (GetSysColor (COLOR_BTNFACE)); dc.FillRect (&rectClient, &brushBack); // Draw the text using the bounding rectangle from DrawText dc.DrawText (strText, m_rectText, DT_NOPREFIX|DT_SINGLELINE); dc.SetBkColor (rgbOldBk); dc.SetTextColor (rgbOldText); dc.SelectObject (pfontOld); // If we have the focus, draw the standard focus rectangle // around the text if (GetFocus() == this) dc.DrawFocusRect (m_rectText); return; } void CHyperlink::OnSetFocus (CWnd* pwndOld) { CWindowDC dc(this); dc.DrawFocusRect (m_rectText); } void CHyperlink::OnKillFocus (CWnd* pwndOld) { CWindowDC dc(this); dc.DrawFocusRect (m_rectText); }

To complete focus processing, catch the WM_SETFOCUS (OnSetFocus) and WM_KILLFOCUS (OnKillFocus) messages. As in the OnPaint handler, these message handlers invoke the DrawFocusRect to paint a highlight around the hyperlink text. The DrawFocusRect API uses an XOR drawing scheme, so the second call erases the rectangle drawn by the first one. Be careful if your subclass relies on the default paint handler (that is, your class lets the control draw itself, and then overlays on top of the existing control). CPaintDC uses the Win32 BeginPaint and EndPaint APIs to allocate and release the device context. The EndPaint API has the normally beneficial side effect of revalidating the paint area. When your control allocates another CPaintDC, the drawing (clipping) area will be empty, and none of your output will appear. To overlay on top of an existing controls output, use a CWindowDC instead. By default, the clipping area of a CWindowDC is the complete area of the window. Controlling the Cursor In most cases, the text of the hyperlink does not occupy the entire area of the window. When the cursor is over the text, you would like to display the traditional hand cursor that users associate with a hyperlink. When the cursor is over a whitespace area of the
186 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

control, you display the arrow. To control the cursor, you catch and respond to the WM_SETCURSOR (OnSetCursor) message. WM_SETCURSOR is passed to a window whenever a mouse event (move, click, and so on) happens over the client area of the window, or when the window has set input capture. The WM_SETCURSOR message includes the window under the cursor, the mouse location in screen coordinates, and the mouse message being processed. The handler converts the mouse coordinates from screen to client coordinates and compares it to the bounding rectangle of the hyperlink text. If the point is inside the text rectangle, the hand cursor is set; otherwise, the default (arrow) cursor is displayed.
BOOL CHyperlink::OnSetCursor (CWnd* pWnd, UINT nHitTest, UINT message) { CPoint ptMouse; GetCursorPos (&ptMouse); ScreenToClient (&ptMouse); if (pWnd == this && m_rectText.PtInRect (ptMouse)) { // If cursor has been loaded, dont re-load. if (m_hcHand == (HCURSOR) 0) { m_hcHand = LoadCursor (AfxGetInstanceHandle(), MAKEINTRESOURCE (IDC_HANDCURSOR)); // If you get this assert, you forgot to include // IDC_HANDCURSOR in your resources. ASSERT (m_hcHand); } ::SetCursor (m_hcHand); return (TRUE); } return (CWnd::OnSetCursor (pWnd, nHitTest, message)); }

After the WM_SETCURSOR handler was added, the initial version of the hyperlink control did not display the hand cursor. Some investigation with Spy++ revealed that the default window handler for WM_NCHITTEST does not specify that the window has a client area. Windows sends a message, WM_NCHITTEST (non-client hit test), whenever the mouse is over any area of a window. The purpose of the message is to allow the window to identify the specific region under the mouse. In other words, the window can return that the mouse is over a border, menu or title bar, close or size box, and so on. Windows handles the cursor display (and suppresses WM_SETCURSOR messages) unless the return from WM_NCHITTEST indicates that the mouse is over the client area of the window. To solve the problem, the control catches the WM_NCHITTEST message. If the mouse cursor is over the hyperlink text, HTCLIENT is returned. This informs Windows of the boundaries of the client area and causes the mouse cursor to display properly.
UINT CHyperlink::OnNcHitTest (CPoint ptScreen) { CRect rectClient; CPoint ptClient (ptScreen); GetClientRect (rectClient);
187 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

ScreenToClient (&ptClient); if (rectClient.PtInRect (ptClient)) return (HTCLIENT); else return (CWnd::OnNcHitTest (ptScreen)); }

Mouse Input Mouse handling for the hyperlink control follows the pushbutton model. When the mouse goes down in a pushbutton, the face of the button is depressed; however, if the mouse is moved outside of the button, the face returns to its normal position. The button action isnt taken unless the mouse is released over the button face. Although the appearance of the hyperlink isnt altered when the mouse is clicked, the program does wait until receiving a mouse-up before launching the hyperlink. To accomplish this, you need to ensure that you receive a mouse-up event (WM_LBUTTONUP) to match the mouse-down event (WM_LBUTTONDOWN). This is done through the SetCapture() API. Normally, input goes to the window under the cursor (for mouse), or the window with focus (keyboard). After SetCapture is invoked, all mouse and keyboard inputs for the process are routed directly to the capturing window. Only one window in a process can have capture at any given time. Setting capture on the mouse-down event ensures that the control will receive the mouseup event, regardless of where it occurs. If the user clicks down on another window and clicks up in yours, the hyperlink will not launch.
void CHyperlink::OnLButtonDown(UINT /* nFlags */, CPoint ptMouse) { if (m_rectText.PtInRect (ptMouse)) { // Make us the focus window. if (GetFocus() != this && (GetStyle() & WS_TABSTOP)) SetFocus(); SetCapture(); } return; } void CHyperlink::OnLButtonUp(UINT /* nFlags */, CPoint ptMouse) { // If capture is set to us, then the mouse went down // over our window if (GetCapture() == this) { ReleaseCapture(); // Verify that the user didnt mouse up or another window if (m_rectText.PtInRect (ptMouse)) { ExecuteLink (); SendParentNotify ((WORD) ::GetWindowLong (GetSafeHwnd(), GWL_ID), HL_LINKCLICKED); } }
188 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

return; } void CHyperlink::SendParentNotify (WORD wID, WORD wNotify) const { HWND hwParent = GetParent()->GetSafeHwnd(); ::SendMessage (hwParent, WM_COMMAND, MAKEWPARAM (wID, wNotify), (LPARAM) GetSafeHwnd()); } void CHyperlink::OnCancelMode (void) { if (GetCapture() == this) ReleaseCapture(); CWnd::OnCancelMode(); return; }

Besides the mouse-down and mouse-up events, you also catch and process OnCancelMode (WM_CANCELMODE). Windows sends this message to any window that captured input through SetCapture when a new window must be displayed. For example, if another application is becoming active due to a system keypress or a message box that is being displayed, the Window manager sends the window capturing input a WM_CANCELMODE. This allows the window to cancel capture and release any other allocated resources (such as timers). Keyboard Input One of the original requirements for the hyperlink control was that it accept keyboard input. This enables the user to tab to and from the hyperlink, much as he would with any other editable control in a dialog box. Several actions are necessary to accomplish this feat. Normal keyboard input is processed by catching the WM_KEYDOWN (OnKeyDown), WM_KEYUP (OnKeyUp), and WM_CHAR (OnChar) messages. Windows sends a keydown message when a key is pressed on the keyboard, and sends a key-up message when a key is released. Key-up and key-down messages are sent for all keystrokesnot only alphanumeric characters, but also Shift keys, Control keys, Caps Lock, and so on. If your control is interested only in the resulting keypress, use the WM_CHAR message. WM_CHAR messages are generated from multiple WM_KEYDOWN messages. For example, to type a capital A, you hold down the Shift key and press the A key. Your application will receive a WM_KEYDOWN for both the Shift key (VK_SHIFT) and the A key. Finally, a WM_CHAR message is generated specifying the resulting A keypress. After the WM_CHAR, WM_KEYUP messages are generated for both the A and Shift keys. In general, custom controls that accept input, like edit controls, need a mixture of keydown/key-up messages and WM_CHAR messages. The hyperlink control needs only the Enter key. When this key is pressed, the hyperlink will launch.
189 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Unfortunately, the Enter key is a very special key to Windows. By default, this keypress is intercepted by the dialog manager and routed to the default button in a dialog box. Other keys intercepted include the Tab key (changes focus through the controls of a dialog box), the arrow keys (change the current control within a group), and mnemonics (used with the Alt key to jump to a specific control). To receive the Enter key (or any of the other special keys), you need to jump through some hoops. The dialog manager is willing to concede these keys on an as-needed basis. To determine which keys a control requires, the dialog manager sends a WM_GETDLGCODE (OnGetDlgCode) message prior to sending a keyboard message. The return from the recipient window controls whether a specific keypress is intercepted or not. Besides values for stock controls (buttons, edit controls, or static text controls), the following return values can be combined to make a valid WM_GETDLGCODE response: WM_GETDLGCODE Constant DLGC_WANTALLKEYS DLGC_WANTARROWS DLGC_WANTCHARS DLGC_WANTMESSAGE DLGC_WANTTAB Description Window receives all WM_KEYDOWN/WM_KEYUP messages. Window receives non-intercepted messages plus arrow keys. Window receives all WM_CHAR messages. Window receives each keyboard message to decide on a message-by-message basis. Window receives non-intercepted messages plus Tab messages.

Conspicuously absent from the list is a code to receive just the Enter key. Experimenting with the dialog code for default pushbuttons yielded no results, so the window is stuck with the catchall DLGC_WANTALLKEYS. If your control receives all keyboard messages, you are on your own to handle navigation commands. That is, instead of relying on the dialog manager to process tabs, arrow keys, and Esc, you must handle them yourselves. This is reasonably simple to do. When the hyperlink control receives the Enter key (VK_RETURN), the link is launched, and a notification is sent to the parent window. In response to the Esc key, the control masquerades as the Cancel button (IDCANCEL) and sends a button-clicked message to its parent window. Finally, if the Tab key is pressed, focus is moved forward or backward through any sibling windows to find the next one with the tabstop style set. This mimics the standard behavior of the dialog manager.
UINT CHyperlink::OnGetDlgCode(void) { return (DLGC_WANTALLKEYS); } void CHyperlink::OnKeyDown (UINT nChar, UINT nRepCnt, UINT nFlags) { if (nChar == VK_RETURN) { ExecuteLink (); SendParentNotify ((WORD) ::GetWindowLong (GetSafeHwnd(), GWL_ID), HL_LINKCLICKED); } else if (nChar == VK_ESCAPE)
190 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

{ SendParentNotify (IDCANCEL, BN_CLICKED); } else if (nChar == VK_TAB) { CWnd* pwndFocus = this; BOOL bForwardTab = (GetKeyState (VK_SHIFT) & 0x8000) ? FALSE : TRUE; // Find the next or previous window which has the // tabstop bit set. do { pwndFocus = pwndFocus->GetWindow (((bForwardTab) ? GW_HWNDNEXT : GW_HWNDPREV)); if (!pwndFocus) pwndFocus = GetWindow (((bForwardTab) ? GW_HWNDFIRST : GW_HWNDLAST)); } while (pwndFocus != this && (!pwndFocus->IsWindowEnabled() && !(::GetWindowLong (pwndFocus->GetSafeHwnd(), GWL_STYLE) & WS_TABSTOP))); if (pwndFocus) pwndFocus->SetFocus (); } else CWnd::OnKeyDown (nChar, nRepCnt, nFlags); }

Launching the Link To launch the hyperlink, the hyperlink control retrieves the current window text and passes it to the Windows shell for execution, with the open opcode. So far, Ive tested the hyperlink with http and ftp links, the mailto: command, and local document files (C:\My Documents\Readme.wri, for example). Any filename registered as a shell extension will work. A logical improvement would be to display one set of text, while executing another this would hide ugly or complex URLs and present the user with a comprehensible interface.
HINSTANCE CHyperlink::ExecuteLink (void) { HINSTANCE hInstReturn; CString strText; GetWindowText (strText); hInstReturn = ShellExecute(GetSafeHwnd(), _T(open), strText, NULL, NULL, SW_SHOWNORMAL); return (hInstReturn); }
191 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Its quite a bit of code for a seemingly simple class.<

Advanced Custom Control Topics Although the clock and hyperlink static controls demonstrate the basics of custom control design, there are some additional techniques that can be useful. One is a technique for accessing Windows messages during the creation of a custom control. I also present various methods for sending notifications from a custom control to its parent window in this section. This section also includes a discussion of how to use a custom control with the Visual Studio Resource Editor. Subclassing Limitations Choosing an existing window class has the advantage of overcoming some significant MFC problems dealing with Window creation and initialization. The MFC message-map paradigm has some severe limitations in creating custom controls. The biggest problem is initialization. Normally, newly created windows are initialized by catching the WM_CREATE message. At this point, the window has text, size, and positioning information. When embedded in dialog boxes, however, MFC window classes do not always get a chance to process WM_CREATE messages. When a dialog box is created, the dialog manager creates the new dialog window, and then creates any child windows in the dialog template. MFC can subclass the dialog box during its creation; however, the child windows are created without subclassing. The soonest the child windows can be subclassed is during the WM_INITDIALOG (OnInitDialog) message. However, by that time, the new child windows have all received their WM_CREATE, WM_SIZE, and several other important messages. Of course, if you create your window directly, rather than from a dialog template, you will get WM_CREATE and the other early messages. A common technique for controls that require the early messages is to use a placeholder window, typically a static. The static is placed on the dialog template with the same position, size, style, and ID as the target window. Then, a member function in the target window class is called to gather the information from the static, destroy it, and create the actual window.
BOOL CHyperlink::CreateFromStatic(CWnd* pwndParent, UINT wID) { BOOL bSuccess = FALSE; CWnd* pwndStatic = pwndParent->GetDlgItem (wID); if (pwndStatic != (CWnd*) 0) { DWORD dwStyle = pwnd->GetStyle(); CString strText; CRect rect; // Get the window text pwndStatic->GetWindowText (strText); // Get the window position and convert from screen -> client pwndStatic->GetWindowRect (rect); pwndParent->ScreenToClient(rect); pwndStatic->DestroyWindow();
192 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

bSuccess = CWnd::Create (NULL, strText, dwStyle, rect, pwndParent, wID, NULL); } return (bSuccess); }

This is a point that causes outrage among Windows API developers. The non-MFC mechanism for creating a class is to register a window class (with a window procedure), and then create windows of that class by embedding them on a dialog box. Why doesnt MFC support registering window classes and doing the same thing? MFC does in fact support window class registration; however, after youve registered the class, its difficult to do anything with it. If you register a custom window procedure, you can handle early messages, but you lose the ability to use message maps and to associate instance-specific data (such as the members of a class object in memory) with a window handle. That negates most of the advantages of using MFC for window class development in the first place. If you use AfxWndProc, you get the message mapping, but no messages until the window is subclassed. This isnt an obvious point. In fact, each message sent to the window is received by AfxWndProc, but no message mapping can exist until somebody associates a class with the window. After the class is attached, messages are routed to the proper handlers. But you cant really attach to the window until your dialog gains control (generally WM_INITDIALOG), and at that point the early messages have been received and discarded. Notifications Controls that accept input need to be able to communicate with their parent windows when the user acts. Well-known examples of this include the EN_CHANGE notification, sent by edit controls when the contents change, and the BN_CLICKED message sent by pushbuttons when activated. Most built-in window classes use notifications of this ilk; they send a WM_COMMAND message to their parent, along with the control ID, the message code, and their window handle. The WPARAM contains the control ID and message code in the low and high words, respectively. The LPARAM contains the window handle (HWND, not CWnd) of the sending control. Of course, this use of message parameters leaves no mechanism to send details. It is incumbent on the parent window to query for details if needed. The SendParentNotify function of the CHyperlink control automates sending these kinds of notifications. If you need to send back detailed parameters, consider sending a message in the WM_USER range to the parent. That way, you have the WPARAM and LPARAM parameters available for data (or to act as pointers). Better still, use the WM_NOTIFY message. The more recent common controls (property sheet, tree control, and so on) use WM_NOTIFY to send data to their parent windows. WM_NOTIFY messages contain the window handle of the sending control in the WPARAM, and a pointer to a structure in the LPARAM. The structure varies from control to control, but all start with an NMHDR.
193 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

For the ultimate in notification, use a callback interface. Allow the parent window (or delegate) to register itself with the control via an interface class; when the control needs to send details back to the parent, it invokes one or more functions in the callback interface. Of course, using this mechanism makes your control unusable by non-C++ applications. Using the Resource Editor with Custom Classes The Visual Studio Resource Editor gives you the ability to place unknown window classes into a dialog template. To do this, you must know the registered class name and the style bits appropriate for the class. The style bits are set as a 32-bit hexadecimal number (no constants)so be prepared to do some math. Reevaluating the style bits causes no end of grief during maintenance. There isnt a place to put any comments into the dialog template, and who can remember that 010830000 is equivalent to WS_TABSTOP, WS_GROUP, WS_BORDER, and WS_VISIBLE? Its far easier to create the controls during OnInitDialog processing, using CWnd::Create and symbolic constants. Summary Creating a custom control can be a very simple process or an extremely complex one. Start by documenting the observable behavior of the control: response to input, output formats, and the effect of any style flags. If you can, base it off an existing control, such as an edit control or combo box. These controls have years of testing and improvement behind them. If you must start from scratch, map from observable behavior to Windows messages. Which messages do you need to capture, and what does each signal to your control? What are the different states (such as capture) that the control supports? Be sure to test your control in a variety of scenarios. For example, make sure that the control works both in a dialog box and on a standalone basis. Change the Windows color scheme to some absurd setting to ensure that the control paints properly. Check that the control resizes properly under very high and very low display resolutions. Ensure that the control creation strategy is valid. Does the control require a ::CreateFromStatic member (or similar), or can it be subclassed from an existing dialog control?

194 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Chapter 6 The MFC Application Object, Message Routing, and Idle Processing by David Lowndes In This Chapter The MFC Application Object 242 Message Routing, Message Maps, and Message Categories 259 Idle Processing 266 The Splash Screen Component 270

In this chapter, you will learn about the CWinApp object. In doing so, I hope to encourage you to examine the MFC source code and discover what happens beneath the surface of your MFC application. The MFC Application Object The CWinApp class is the starting point of your MFC application. It controls the initialization and startup operations, runs the main message loop, and handles shutdown. CWinApp can handle command messages in the same way as your view and document classes, and it contains several important public member variables that set the help file, the root registry key, and other system-wide settings for your application. A CWinApp object never appears directly in your application; instead, it is the base class of your application object. This is a global object named theApp, created by AppWizard in your main application source file. If you use AppWizard to create a project named MyProject, your application class is named CMyProjectApp and the theApp object is declared in MyProject.cpp. Because it is a global variable, this object is instantiated along with any other global C++ objects as part of the runtime startup code. You can access it from anywhere in your source code using the AfxGetApp function like this:
CMyApp * pApp = static_cast<CMyApp*>( AfxGetApp() );

Because the object is a global variable, you might wonder why MFC exposes it this way rather than providing an extern definition in a header file. In the context of your executable, you could just as well access the object directly, but in the context of an MFC DLL, it is significant. Listing 6.1, a code snippet of an exported function in an MFC DLL, illustrates the point. Listing 6.1 AfxGetApp and Application Module State
BOOL PASCAL ExportedFunction1( ) { CWinApp * pApp = AfxGetApp(); AFX_MANAGE_STATE(AfxGetStaticModuleState()); pApp = AfxGetApp();

The returned pApp object pointer from the first call to AfxGetApp is the calling applications CWinApp object. The result of the second call to AfxGetApp is the DLLs CWinApp object. This apparent switch is accomplished by the AFX_MANAGE_STATE macro. It constructs an object that chains a module state to the calling applications module data, so that in this example, the DLL module state (returned by the call to AfxGetStaticModuleState) is linked to the application. Theres no need to remember to reset these state variables; its done
195 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

automatically by the objects destructor when the function exits. Its common to use AFX_MANAGE_STATE in any exported MFC DLL functions where you want the program to use a resource from the DLL. If you are writing an EXE application, you can add the following extern definition to your application objects header file and access the global theApp object directly in any source file that includes the header file:
extern CMyProjectApp theApp;

CWinApp and Application Lifetime As mentioned previously, the CWinApp-derived object is a global variable, and therefore exists for the lifetime of your program. The class member variables are initialized by the classs constructor code. Because the object is global, the constructor is called by the compilers runtime startup code, and therefore all the default values are set before the main part of your program code executes. After global variables and other aspects of the C runtime support are initialized, your program code executes and is essentially as straightforward as the pseudocode in Listing 6.2. Listing 6.2 Program Execution Sequence
if ( InitInstance() ) { Run; } else { Destroy the main window if there is one; } ExitInstance;

In practice, if you examine the MFC source code in WinMain.cpp, youll find that its a little more involved, but this simple representation expresses the general idea. Its rare that youll need to know about or override the default implementations of Run or ExitInstance, but its almost mandatory for you to modify the boilerplate code for your applications InitInstance. InitInstance InitInstance performs all the application-specific initialization. Ill cover this in depth later in the chapter (see InitInstanceApplication-Specific Initialization). Run Run is the heart of your application and is where your application spends the vast majority of its processing life. This function is a loop that retrieves and processes messages from your applications message queue. If there are no messages in the queue, it calls the OnIdle function. If there is a message, and its not WM_QUIT, it is dispatched into the MFC message processing code. If the message is WM_QUIT, the loop ends and the function returns. You can think of the Run function as something like the pseudocode in Listing 6.3.
196 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Listing 6.3 Run() Pseudocode


for (;;) { MSG msg; while ( !PeekMessage( &msg, ...) ) { OnIdle(); } if ( msg.message == WM_QUIT ) { return ExitInstance(); } else { DispatchMessage( &msg ); } }

Ill discuss the path that dispatched messages take later on in this chapter under the Message Routing topic. ExitInstance Youre only likely to need to override the ExitInstance member function if you need to free some special allocated resource that your application used. The return value from ExitInstance is the exit code value that your application returns on termination. The normal convention is to return zero if the application shuts down normally, and any other value to indicate an error. Tip: If you override ExitInstance, be sure to call the base class function because it saves your application Registry settings and performs necessary cleanup of resources used by MFC. OnIdle Override the OnIdle function if you need to perform background processing in your application. OnIdle is covered in more depth under Idle Processing later in the chapter. The CWinApp Data Members The CWinApp data members are documented in the Visual C++ help, but the help doesnt give any examples of typical values that these variables take. Ive included a few examples of those public member variables that you might find useful in your own programs. Note: Some of these members are actually declared in CWinThread (which is the base class of CWinApp), but for now, you can regard them as being part of CWinApp.

197 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

m_pszAppName The m_pszAppName variable contains the name of the application as a string, for example, MyProject. MFC displays this string for the caption bar text if you use AfxMessageBox. If you look at the help for the CWinApp constructor, youll find that it accepts a string parameter. In the boilerplate code generated by AppWizard, this is NULL. This NULL value instructs MFC to use the AFX_IDS_APP_TITLE string resource to initialize the m_pszAppName member. However, if you want to, you can modify the line of code that constructs this object to set this variable this way:
CMyProjectApp::CMyProjectApp() : CWinApp(I want to set this string as the application name) { }

m_hInstance Although the Visual C++ help describes m_hInstance as the current instance of the application, the concept of an instance handle is a relic from 16-bit Windows, and it doesnt exist as such under Win32. It is better described as a module handle because its actually the load address of the module in the processs address space. For a default Visual C++ project, an EXE module will have the value 0x00400000, whereas a DLL would have 0x10000000. The linker assigns these values. You can override them from your projects Link settings. Note: The EXE module values can be different at runtime because the Win32 loader can relocate a module if the default address space is already in use by another module. m_hPrevInstance m_hPrevInstance is a relic from 16-bit Windows applications, where it is used to indicate the previous instance handle of a running application. Under Win32, it is always NULL. Under 16-bit Windows, you could use this variable to restrict an application to a single instance. Under Win32, you can achieve the same result using a mutex, as illustrated in Listing 6.4. Listing 6.4 Using a Mutex to Limit an Application to a Single Instance
BOOL CDlgApp::InitInstance() { bool bAlreadyRunning; HANDLE hMutexOneInstance = CreateMutex( NULL, TRUE, 18A5330D_9DCA_11D2_9847_006052028C2E ); bAlreadyRunning = ( GetLastError() == ERROR_ALREADY_EXISTS ); if ( hMutexOneInstance ) { ReleaseMutex( hMutexOneInstance ); } if ( bAlreadyRunning )
198 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

{ AfxMessageBox( Already running ); return FALSE; } ...

The CreateMutex call is an atomic operation and guarantees that this operation is failsafe. Note: The string Ive used in CreateMutex is a GUID generated by the MFC AppWizard in the header file for my project. If you use this technique, be sure to use your own guaranteed unique string from your own projects header file. m_lpCmdLine m_lpCmdLine is a pointer to the null-terminated command-line string. This string is the portion of the command line after the executable name. There is no additional processing of the command line, so all white space characters between parameters are intact. For example, if you ran the program from the following command line
>C:\Tests\MyApp -a -b c:\Tests\filename.ext

this member would be -a -b c:\Tests\filename.ext. TIP: You might find the m_lpCmdLine member difficult to use, but dont forget that you can always make use of the global C runtime __argc and __argv facilities to handle command-line parameters in the same easy way that you could if you were writing a console application. The __argc and __argv variables are global and can be accessed anywhere in your program. m_nCmdShow When the shell or another application starts your program, it passes a parameter to suggest the initial show state of the main frame window. This value is eventually passed to the ShowWindow API by way of the m_nCmdShow member variable. The m_nCmdShow variable takes on different values depending on how you invoked your application. For example, if you start your application by double-clicking the executable in Explorer, the value in m_nCmdShow will instruct ShowWindow to display the window in its visible, nonmaximized state. However, if you invoke the application through a shortcut, you can use the shortcuts property page to specify the initial window state as Normal, Maximized, or Minimized. m_bHelpMode m_bHelpMode indicates whether the application is in help context mode (typically invoked when you press Shift+F1). m_pActiveWnd m_pActiveWnd is used as a pointer to the main window of the container application when an OLE server is in-place active. It is NULL if the application is not in-place activated.
199 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

m_pMainWnd m_pMainWnd is used to store a pointer to the applications top-level window. MFC terminates the application when this window is closed. A consequence of this is discussed under the topic Dialog Application later in this chapter. m_pszExeName m_pszExeName is the module name of the application, for example, MYPROJECTAPP. m_pszHelpFilePath m_pszHelpFilePath is the full path to the applications Help file, for example, C:\SAMPLES\MYPROJECT\MYPROJECTAPP.HLP. m_pszProfileName m_pszProfileName is the applications INI filename or Registry key name. Initially, this is an INI filename, such as MYPROJECTAPP.INI. If your application calls SetRegistryKey, this variable becomes a duplicate of the m_pszAppName variable (MyProjectApp). If you prefer your application to store its settings in an INI file rather than the Registry, remove the call to SetRegistryKey in your applications InitInstance. m_pszRegistryKey The m_pszRegistryKey variable is the name of the Registry key for your applications settings. It is set up in the SetRegistryKey function. If you examine your applications InitInstance function, youll see that it includes a call to SetRegistryKey with a fixed string. The code comment inserted by AppWizard rightly suggests that you should set this string to your company name. Internally, MFC concatenates this string with the m_pszProfileName variable to form the Registry key for your application. For example, if you set this value to MyCompanyName, your applications Registry key is Software\MyCompanyName\MyProjectApp. This is used by the CWinApp registry routines such as GetAppRegistryKey, GetProfileInt, GetProfileString, GetProfileBinary, WriteProfileInt, WriteProfileString, and WriteProfileBinary. Note: Theres an alternative form of the SetRegistryKey function that loads this string from a resource:
void SetRegistryKey(UINT nIDRegistryKey);

You might find this form useful if you produce a program that is resold to OEMs who need to rebadge the program with their own application name. Placing the string in the resource file means that you wont have to modify the source code to accommodate this requirement.

The CWinApp Member Functions


200 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

CWinApp includes many member functions that are small wrappers, such as the LoadCursor and LoadIcon routines. These are not of any great interest except to note that they make use of AfxFindResourceHandle, which enables your program to access resources from within the EXE or MFC extension DLLs. Run, InitInstance, and ExitInstance, are actually members of CWinThread, but for this discussion, you can assume they belong to CWinApp. Perhaps the most significant member function of CWinApp is the one that performs the application-specific initializationInitInstance. Unlike most other functions in CWinApp, the AppWizard generates different code for this function depending on the type of application and the options you chose. I will now cover the InitInstance code for the three most common types of MFC applications generated with the default AppWizard options. InitInstanceApplication-Specific Initialization The code examples in the following sections show the boilerplate code generated by the AppWizard for three types of MFC applications: Listing 6.5 shows a dialog application, Listing 6.6 an SDI doc/view application, and Listing 6.7 an MDI doc/view application. For clarity, and to emphasize the important aspects, Ive removed the comments and the less significant lines of code. Listing 6.5 A Dialog Applications InitInstance Code
BOOL CDlgApp::InitInstance() { AfxEnableControlContainer(); CDlgDlg dlg; m_pMainWnd = &dlg; int nResponse = dlg.DoModal(); if (nResponse == IDOK) { } else if (nResponse == IDCANCEL) { } return FALSE; }

The dialog application is quite straightforward. After calling AfxEnableControlContainer to enable ActiveX container support, the application consists of a modal dialog box. Note: One point to note in the dialog application is that it returns FALSE from InitInstance. If you refer back to the Program Execution Sequence shown earlier in Listing 6.2, youll see that this bypasses the Run message-processing loop and terminates the application. Note also that the m_pMainWnd variable is assigned to the modal dialog. This has the side effect of causing MFC to generate a WM_QUIT message when the dialog closes. This in turn gives rise to a common problem. If you want to use another dialog or a message box after the main dialog has closed, these subsequent windows might briefly appear and promptly close. Microsofts Knowledge Base article Q138681 gives the answer and suggests either removing the line of code that assigns m_pMainWnd in InitInstance, or setting the m_pMainWnd member to NULL in the dialogs
201 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

WM_NCDESTROY (OnNcDestroy) message handler. Because other aspects of a program might need the m_pMainWnd variable, Id recommend the latter action. If you want to show another modal dialog after the first, duplicate the assignment of m_pMainWnd to this new dialog. SDI/MDI Document/View Applications The SDI and MDI application startup code is remarkably similar, so Ill discuss them together. Listing 6.6 An SDI Applications InitInstance Code
BOOL CSdiApp::InitInstance() { AfxEnableControlContainer(); SetRegistryKey(_T(Local AppWizard-Generated Applications)); LoadStdProfileSettings(); CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CSdiDoc), RUNTIME_CLASS(CMainFrame), RUNTIME_CLASS(CSdiView)); AddDocTemplate(pDocTemplate); CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo); if (!ProcessShellCommand(cmdInfo)) return FALSE; m_pMainWnd->ShowWindow(SW_SHOW); m_pMainWnd->UpdateWindow(); return TRUE; }

Listing 6.7 An MDI Applications InitInstance Code


BOOL CMdiApp::InitInstance() { AfxEnableControlContainer(); SetRegistryKey(_T(Local AppWizard-Generated Applications)); LoadStdProfileSettings(); CMultiDocTemplate* pDocTemplate; pDocTemplate = new CMultiDocTemplate( IDR_MDITYPE, RUNTIME_CLASS(CMdiDoc), RUNTIME_CLASS(CChildFrame), RUNTIME_CLASS(CMdiView)); AddDocTemplate(pDocTemplate); CMainFrame* pMainFrame = new CMainFrame; if (!pMainFrame->LoadFrame(IDR_MAINFRAME)) return FALSE; m_pMainWnd = pMainFrame; CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo); if (!ProcessShellCommand(cmdInfo)) return FALSE; pMainFrame->ShowWindow(m_nCmdShow); pMainFrame->UpdateWindow(); return TRUE; }
202 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

The InitInstance function of the doc/view applications is quite different from that of the dialog application. They add Registry support, SDI/MDI document/view, and MFC command-line handling. The differences between an MDI and SDI application are as follows: 1. MDI and SDI each have their own specific document template classes. 2. The MDI application explicitly creates its main frame window. 3. MDI and SDI have a subtly different usage of ShowWindow. The latter two differences come about because an MDI application has an extra layer of window hierarchy, as shown in Figure 6.1.

Figure 6.1 SDI and MDI window hierarchy. In the SDI, the document view window has a single-frame window that is the main application window. In the MDI, the main frame window holds the MDI client window that in turn holds the child frame windows. These child frame windows in turn contain the documents view windows. Automation Doc/View Application Adding Automation support to an application incorporates a fair amount of extra boilerplate code to InitInstance. Listing 6.8 shows the InitInstance function for an MDI application with automation support. Listing 6.8 InitInstance for an MDI Application with OLE Automation Support
BOOL CMdiAutoApp::InitInstance() { if (!AfxOleInit()) { AfxMessageBox(IDP_OLE_INIT_FAILED); return FALSE; } AfxEnableControlContainer(); SetRegistryKey(_T(Local AppWizard-Generated Applications)); LoadStdProfileSettings(); // Load standard INI file options (including MRU) CMultiDocTemplate* pDocTemplate; pDocTemplate = new CMultiDocTemplate( IDR_MDIAU1TYPE, RUNTIME_CLASS(CMdiAutoDoc), RUNTIME_CLASS(CChildFrame), // custom MDI child frame RUNTIME_CLASS(CMdiAutoView)); AddDocTemplate(pDocTemplate); m_server.ConnectTemplate(clsid, pDocTemplate, FALSE); COleTemplateServer::RegisterAll(); CMainFrame* pMainFrame = new CMainFrame; if (!pMainFrame->LoadFrame(IDR_MAINFRAME)) return FALSE; m_pMainWnd = pMainFrame; m_pMainWnd->DragAcceptFiles(); EnableShellOpen(); RegisterShellFileTypes(TRUE);
203 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo); if (cmdInfo.m_bRunEmbedded || cmdInfo.m_bRunAutomated) { return TRUE; } m_server.UpdateRegistry(OAT_DISPATCH_OBJECT); COleObjectFactory::UpdateRegistryAll(); if (!ProcessShellCommand(cmdInfo)) return FALSE; pMainFrame->ShowWindow(m_nCmdShow); pMainFrame->UpdateWindow(); return TRUE; }

If you compare the preceding listing with Listing 6.7, you can see that theres quite a lot more code to this one.

Functionality in InitInstance To help you understand the functions in InitInstance, Ive grouped them into their functional areas, as described in the following sections. OLE Container Support If you set the ActiveX Controls check box on step 3 of the AppWizard, the AppWizard adds a call to AfxEnableControlContainer in InitInstance, as illustrated in Listing 6.8. This allows your application to host ActiveX controls. 3D Look for Windows NT 3.5x Prior to Windows 95, the 3D dialog look was implemented in an additional DLL. It was quite easy to make use of this in an application, and MFC wrapped this up behind the scenes, which made it even easier. Windows 95 and subsequent operating systems have implemented this 3D look natively, and you no longer need this extra DLL or the support in MFC. Because Visual C++ still supports developing applications for versions of Windows earlier than Windows 95, AppWizard defaults to including the code found in Listing 6.9 in InitInstance. Listing 6.9 3D-Look Support Code
#ifdef _AFXDLL Enable3dControls(); #else Enable3dControlsStatic(); #endif

If you dont need to run your application under earlier versions of Windows, you can safely uncheck the 3D Controls check box on the AppWizard or delete these lines of code afterwards. Leaving this code in under Windows 9x and NT 4 has no detrimental effect because the MFC code checks which operating system the program is running under and
204 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

ignores the facility if it is not needed. Registry Usage You can choose to have your application use private INI files or the registry by using the SetRegistryKey function. Calling SetRegistryKey causes your application to use the Registry rather than a private INI file. If you call this function, the CWinApp profile functions (such as GetProfileString and WriteProfileString) and the applications most recently used (MRU) filenames are stored in the Registry. Most Recently Used Files List When you open documents in your application, the CWinApp object keeps a note of the last documents you used. These are stored in the Registry or your applications private INI file. The LoadStdProfileSettings function loads the most recently used filename information and the print previews number of pages setting. SDI and MDI Document/View The most significant (if not immediately visible) difference between the code in InitInstance for SDI and MDI applications is that they use different document template classes, which are described in the following sections. CSingleDocTemplate and CMultiDocTemplate The CSingleDocTemplate and CMultiDocTemplate classes handle the differences between SDI and MDI document management. CSingleDocTemplate can handle a single document of a single type, whereas CMultiDocTemplate can handle many documents of a single type. AppWizard adds code to InitInstance to create a single document template type for both SDI and MDI applications. In an MDI application, its common to need to support several types of documents and views. Its quite easy to add additional document/views to your application. Using the ClassView, right-click the project and choose New Form. This lets you create a document/view based on a new form. If you dont want a form view, you can do it yourself by replicating the few lines of code that create and add a new document template to CWinApp (see Listing 6.10). Listing 6.10 Creating Additional Document Types
pDocTemplate = new CMultiDocTemplate( IDR_MDITYPE1, RUNTIME_CLASS(CMdiDoc1), RUNTIME_CLASS(CChildFrame), RUNTIME_CLASS(CMdiView1)); AddDocTemplate(pDocTemplate);

Because you will presumably want to differentiate your document types from one another, youll need to use a new resource ID. In the preceding code, Ive used IDR_MDITYPE1. MFC uses this ID in three places: 1. As a multisection resource string. See Knowledge Base article Q129095 in your Visual C++ help for the details of the format of this string.
205 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

2. As the menu ID for your document type. 3. As the document icon ID. The easiest way to identify the usage of this resource is to use the Resource Symbols dialog which is accessed from the View, Resource Symbols menu. Select the existing document resource ID, view its usage, and then duplicate and change the three resources to create the resources for your new type. Although Ive cited this as something you might want to do for an MDI application, in fact the same technique also works for SDI. In addition to the resources, youll probably want to create new document and view classes to support your alternative document. To do this, right-click your project in the Class View, choose New Class, and select the appropriate MFC Classes. DragAcceptFiles DragAcceptFiles tells Windows that your application window supports drag-and-drop file opening. EnableShellOpen EnableShellOpen enables your application to support activation by DDE commands. If your MDI application is already running, and you activate one of your applications supported documents from the shell, the shell uses a DDE command to open the document in the instance of your application that is currently running, rather than start a new instance of your program. RegisterShellFileTypes RegisterShellFileTypes sets all the Registry command strings that let the Windows shell automatically invoke your application to open and print your supported documents. Main Frame Window Creation In the SDI, the frame window is created when the initial document is created, that is, when ProcessShellCommand is called. An MDI application creates its main frame window explicitly in InitInstance and sets the CWinApp m_pMainWnd member to this window. Automation Support If you create an application with (OLE) Automation support, youll have an application that can be instantiated by other applications through Automation facilities. See Chapter 11, COM and MFC, for more information on Automation. AfxOleInit The AfxOleInit call initializes COM support in your application. ConnectTemplate ConnectTemplate creates a class factory and links it to the document template. RegisterAll RegisterAll performs the registration of the applications class factory objects. UpdateRegistry
206 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

UpdateRegistry puts information in the Registry that identifies your application document types. UpdateRegistryAll UpdateRegistryAll registers all the applications class factory objects in the Registry. Rich Edit Control Support If you use the rich edit control in your application, you need to call AfxInitRichEdit to ensure that the rich edit DLL is loaded for your application.

Command-Line Handling MFC handles a predefined set of standard command-line parameters by the lines of code shown in Listing 6.11, which are common to doc/view architecture applications. Listing 6.11 Command-Line Handling Code
CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo); if (!ProcessShellCommand(cmdInfo)) return FALSE;

As you can see, theres not much to it. The key is really in the CCommandLineInfo class. Its parsed by ParseCommandLine (surprisingly), and then executed by ProcessShellCommand. MFC supports the command-line options shown in Table 6.1. Table 6.1 MFC Command-Line Options Command AppName AppName filename AppName /p filename AppName /pt filename printer driver port AppName /dde AppName /Automation AppName /Embedding AppName /unregister Operation Creates a new file Opens the file Prints the file to the default printer Prints the file to the specified printer Starts up and executes the DDE command Starts up as an OLE Automation server Starts up to edit an embedded OLE item Removes the applications standard registry settings from the Registry

The following sections examine the data members of this class to clarify how the class works: m_bShowSplash Setting m_bShowSplash to TRUE indicates that a splash screen should be shown. This value defaults to TRUE unless the command line has the /Embedding or /Automation parameters, which invoke the application without a main
207 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

window. In a plain AppWizard-generated application, m_bShowSplash is useless because nothing ever uses it. To see it in action, you can add the splash screen component to your application. This is covered a little later in the chapter; see The Splash Screen Component. m_bRunEmbedded The m_bRunEmbedded value is TRUE if the application was started with the /Embedding switch. m_bRunAutomated The m_bRunAutomated value is TRUE if the application was started with the /Automation switch. m_nShellCommand The m_nShellCommand value is one of the enumerated values FileNew, FileOpen, FilePrint, FilePrintTo, FileDDE, or AppUnregisterthat correspond to the options available in Table 6.1. This value is used to determine the operation of the ProcessShellCommand function. m_strFileName m_strFileName is the command-line filename parameter. If there isnt a command-line filename, this string is empty. m_strPrinterName If the command is FilePrintTo, m_strPrinterName is the printer name. m_strDriverName If the command is FilePrintTo, m_strDriverName is the printer driver name. m_strPortName If the command is FilePrintTo, m_strPortName is the printer port name. Message Routing, Message Maps, and Message Categories Windows is a message-driven environment that communicates with an application through messages placed in a queue. An application retrieves these queued messages and dispatches them to functions that correspond to the type of destination window. In traditional non-MFC Windows applications, these messages are handled in a large switch statement.The MFC architecture refines this mechanism and handles these messages in a more elegant manner. Message Routing Earlier in this chapter, I mentioned that the Run function is the heart of your application. Run retrieves messages from your applications message queue and pumps them into the MFC framework. Run is essentially a traditional message loop, as shown in Listing 6.12. Listing 6.12 Traditional Message Loop Pseudocode
MSG msg; while ( GetMessage( &msg, NULL, 0, 0 ) ) { DispatchMessage( &msg ); }

When your application calls GetMessage, it relinquishes control to Windows and never returns until Windows has a message for it. If the message returned in the MSG structure isnt WM_QUIT, the message is then passed to DispatchMessage. DispatchMessage examines the MSG hwnd member and calls the registered window function for that class of window. MFC cant alter this fundamental aspect of how Windows works, but it does refine the mechanism so that you might never need to know about registered window procedures. Instead, you write specific functions for those messages that your application classes handle. If you examine the Run function in the MFC source code, youll find this traditional message loop in the CWinThread::PumpMessage function. One key point to note in this
208 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

function is that MFC calls a PreTranslateMessage function to try to handle the message before calling DispatchMessage. I cover PreTranslateMessage separately because its a useful function to know. If the message isnt fully handled by the PreTranslateMessage handlers, the message is finally passed to DispatchMessage and on to the target windows message-handling procedure. The pseudocode in Listing 6.13 illustrates the operation. Listing 6.13 Message Processing Calls Pseudocode Showing PreTranslateMessage Usage
MSG msg; while ( GetMessage( &msg, NULL, 0, 0 ) ) { if ( !PreTranslateMessage( &msg ) ) { DispatchMessage( &msg ); } }

MFC hides raw window message-handling procedures from you, but theyre still there, lurking in the depths. MFC provides its own window procedure in the guise of AfxWndProc. This eventually calls CWnd::OnWndMsg, and it is this function that processes the messages and deals with the next stage of processing: message maps. If you have a look at the source for OnWndMsg, youll see that it treats some messages, such as WM_COMMAND and WM_NOTIFY, as special cases before it gets down to working with the message maps.

What Does the Message Routing? You cant see the full picture of the command routing from a casual inspection of the MFC source code in your project. From the BEGIN_MESSAGE_MAP, you can see that a derived class defers messages to its base class, but the relationship between a view and its document class isnt visible. These relationships are embedded in the MFC source codein a specific classs implementations of OnCmdMsg, to be precise. For example, if you examine CView::OnCmdMsg, youll see that if the view itself doesnt handle the message, it tries the attached CDocument class. Following are a few of the embedded relationships. Note that in each of these, the message proceeds to the next stage only if the current class doesnt handle the message. CFrameWnd CFrameWnd first passes the message to the active view, tries to handle the message itself, and finally passes the message to the CWinApp-derived object. CView CView first handles the message itself and then passes the message to the document class. CDocument CDocument first handles the message itself and then passes the message to its document template class.
209 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

CDialog and CPropertySheet CDialog and CPropertySheet handle the message themselves, pass the message to their parent window, and finally pass the message to their CWinThread object. For a command message in a doc/view arrangement, the message flow is therefore as follows: View->Document->Document Template->Frame->Application PreTranslateMessage Ill not delve into the nitty-gritty depths of MFC here, but the gist is that MFC first lets the child-parent window hierarchy try to process the message through their own PreTranslateMessage handlers. First, the window that the message is intended for has its PreTranslateMessage handler called. If that doesnt handle the message, the message is then passed to its parent windows PreTranslateMessage handlers. Each parent window, up to the top-level frame window, gets its chance to handle the message. This makes PreTranslateMessage a key function to remember if you need to handle messages in a slightly unorthodox manner. For example, in a dialog box, keystroke messages are intended for the child window that has focus, but you might want to do something slightly out of the ordinarysuch as have the Enter key function as the Tab key normally does. PreTranslateMessage is a great place to do these sorts of things because you can handle the message before it is dispatched to its default handler. I dont recommend the example in Listing 6.14 because it goes against the consistency of Windows applications, but it does illustrate the point. Please dont use this as anything other than an illustrationunless your boss makes you use it. Listing 6.14 Using PreTranslateMessage to Handle Keystrokes in a Dialog Box
BOOL CMyDlg::PreTranslateMessage(MSG* pMsg) { if ( ( pMsg->message == WM_KEYDOWN ) && ( pMsg->wParam == VK_RETURN ) ) { // Simulate the normal TAB operation PostMessage( WM_NEXTDLGCTL, 0, false ); // Return TRUE as we dont want anyone else to handle this message return TRUE; } else // Let the base class try to handle the message return CDialog::PreTranslateMessage(pMsg); }

Message Maps Message maps are essentially a lookup table that links a message number to a member function of a class. The message map is implemented as an array of message numbers and pointers to functions that handle that message. Each derived message map is chained to its base class map, so that if the derived class doesnt handle the message, the base class can have a chance at it. MFC handles groups of messages differently. Generally, you can assume the categories that are described in the following sections.
210 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Windows Messages The Windows Messages category forms the bulk of the WM_* messages, with the notable exception of those in the following categories. Control Notification Messages The Control Notification Messages are traditionally passed as WM_COMMAND messages from a child window to its parent window. Newer common controls use a WM_NOTIFY message, rather than the much-abused WM_COMMAND message. These first two message classifications are handled by classes derived from CWnd. In other words, these messages are handled by classes that represent real windows. Command Messages Command messages are WM_COMMAND messages from menus, accelerator keys, and toolbar buttons. MFC makes special provision for these messages, by enabling them to be handled not only by window classes, but also by any class derived from CCmdTarget. This includes CDocument, CDocTemplate, and the CWinApp object as well. Most of these messages are targeted to the frame window onlythere is no other window that has context information to send the message elsewhere. For example, the menu is attached only to the main frame window; there is no data attached to menu items that would enable a message to be sent to a particular type of view window. This is why MFC provides an enhanced routing mechanism; it lets you decide where a message is most appropriately handled. The first two classifications, Windows and Control Notification Messages, are dispatched directly to their destination window, where the message map is searched for a match with the appropriate parameters of the MSG structure. MFC routes command messages first to the active top-level child window and then back down through the MFC window/class hierarchy. Thus, for example, in the case of a view window, the view class tries to handle the message and then passes it down to the document class. If the document class cant handle the message, it passes it down to the document template, which in turn passes it to the main frame window and lastly to the application object. To summarize this, you can assume that MFC routes messages first to the most specific class object. If that doesnt handle the message, it is then passed to the next less specific object. Which Is the Best Class to Handle Command Messages? You might wonder why youd want to handle Windows messages by classes that dont have a window. I hope to explain why I think youll find its actually very useful to do so. Although the traditional MDI model is apparently losing favor, I find its facility of having multiple views on a document is a good model to remember when deciding where a message should be handledeven if your application wont have multiple views or multiple documents. Let me illustrate. In an SDI application, it often doesnt matter if a message is handled in the view or document class because theres a one-to-one correspondence between the two, and from either class you can access the other. Lets say you have a command in a word processor application that implements a Select All facility. In a simple SDI application, you could handle this command in the document class and keep the state variables that determine the selection in the CDocument-derived class. Because the view reflects the document, it will display the entire document as selected. However, if you consider that
211 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

your application could have multiple views simultaneously displaying the document, youll realize that youd probably want to have different selections for both views. Therefore, your decision of where to store the selection variables, as well as how to handle the selection commands, would immediately place them as the responsibility of the view class.

On the other hand, if you had a command that changed the font of the selected text, because youd want all views to reflect the change in the font, you would probably choose to handle the command in the document class. These contrived examples dont present any situation that you couldnt achieve in either case. However, in real-world situations, things can get rather messy if you get the design wrong by handling a command in an inappropriate class. Therefore, I find its best to always consider the MDI multiple-document, multiple-view situation. Whats Behind the Message Map Macros You can think of an MFC Message Map as the equivalent of a traditional non-MFC applications message switch logic. In order to understand a little more about Message Maps, Ill explain how they are constructed. Listing 6.15 provides an example of a message map. Listing 6.15 Message Map Example
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) //{{AFX_MSG_MAP(CMainFrame) ON_WM_CREATE() //}}AFX_MSG_MAP ON_WM_NCLBUTTONDOWN() END_MESSAGE_MAP()

The map begins with the BEGIN_MESSAGE_MAP macro. If you decipher the macro, this expands to give your class the following items: An array named _messageEntries. A messageMap structure that points to this array and also the base classs messageMap structure. A function named GetMessageMap that returns a pointer to the messageMap structure. Note: The implementation is slightly different if you build your project with MFC linked statically, or as a DLL, but the net result is the same. END_MESSAGE_MAP creates a predefined end for the message map array. In your classs header file, theres also a corresponding DECLARE_MESSAGE_MAP macro that declares these variables and functions. In the main body of the message map, you can see the comment block lines shown in Listing 6.16.
212 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Listing 6.16 MFC Comment Blocks


//{{AFX_MSG_MAP(CMainFrame) ... //}}AFX_MSG_MAP

These delimit the area that the Visual C++ Wizards use. You should not edit anything between these comments. If you do, youll probably prevent ClassWizard from working. The Visual C++ wizards are a great help most of the time, but they dont handle every eventuality. ClassWizard is there to handle the most common messages that youre likely to come across, but it doesnt know about all messages. At some point, you will likely have to add a handler that the wizard doesnt cater to. In these circumstances, you can add the appropriate entry to the message map outside the comment blocks as shown by the ON_WM_NCLBUTTONDOWN line in Listing 6.15. Youll probably need to cross-check with the Visual C++ help, but theres usually a direct macro equivalent to most messages. In Listing 6.15, the WM_NCLBUTTONDOWN message has an ON_WM_NCLBUTTONDOWN macro. Where there isnt a direct macro, such as with a user-defined message number, you can use the general ON_MESSAGE macrosee your Visual C++ help for details. Message Map Efficiency In traditional Windows applications, the window procedure consisted of a large switch statement that checked each message against all the messages that the window needed to handle, and finally, if the message wasnt handled, it was passed to the DefWindowProc routine. MFC does away with these protracted switch statements and introduces its more refined message-routing mechanisms. You might assume that the extra code added by MFC to do this would result in slower operation. In some circumstances, such as a trivial Windows program, that may well be the case, but in real-world complex applications, the old switch statement method is grossly inefficient because the compiler generates code that performs an if-then-else operation for every case statement. For an unhandled message (which is probably most messages), every test is performed every time. In MFCs message maps, the message tests are performed in an efficient tight loop of code (which is better for processor caches). MFC also caches recent hits on the message maps so that frequent messages are routed more efficiently. This performance boost is probably most significant for the vast majority of unhandled messages. Idle Processing When your application has no messages to process, the Run function calls the OnIdle function. You can override this to have your application perform any background tasks. OnIdle Run passes a single value to OnIdle, which counts the number of times OnIdle has been successively called, and has little reflection on the real time your application has been idle. Each time your application processes a message, this count is reset to zero. If you need to determine the real idle time, the code example in Listing 6.17 shows how you can do this yourself. Because OnIdle is intended for background task operation, you need to prevent your application from making Windows unresponsive. Therefore, you should only perform short processing tasks there. If you need to do a long operation, you should break up the task
213 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

into a set of short states and do one operation on each call to OnIdle. If your application needs further calls to OnIdle, return TRUE to have OnIdle called again (providing there are no messages pending). If your application has completed all its background tasks, return FALSE to relinquish processing and wait for the next message. Note: Returning FALSE will not permanently stop OnIdle being called. As soon as your application processes all the messages in its queue, OnIdle will once again be called with a count of zero. The documented place to override the OnIdle function is in your CWinApp-derived class. The example in Listing 6.17 determines the real idle time in seconds and displays it along with the count value on the status bar. Listing 6.17 OnIdle Example
BOOL CSdiApp::OnIdle(LONG lCount) { static CTime StartTime; CTimeSpan Span; /* If this is the first call to OnIdle, initialize the start time * Otherwise, calculate the idle time */ if ( lCount == 0 ) { StartTime = CTime::GetCurrentTime(); Span = 0; } else { Span = CTime::GetCurrentTime() - StartTime; } char szMsg[100]; wsprintf( szMsg, Idle for %d seconds, lCount %d, Span.GetTotalSeconds(), lCount ); static_cast<CFrameWnd*>( AfxGetMainWnd() )->SetMessageText( szMsg ); /* Call the default handler to run MFC debugging * facilities and temporary object cleanup */ CWinApp::OnIdle(lCount); return true; }

Note: Its important to call the default OnIdle handler in your OnIdle routine, as it performs debug checks (in a DEBUG build) and garbage collection of temporary MFC objects. See About Temporary Objects in TN003 in your Visual C++ help for details.

An interesting, largely undocumented aspect is that the default implementation of


214 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

CWinApp::OnIdle calls an OnIdle function for any document templates and their documents. Although these functions arent documented in the Visual C++ help, they are public functions and I would expect them to remain in later versions of Visual C++. You can therefore handle OnIdle not only in CWinApp, but also in your derived CDocument class. This is a useful facility to remember if your OnIdle requirements are related to your document data rather than application-wide. For example, if your application is a word processor, it might be appropriate to perform background spelling and grammar checking in the CDocument::OnIdle handler. In Visual C++ 6, the default CDocument::OnIdle does nothing, but you should still call it on the off chance that any future version of MFC could use the default behavior. Idle Processing for Dialogs If you look at the sample InitInstance for a dialog application (in Listing 6.5), youll see that the application has effectively ended when InitInstance returns. This is unlike other MFC applications that normally return TRUE from InitInstance and enter the Run function to process messages. Because the dialog application doesnt call Run, it also never benefits from the normal OnIdle handling. So how do you perform idle processing in a dialog application? The answer is the MFC WM_KICKIDLE message. When you call a dialogs DoModal function, MFC no longer creates a real modal dialog box. Instead, it fakes it by disabling any main frame window, and performs its own message loop to handle the modal dialog box. You can find the code in the MFC source code RunModalLoop function. When you know about WM_KICKIDLE, its easy to perform background tasks in a dialog application. All you have to do is add a handler for WM_KICKIDLE. Youll need to add this manually, as illustrated in Listing 6.18, because the wizards dont know about this message. Listing 6.18 Message map for WM_KICKIDLE
BEGIN_MESSAGE_MAP(CDlgAppDlg, CDialog) //{{AFX_MSG_MAP(CDlgAppDlg) ... //}}AFX_MSG_MAP ON_MESSAGE(WM_KICKIDLE, OnKickIdle) END_MESSAGE_MAP()

Note: The WM_KICKIDLE message map entry is added outside of the commented wizard block code so as not to confuse ClassWizard. In your dialog classs header file, add the following definition,
afx_msg LRESULT OnKickIdle(WPARAM, LPARAM lCount);

and in the source module, add the function


LRESULT CDlgAppDlg::OnKickIdle(WPARAM, LPARAM lCount) { // Add your idle code and return TRUE or FALSE in the same way as OnIdle
215 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Dialog Command Updating In a dialog application, the normal MFC command update mechanism that enables and disables menu items and toolbar buttons doesnt workyou need to add a few lines of code to make it happen. You can do this quite easily by calling UpdateDialogControls from the WM_KICKIDLE handler:
LRESULT CDlgAppDlg::OnKickIdle(WPARAM, LPARAM lCount) { UpdateDialogControls( this, TRUE ); return 0; }

The only other additions you need to make are to write the command handlers and add entries to the message map. The following code shows the function:
void CDlgAppDlg::OnUpdateCommandX(CCmdUI* pCmdUI) { pCmdUI->Enable( your_expression_logic_to_enable_the_control ); }

To add the update command handler, modify the message map like this:
BEGIN_MESSAGE_MAP(CDlgAppDlg, CDialog) //{{AFX_MSG_MAP(CDlgAppDlg) ... //}}AFX_MSG_MAP ON_MESSAGE(WM_KICKIDLE, OnKickIdle) ON_UPDATE_COMMAND_UI( IDM_COMMANDX, OnUpdateCommandX ) END_MESSAGE_MAP()

If you use UpdateDialogControls with its second parameter set to TRUE, most controls on your dialog that dont have a handler routine are automatically disabled. I say most because MFC explicitly skips disabling controls with the following button styles:
BS_AUTOCHECKBOX BS_AUTO3STATE BS_AUTORADIOBUTTON BS_GROUPBOX

Im not aware of the reason behind this, but it seems reasonable to assume that auto check boxes and radio buttons may well be used to selectively enable and disable some of the controls on a dialog. Consequently, its unlikely that youd want to disable the control that enables you to select other options on your dialog. The Splash Screen Component If you feel the urge to express your artistic talents in the form of a splash screen, its easy to add the code for one in your MFC application. Although the default applications generated by AppWizard dont give you a splash screen, you can add one using the additional component and controls with Visual C++. Click Project, Add to Project, Components and Controls. Double-click the Visual C++ Components and insert the Splash Screen component.
216 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

This adds a new class module (CSplashWnd) to your project and the following lines of code. This line is added to the CMainFrame::OnCreate handler to create the splash window:
CSplashWnd::ShowSplashScreen(this);

These are added to InitInstance:


CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo); CSplashWnd::EnableSplashScreen(cmdInfo.m_bShowSplash);

This last line enables the splash screen if there are no command-line parameters that dictate otherwise. In the applications PreTranslateMessage handler, this code handles messages for the splash screen window so that any keyboard or mouse messages destroy the splash window.
if (CSplashWnd::PreTranslateAppMessage(pMsg)) return TRUE;

In order to remove the splash screen window automatically, the splash window starts a timer for itself. When the timer expires, the splash window self-destructs. Thats the easy bitnow you need to create your artwork. Have fun! Summary In this chapter, Ive tried to cover the most common aspects of the CWinApp class, application lifetime, and the message routing architecture. In doing so, Ive touched on other areas, such as the document/view architecture, and passed on a few tips learned from my own experiences with MFC. When you come across problems in your own development, its always worth remembering that the MFC source code is provided with Visual C++ so that you can delve deeper and gain a better understanding. I hope that you find this information helpful in your own MFC applications, and I encourage you to investigate the inner workings of MFC.

217 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Part II Documents, Views, and Applications That Use Them In This Part The Document/View Architecture 275 Extending the User Interface 301 Printing 345 Chapter 7 The Document/View Architecture by K. David White In This Chapter Documents, Frames, and Views 276 Creating New Documents 280 Views 285 In most books on MFC and Visual C++, the document/view architecture is usually given top billing. There are reasons for this. Microsoft, in developing MFC, decided that a consistent framework was needed to encapsulate the Windows functionality. Although some might argue that the document/view architecture is really all you need to understand in order to create robust MFC applications, it is apparent to the experienced MFC developer that this is not the case. The document/view architecture is a consistent framework and provides the necessary control over MFC applications; however, it lends itself to expansion and flexibility. There are also many situations in which the doc/view architecture should be abandoned entirely. This chapter tries to unlock the doc/view framework and concentrate on using it to the best advantage. The first section covers all the components and provides an overview of the doc/view architecture. The Creating New Documents section explores the means necessary to utilize the framework to create new documents. Next, the chapter explores views and how the views interact with the document and frames. After you gain an understanding of views, youll take a look at the base of this framework, the document. With this knowledge of all the pieces, youll look to ways to manage it above and beyond the framework. The last section of this chapter takes a quick look at other frameworks to consider. Documents, Frames, and Views Most senior-level developers understand how data should be managed and how it should be presented. In object-oriented presentation, a class contains member variables that are accessed through member functions. This encapsulation ensures that the class is basically responsible for itself. How then does an application that contains many classes manage the data contained within those classes in a controlled and structured manner? If you look at the problem of presenting data versus managing data, you can quickly see where the document/view architecture solves this dilemma. Lets take a closer look. As you can easily see from Figure 7.1, the document contains the data store, and the view is a window into a certain location of that data. This store can be ultimately a database connection, a disk storage file, or some other mechanism. Later sections look at the problem of where to store data needed by a view. Simply put, however, the view is a window into the data that is stored within the document. Within this architecture, the document is responsible for exposing the necessary interfaces for the view to display the data.
218 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

Figure 7.1 A simple representation of the document/view architecture. The doc/view architecture comes in two primary flavors: the Single Document Interface (SDI) and the Multiple Document Interface (MDI). An SDI application contains one and only one document class with which to control the data. The MDI application can contain any number of documents. You will notice from Figure 7.2 that the documents are created through a document template class.

Figure 7.2 Document types within the doc/view architecture. The solid dark lines in Figure 7.2 represent the instantiation path for creating views, and the dotted lines represent the pointer direction. Now that youve seen how this picture is painted, lets start looking at each individual piece of the puzzle. Note: There are many different document classes, which are usually derived from the base class CDocument. For instance, if you are using COM and want compound document support, you might want to choose the COleDocument class.

Note: As Chapter 27, MAPI and MFC, points out, the CDocument class also provides the necessary structure to support the Microsoft Messaging Application Program Interface (MAPI). This is done through the two CDocument member functions: OnFileSendMail and OnUpdateFileSendMail Document Templates Imagine, if you will, trying to manage all the necessary information about data without the different classes to define the structure of the data. But what about relating the data contained within the document with the necessary display constraints defined by a view? Some overhead mechanism is needed to keep track of the document and its relation to the views that it owns. Enter the document template. The document template class defines all the menus, accelerators, and other important resources that a view requires. This template mechanism loosely ties the document class with the view class. As Figure 7.2 shows, the CWinApp class creates and contains a pointer to one or more document templates, depending on whether the application is an SDI or MDI application. CSingleDocTemplate is responsible for managing one and only one CDocument class. CMultiDocTemplate maintains a list of CDocument classes that are currently opened from that template. MFC breaks the object-oriented paradigm (only a little bit) here by allowing
219 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

the CDocument class and the CDocTemplate classes to be friends. The CDocument contains a back pointer to its owning CDocTemplate class. This allows an application to traverse the CWinApp/CDocument/CView hierarchy in either direction. Whenever the CWinApp class creates a document template, it does so in a two-step process. It first instantiates the DocTemplate class and then performs an AddDocument to add the document to the templates list and sets the back pointer. Listing 7.1 shows the CSingleDocTemplate constructor, and Listing 7.2 shows the AddDocument method. Notice that the back pointer is set during the AddDocument method. One reason that it is done this way is to enable the user to create templates and specify document types outside what the framework automatically provides. Listing 7.3 is from the SDI sample UNL_Single. Listing 7.1 The CSingleDocTemplate Constructor
CSingleDocTemplate::CSingleDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass) : CDocTemplate(nIDResource, pDocClass, pFrameClass, pViewClass) { m_pOnlyDoc = NULL; }

Listing 7.2 The CSingleDocTemplate AddDocument Method


void CSingleDocTemplate::AddDocument(CDocument* pDoc) { ASSERT(m_pOnlyDoc == NULL); // one at a time, please ASSERT_VALID(pDoc); CDocTemplate::AddDocument(pDoc); m_pOnlyDoc = pDoc; }

Listing 7.3 The UNL_Single.cpp InitInstance Routine


CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CUNL_SingleDoc), RUNTIME_CLASS(CMainFrame), // main SDI frame window RUNTIME_CLASS(CUNL_SingleView)); AddDocTemplate(pDocTemplate);

The sample application, UNL_Single, is a fairly simple application that is used to represent the doc/view framework. Notice the creation of a single document template and the use of the AddDocTemplate method. In this snippet of code, you see that the framework is adding
220 / 1166

MFC Programming with Visual C++ 6 Unleashed

Summary

a single template that is defined by a document class, a view class, and something I havent discussed yet. Note: The RUNTIME_CLASS macro returns a pointer to the CRuntimeClass class. The CRuntimeClas