Visual C++ Workshop Session 8 R. Eckert MFC WINDOWS PROGRAMMING (APP/WINDOW APPROACH) The Microsoft Foundation Class (MFC) Library-- A Hierarchy of C++ classes designed to facilitate Windows programming An alternative to using Win32 API functions A Visual C++ Windows app can use either Win32 API MFC or both:About 200 MFC classes (versus 2000+ API functions) Provide a framework upon which to build Windows applications Encapsulate most of Win32 API in a set of logically organized classes Some characteristics of MFC: 1. Offer convenience of REUSABLE CODE: Many tasks common to all Windows apps are provided by MFC Our programs can inherit and modify this functionality as needed We don't need to recreate these tasks MFC handles many clerical details in Windows programs 2. Produce smaller executables: Typically 1/3 the size of their API counterparts 3. Can lead to faster program development: But there's a steep learning curve-- Especially for newcomers to object-oriented programming 4. MFC Programs must be written in C++ and require the use of classes: Programmer must have good grasp of: How classes are declared, instantiated, and used Encapsulation Inheritance Polymorphism--virtual functions MFC Class Hierarchy (See online help on "Hierarchy Chart")-- CObject: At top of hierarchy ("Mother of all classes") Provides features like serialization (disk reading/writing) All its functionality is inherited by any classes derived from it Important Derived Classes-- CFile: Support for file operations CDC: Encapsulates the device context (Graphical Drawing) CGdiObject: Base class for various drawing objects (bitmaps, pens, etc.) CMenu: Encapsulates menu management CCmdTarget: Encapsulates message passing process & is parent of: CWnd: Base class all windows are derived from; most common: CFrameWindow: Can contain other windows ("normal" kind of window we've been using) CView: Encapsulates process of displaying data CDialog: Encapsulates dialog boxes CWinThread: Defines a thread of execution & is parent of: CWinApp: Most important class dealt with in MFC apps: Encapsulates an MFC application Controls following aspects of Windows programs: Startup, initialization, execution, shutdown An application should have one CWinApp object When instantiated, application begins to run CDocument: Encapsulates the data associated with a program Primary task in writing MFC program--to create classes Most will be derived from MFC library classes MFC Class Member Functions-- Most functions called by an app will be members of an MFC class Examples: ShowWindow()--a member of CWnd class TextOut()--a member of CDC LoadBitmap()--a member of CBitmap Applications can also call API functions directly More convenient to use MFC member functions MFC Global Functions-- Not members of any MFC class Always begin with Afx prefix (Application FrameworKS) Independent of or span MFC class hierarchy Example: AfxMessageBox()-- Message boxes are predefined windows Can be activated independently from rest an application A Minimal MFC Program (App/Window Approach) Simplest MFC programs must contain two classes derived from hierarchy: An application class derived from CWinApp-- Defines the application A window class usually derived from CFrameWnd-- Defines the application's main window Every MFC program must have these two classes Message Processing under MFC-- Like API programs, MFC programs must handle messages from Windows API mechanism: huge switch/case statement MFC mechanism: "message maps" (lookup tables): Table entries: Message number Pointer to a derived class member message-processing function Programs must: Declare message-processing functions Map them to messages app is going to respond Mapping by "message-mapping macros" Most MFC app windows use a window procedure supplied by library Message maps enable the library window procedure to find the function corresponding to the current message
SPECIFIC STEPS IN WRITING A SIMPLE MFC PROGRAM (App/Window Approach)-- A. DECLARATIONS (.h) 1. Declare a window class derived from CFrameWnd (e.g., CMainWin)-- Class Members: The constructor Message-processing ftn declarations [e.g., afx_msg void OnChar()] DECLARE_MESSAGE_MAP() macro: Allows windows based on this class to respond to messages Declares that a msg map will be used to map messages to functions Should be last class member declared 2. Declare an application class derived from CWinApp (e.g., CApp)-- Must override CWinApp's InitInstance() virtual function: Called each time a new instance of application is started (i.e., when an object of this class is instantiated) B. IMPLEMENTATION (.CPP) 1. Define constructor for class derived from CFrameWnd (CMainWin)-- Should call member function Create() to create the window Does what CreateWindow() does in API programming 2. Define message map for class derived from CFrameWnd CMainWin)-- BEGIN_MESSAGE_MAP(owner, base) List of "message macros" [e.g., ON_WM_CHAR()] END_MESSAGE_MAP() 3. Define (implement) message-processing functions declared in 1 above-- 4. Define (implement) InitInstance() overriding function-- Done in class derived from CWinApp (CApp): Should have initialization code for each new instance of the app: Create a CMainWin object pointer to program's main window (Used to refer to the window, like hWnd in API programs) Invoke object's ShowWindow() member function Invoke object's UpdateWindow() member function Must return non-zero to indicate success [MFC's implementation of WinMain() calls this function] Nature & form of simple window & application have been defined But neither exists-- Must instantiate an application object derived from CWinApp (CApp) 5. Create an instance of the application class (CApp) Causes WinMain() to execute--it's now part of MFC [WINMAIN.CPP] WinMain() does the following: Calls AfxWinInit()-- which calls AfxRegisterClass() to register window class Calls CApp::InitInstance() [virtual function overridden in 4 above]-- which creates, shows, and updates the window Calls CWinApp::Run()-- which calls CWinThread::PumpMessage()-- which contains the GetMessage() loop After this returns (i.e., when the WM_QUIT message is received)-- AfxWinTerm() is called-- which cleans up and exits PROG1 Example MFC Application: Just creates a skeleton frame window--
Specific Steps in Creating and Building an MFC Application like PROG1 "Manually" using Microsoft Developer Studio-- 1. "File | New", "Win32 Application" as always Enter a Project Name and Location as usual 2. Enter header file text (e.g., PROG1.H)--see DECLARATIONS above Enter source text (e.g., PROG1.CPP)--see IMPLEMENTATION above [If already entered, copy into project's directory and: "Project | Add To Project | Files", entering name of .CPP file.] 2. "Project | Settings", "General" Tab From "Microsoft Foundation Classes:" combo box, choose: "Use MFC in a Shared DLL" 3. Build the project as usual <See PROG1.H and PROG1.CPP Listings> MSG1 Example MFC Application: Mouse/Character Message Processing--
User presses mouse button Left/Right Button down==>string displayed at current mouse cursor position Keyboard key pressed==> Character displayed at upper lefthand corner of client area <See MSG1.H and MSG1.CPP Listings> DEVELOPING AND USING RESOURCES WITH MFC: Menus in App/Window Programs-- Create .rc file manually with text editor Or visually with Developer Studio resource editorsresource.h & .rc files Both should be edited to get rid of extra "garbage" (Used by AppWizard) resource.h file: Should only contain #defines for menu item IDs program's .rc file: Should only have menu section and two #includes at the top-- #include <afxres.h> #include "resource.h" All other stuff should be edited out Add .cpp source file and .rc file to project and build (as usual) MENU1 Example Program--
Has menu with items "One", "Two", and "Help" "One" a popup with items "Alpha" and "Beta" "Two" a popup with item "Gamma" Selection of any menu item==>appropriate message box is displayed Left mouse button pressed==>"Abort/Retry/Ignore" message box Appropriate message box depending on which button user selects from MENU1.RC Resource Script File-- Created with Developer Studio's menu editor --> RESOURCE.H Both edited to get rid of AppWizard "garbage" USING MODAL DIALOG BOXES IN MFC-- Dialog Box--child window containing one or more child window controls Dialog template given in resource (.rc) file Under Win32 API: Created with DialogBox() or DialogBoxParam() Messages from controls handled by Msg-processing function-- Uses switch/case statement to handle msgs from Under MFC: Dialog boxes encapsulated by the CDialog class (derived from CWnd) App should derive its own dialog box class from CDialog Dialog box message handling done with message maps Dialog box class declarations (.h file): Message handling functions Message map Dialog box class implementation (.cpp file) defines: Dialog box message map Message handler functions Creating a dialog box under MFC: Constructor of CDialog-derived class should call CDialog constructor Arguments: ID of dialog box (as specified in .rc file), owner window Creates the dialog box (not activated yet) Init code should be placed in CDialog's OnInitDialog() handler function (Invoked in response to the WM_INITDIALOG message) Activating dialog box under MFC: Use CDialog's DoModal() member function Displays dialog box Msgs from dialog box controls go to dialog box handler functions Until dialog box has been closed with CDialog's EndDialog() DoModal() returns & main message processing continues DIALOG1 Example Program--
User clicks "Dialog" main menu item: Modal dialog box containing "Red", "Green", "Cancel", "OK" buttons User chooses "Red" or "Green" buttons: Message box with appropriate text appears User chooses "Cancel" or "OK" buttons: Dialog box closes User clicks "Exit" main menu item: window is closed and program terminates DIALOG1.RC and RESOURCE.H files-- Created with Developer Studio's menu and dialog box editors "Garbage" edited out Dialog box ID name: "SAMPLEDIALOG" Default ID's for dialog box "OK" and "Cancel" buttons: IDOK, IDCANCEL CDialog contains default handlers for their WM_COMMAND messages DIALOG1.H and DIALOG.CPP <See Listings> DIALOG BOXES CONTAINING LIST BOXES-- List boxes: controls that both generate and receive messages Under Win32 API: App uses SendDlgItemMessage() to send messages like: LB_ADDSTRING, LB_GETCURSEL, LB_GETTEXT, etc. List box sends messages in response to user selecting items Under MFC: App uses CListBox-derived class member functions to send messages e.g., AddString(), GetCurSel(), GetText(), etc. (See online help) To identify control message is for, use CWnd's GetDlgItem() function Returns a pointer to the control--which should be typecast: CListBox *plistbox = (CListBox *)GetDlgItem(CONTROL_ID); Pointer used to access member functions to send messages control Example: plistbox->AddString("some string"); Causes "some string" to be added to list box List box initialization-- Override WM_INITDIALOG message handler, OnInitDialog()-- Adds strings to the list with calls to AddString() Declare OnInitDialog() in section of .h file that declares CDialog class. OnInitDialog() definition (in .cpp file): Call base class OnInitDialog() Make calls to AddString() to fill the dialog box DIALOG2 Example Program--
User clicks "Dialog" main menu item: Dialog box with fruit selection list box appears User double clicks fruit or selects fruit & presses "Select Fruit" button Message box appears with fruit selection and position in list box Dialog box messages of interest: IDD_SELFRUIT (the button) LBN_DBLCLK of ID_LB1 list box (notification code generated when user double clicks item in list box) Dialog's Message Map defines OnSelect() to be handler for both Calls GetDlgItem()-->Pointer to the list box Invokes GetCurSel() and GetText() to retrieve item selected