Visual C++ and MFC Fundamentals Chapter 12: Dialog-Based Windows Practical Learning: Creating a Top-Most Window To create the dialog box with a thin border, check its Static Edge check box or set it to True To indicate that this dialog can accept dragged files, check its Accept Files check box or set it to True 12.1.8 Creating the Dialog Resource File As mentioned already, a dialog box means nothing except for the controls it is created to host The controls are listed in a section that starts with the BEGIN and ends with the END keywords Here is an example: IDD_SCHOOL_SURVEY DIALOGEX 0, 0, 340, 268 STYLE WS_CAPTION | WS_SYSMENU | WS_POPUP | DS_MODALFRAME EXSTYLE WS_EX_TOOLWINDOW CAPTION “School Survey – For Teachers Only” FONT 8, “MS Shell Dlg”, 400, 0, 0x1 BEGIN END If you design a dialog box and the first object of your application, you must save it before using it Even if you had decided to manually create the dialog box as a text file, you must save it before using it In both cases, whether saving the dialog box designed or the text file, saving it results in creating a resource file This file should have the rc extension If your resource file is using some identifiers, which is the case for most or all controls you will use in your application, you must list them in a header file This file is traditionally called Resource.h or resource.h Each identifier must be listed using the following syntax: #define Identifier Constant When you save the resource file, Visual C++ automatically creates the resource header file and names it resource.h After saving the resource file, it is still considered external to the application If you are using MSVC 6, you must add it to your project After it has been added to your project, you can continue working on the dialog box, adding, deleting, o manipulating the r controls After doing that, if you save the resource, Visual C++ automatically makes the appropriate changes in the resource and the resource header files Practical Learning: Saving the Resource File If you are using MSVC 7, on the Standard toolbar, click the Save All button If you are using MSVC 6: a b © FunctionX, Inc To save the dialog box, click the system Close button of the window that holds the dialog box This will display a window with a tree view that starts with Script1 Close its window also 359 Chapter 12: Dialog-Based Windows Visual C++ and MFC Fundamentals c You will receive a message box asking you whether you want to save the script Click Yes d Locate the ExoDialog1 folder that was used to create the current project Display it in the Save In combo box Change the name in the File Name with ExoDialog1 e Click Save To add the resource to your application, on the main menu, click Project -> Add To Project -> Files… Click ExoDialog1.rc and click OK To verify that the dialog box has been added, in the Workspace, click the ResourceView tab and expand the ExoDialog1 Resources node Then expand the Dialog folder and double-click IDD_EXERCISE_DLG If you are using MSVC 7, on the Standard toolbar, the Save All Then 12.1.9 Creating a Class for the Dialog After creating the resource for the dialog box, you must create a class that will be used to handle its assignment To this, you must derive a class from CDialog In the header file of the dialog’s class, define an enumerator whose only member is called IDD and initialize it with the identifier of the dialog box Because the identifier is listed in the resource header file, you must include this resource header in the file in which you are using the dialog’s identifier Practical Learning: Create the Dialog’s Class To create a class for the dialog box, open the Exercise.cpp created earlier and add the following: #include #include #include "resource.h" class CExerciseApp : public CWinApp { public: 360 © FunctionX, Inc Visual C++ and MFC Fundamentals }; Chapter 12: Dialog-Based Windows BOOL InitInstance(); class CExerciseDlg : public CDialog { public: enum { IDD = IDD_EXERCISE_DLG }; }; BOOL CExerciseApp::InitInstance() { return TRUE; } CExerciseApp theApp; 12.1.10 Save All Dialog Box Methods A dialog box is based on the CDialog class As seen above, when creating your dialog box, you can derive a class from CDialog The CDialog class itself provides three constructors as follows: CDialog(); CDialog(UINT nIDTemplate, CWnd* pParentWnd = NULL); CDialog(LPCTSTR lpszTemplateName, CWnd* pParentWnd = NULL); The default constructor, CDialog(), can be used to declare a variable whose behavior is not yet known or, for one reason or another, cannot yet be defined When creating your class, you should also declare at least a default constructor The identifier of the dialog box, such as IDD_DIALO G1, can be used as the first argument, nIDTemplate, of a CDialog() constructor to create a dialog box from an existing resource If you are using a Win32 template to create your dialog box, pass the name of this template as a string to a CDialog() constructor, lpszTemplateName When implementing your default constructor, initialize the parent CDialog constructor with the IDD enumerator declared in your class If your dialog box has an owner, specify it as the pParentWnd argument If you set it to NULL, the application will be used as the dialog’s parent If you dynamically create objects for your application using your dialog class, it is a good idea to also declare and define a destructor This would be used to destroy such dynamic objects Most other methods of a dialog box depend on circumstances we have not yet reviewed Practical Learning: Creating a Dialog Box Declare a default constructor and a destructor for your dialog class and implement them as follows: class CExerciseDlg : public CDialog © FunctionX, Inc 361 Chapter 12: Dialog-Based Windows Visual C++ and MFC Fundamentals { public: enum { IDD = IDD_EXERCISE_DLG }; CExerciseDlg(); ~CExerciseDlg(); }; CExerciseDlg::CExerciseDlg() : CDialog(CExerciseDlg::IDD) { } CExerciseDlg::~CExerciseDlg() { } Before using the new class, declare a variable of it as follows: BOOL CExerciseApp::InitInstance() { CExerciseDlg Dlg; m_pMainWnd = &Dlg; } return TRUE; Save All 12.2 Modal Dialog Boxes 12.2.1 Dialog-Based Applications There are two types of dialog boxes: modal and modeless A Modal dialog box is one that the user must first close in order to have access to any other framed window or dialog box of the same application One of the scenarios in which you use a dialog box is to create an application that is centered around a dialog box In this case, if either there is no other window in your application or all the other windows depend on this central dialog box, it must be created as modal Such an application is referred to as dialog-based There are two main techniques you can use to create a dialog-based application: from scratch or using one of Visual C++ wizards After creating a dialog resource and deriving a class from CDialog, you can declare a variable of your dialog class To display your dialog box as modal, you can call the CDialog::DoModal() method in your CWinApp::InitInstance() method Its syntax is: virtual int DoModal(); This method by itself does nothing more than displaying a dialog box as modal We will learn that you can use this method to find out how the user had closed such a dialog box 362 © FunctionX, Inc Visual C++ and MFC Fundamentals Chapter 12: Dialog-Based Windows Practical learning: Displaying a Modal Dialog Box To display the dialog box as modal, in the InitInstance() event of your CWinApp derived class, call the DoModal() method using your dialog variable: #include #include #include "resource.h" class CExerciseApp : public CWinApp { public: BOOL InitInstance(); }; class CExerciseDlg : public CDialog { public: enum { IDD = IDD_EXERCISE_DLG }; CExerciseDlg(); ~CExerciseDlg(); }; CExerciseDlg::CExerciseDlg() : CDialog(CExerciseDlg::IDD) { } CExerciseDlg::~CExerciseDlg() { } BOOL CExerciseApp::InitInstance() { CExerciseDlg Dlg; m_pMainWnd = &Dlg; Dlg.DoModal(); return TRUE; } CExerciseApp theApp; © FunctionX, Inc Test the application 363 Chapter 12: Dialog-Based Windows Visual C++ and MFC Fundamentals Close it and return to MSVC 12.2.2 The MFC Wizard for a Dialog-Based Application Microsoft Visual C++ provides an easier way to create an application that is mainly based on a dialog box To use this technique, start a new project and specify that you want to create an MFC Application In the MFC Application Wizard, set the Application Type to Dialog Based Practical Learning: Using the Wizard to create a Dialog-Based Application In the New Project dialog box, in the Templates list, click MFC Application 364 On the main menu, click File -> New -> Project Set the Project Name to ExoDialog2 © FunctionX, Inc Visual C++ and MFC Fundamentals Chapter 12: Dialog-Based Windows Clic k OK In the MFC Application Wizard dialog box, click Application Type and, on the right side, click the Dialog Based radio button Click User Interface Features and click About Box to remove its check box Under Dialog Title, select the text and replace it with Dialog Box Exercise Click Advance Features to see its content Click Generated Classes 10 In the Generated Classes list, click CExoDialog2App and, in the Class Name, replace the text with CExerciseApp 11 In the Generated Classes list, click CExoDialog2Dlg In the Class Name, replace the name with CExerciseDlg In the h file edit box, replace the name of the file with ExerciseDlg.h In the cpp file edit box, replace the name of the file with ExerciseDlg.cpp Make sure the Base Class is set to CDialog © FunctionX, Inc 365 Chapter 12: Dialog-Based Windows Visual C++ and MFC Fundamentals 12 Click Finish 13 Test the application and return to MSVC 12.2.3 A Modal Dialog Box in an Application Some applications require various dialog boxes to complete their functionality When in case, you may need to call one dialog box from another and display it as modal Here is an example: The Paragraph dialog box of WordPad is a modal dialog box: when it is displaying, the user cannot use any other part of WordPad unless he or she closes this object first Since a dialog box is created using a class, the first thing you must is to include the header file of the CDialog object whose box you want to call To call one dialog box from another window, select the event (or message) from where you would make the call Declare a variable of the other class and use the CDialog::DoModal() method to display the other object as a modal dialog box Calling a dialog box from the menu of a frame-based application is done the same way as calling it from a dialog box 366 © FunctionX, Inc Visual C++ and MFC Fundamentals Chapter 12: Dialog-Based Windows Practical Learning: Adding a Dialog Box to an Existing Application Start a new MFC Application project named SDIAndDlg Create it as a Single Document and click Finish To add a dialog box, on the main menu, click Project -> Add Resource… In the Add Resource dialog box, double-click Dialog To create a class for the new dialog box, right-click it and click Add Class… In the MFC Class Wizard, specify the Class Name as CExerciseDlg In the Base Class combo box, select CDialog and click Finish Display the menu by double-clicking IDR_MAINFRAME under the Menu folder of the Resource View tab Click View and click the first empty item under it Type – and press Enter to add a separator 10 In the menu item under the new separator, type &Exercise… and press Enter 11 Right-click the new Exercise menu item and click Add Event Handler… 12 In the Message Type, access the COMMAND item In the Class List, click CMainFrame Accept the Function Handler Name then click Finish Add And Edit 13 Change the file as follows: #include "stdafx.h" #include "SDIAndDlg.h" #include "MainFrm.h" #include "ExerciseDlg.h" #ifdef _DEBUG #define new DEBUG_NEW #endif void CMainFrame::OnViewExercise() { // TODO: Add your command handler code here CExerciseDlg Dlg; Dlg.DoModal(); } 14 Execute the application On its main menu, click View -> Exercise… 15 After using the dialog box, close it and close the application to return to MSVC 12.3 Property Sheets and Wizards 12.3.1 Introduction to Property Pages In some applications, you may want to add many but necessary and useful controls to a dialog box You can solve this problem in three main ways: © FunctionX, Inc 367 Chapter 12: Dialog-Based Windows Visual C++ and MFC Fundamentals ?? You may have to tremendously increase the width and the height of your dialog box Although this solution works sometimes, it may have the disadvantage of producing a highly crowded dialog box ?? You can hide some controls and display them only when needed, such as in response to a user clicking a button The concept of this type of application involves an unpredictable and non-practical design It also eventually requires a lot of coding ?? The third solution involves the user of property pages A property page is a dialog box, that can be positioned in front of, or behind of, another This has the advantage of providing various dialog boxes that are “physically” grouped as one entity Each part is represented by a tab The tabs are usually positioned in the top section and each is used to identify a particular page: To use a property page, the user clicks one The page clicked besomes positioned in front of the other(s) The user can click another tab to select a different page: 12.3.2 Creating Property Pages A property page is designed from a dialog box and it must have the following characteristics: ?? Style: Child or WS_CHILD ?? Border: Thin or WS_POPUP 368 © FunctionX, Inc Visual C++ and MFC Fundamentals Chapter 15: Fundamental Controls Chapter 14: Controls Functionality ? Handling Controls ? Controls Management ? Controls Instance and Handle ? © FunctionX, Inc Access to Controls Information 411 Chapter 15: Fundamental Controls Visual C++ and MFC Fundamentals 14.1 Handling Controls 14.1.1 Introduction There are two particularly important pieces of information about every control of your application The information stored in the control and the action or type of action the user can perform on that control At its most basic level, at one time in the life of an application, a control holds a value that can be of particular interest either to you or to the user On the other hand, when interacting with the computer, based on your application, the user will usually face different types of controls that various things and produce different results These are two features of controls (their values and the actions the user can perform on them) that you should know as much as possible, about the controls you choose to involve in your application Practical Learning: Introducing Controls Variables Open the Geometry application If you not have it, open the Geometry1 application from the accompanying exercises of this book Test the application to make sure it is working fine Then close it and return to MSVC Click the Resourve View tab, expand the Dialog folder and double-click IDD_QUADRILATERAL Delete the existing control on the dialog box On the Controls toolbox, click the Custom Control button center section of the dialog box On the Properties window: Change its ID to IDC_LBL_SSIDE Set its Caption to &Side: Set the Class name as Static Using only the Custom Control object, design the rest of the property page as follows (the controls are listed from left to right then from up -> down): ID 412 Caption Class and click in the top Style ExStyle © FunctionX, Inc Visual C++ and MFC Fundamentals IDC_LBL_SSIDE IDC_EDT_SSIDE IDC_BTN_SCALC IDC_LBL_SPRM IDC_EDT_SPRM IDC_LBL_SAREA IDC_EDT_SAREA IDC_LBL_RLENGTH IDC_EDT_RLENGTH IDC_LBL_RHEIGHT IDC_EDT_RHEIGHT IDC_BTN_RCALC IDC_LBL_RPRM IDC_EDT_RPRM IDC_LBL_RAREA IDC_EDT_RAREA Chapter 15: Fundamental Controls &Side: &Calculate &Perimeter: &Area: &Length: &Height: Calc&ulate P&erimeter: A&rea: Static Edit Button Static Edit Static Edit Static Edit Static Edit Button Static Edit Static Edit 0x50010000 0x50810000 0x50010000 0x50010000 0x50810000 0x50010000 0x50800000 0x50010000 0x50810000 0x50010000 0x50810000 0x50010000 0x50010000 0x50810000 0x50010000 0x50810000 0x0 0x0 0x0 0x0 0x1 0x0 0x1 0x0 0x0 0x0 0x0 0x0 0x0 0x1 0x0 0x1 Save All Click anywhere on the dialog box to make sure it has focus Press Ctrl + A to select all controls Press Ctrl + C to copy the selection Open the IDD_CIRCULAR dialog box Delete its control and drag its lower-right corner to give a 320 x 200 size Click anywhere in the body of the dyalog box Press Ctrl + V to paste the selection 10 Change the IDs and captions of the controls as follows (from left to right and up -> down): ID IDC_LBL_CRADIUS IDC_EDT_CRADIUS IDC_BTN_CCALC IDC_LBL_CCIRC IDC_EDT_CCIRC IDC_LBL_CAREA IDC_EDT_CAREA IDC_LBL_VRADIUS IDC_EDT_VRADIUS IDC_LBL_HRADIUS IDC_EDT_HRADIUS IDC_BTN_ECALC IDC_LBL_ECIRC IDC_EDT_ECIRC IDC_LBL_EAREA IDC_EDT_EAREA Caption &Radius: &Calculate Circum&ference: &Area: Radiu&s: Ra&dius: Calc&ulate Circu&mference: Ar&ea: Class Static Edit Button Static Edit Static Edit Static Edit Static Edit Button Static Edit Static Edit Style 0x50010000 0x50810000 0x50010000 0x50010000 0x50810000 0x50010000 0x50800000 0x50010000 0x50810000 0x50010000 0x50810000 0x50010000 0x50010000 0x50810000 0x50010000 0x50810000 ExStyle 0x0 0x0 0x0 0x0 0x1 0x0 0x1 0x0 0x0 0x0 0x0 0x0 0x1 0x1 0x0 0x0 11 Save All Just in case you may have change the contents of the clipboard, click the body of the dialog box, press Ctrl + A and press Ctrl + C 12 Open the IDD_G3D dialog box Delete its control and set its size to 320 x 210 Then, click its body 13 Press Ctrl + V to paste 14 Select a left and right combination of controls on the dialog box Copy and past them under the existing controls © FunctionX, Inc 413 Chapter 15: Fundamental Controls Visual C++ and MFC Fundamentals 15 Change the IDs, captions and styles of the controls as follows (from left to right and up -> down): ID IDC_LBL_USIDE IDC_EDT_USIDE IDC_BTN_UCALC IDC_LBL_UAREA IDC_EDT_UAREA IDC_LBL_UVOL IDC_EDT_UVOL IDC_LBL_BLENGTH IDC_EDT_BLENGTH IDC_LBL_BHEIGHT IDC_EDT_BHEIGHT IDC_LBL_BWIDTH IDC_EDT_BWIDTH IDC_BTN_BCALC IDC_LBL_BAREA IDC_EDT_BAREA IDC_LBL_BVOL IDC_EDT_BVOL Caption &Side: &Calculate &Area: &Volume: &Length: &Height: &Width Calc&ulate A&rea: V&olume: Class Static Edit Button Static Edit Static Edit Static Edit Static Edit Static Edit Button Static Edit Static Edit Style 0x50010000 0x50810000 0x50010000 0x50010000 0x50810000 0x50010000 0x50800000 0x50010000 0x50810000 0x50010000 0x50810000 0x50010000 0x50810000 0x50010000 0x50010000 0x50810000 0x50010000 0x50810000 ExStyle 0x0 0x0 0x0 0x0 0x1 0x0 0x1 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x1 0x1 0x0 0x0 16 Close MSVC completely When asked to save, click Yes as many times as you are asked 14.1.2 Control's Control Variables: After visually adding a control to your application, if you want to refer to it in your code, you can declare a variable based on, or associated with, that control The MFC library allows you to declare two types of variables for some of the controls used in an application: a value or a control variables A control variable is a variable based on the class that manages the control For example, a static control is based on the CStatic c lass Therefore, if you add a static control to your 414 © FunctionX, Inc Visual C++ and MFC Fundamentals Chapter 15: Fundamental Controls application and you want to refer to it later on, you can declare a CStatic variable in the header of its parent window Here is an example: class CExchangeDlg : public CDialog { public: CStatic stcAdvanced; }; The control variable is available for all controls It is a habit for Visual C++ programmers to start the name of a control variable with m_ Practical Learning: Adding Control Variables Open MSVC again and open the Geometry application created above If you not have it, open the Geometry2 application that accompanies this book Open the IDD_QUADRILATERAL dialog box and adjust the locations and dimensions as you see fit or open the Geometry2 application that accompanies this book To declare a control variable, on the dialog box, right-click the edit box on the right side of Side and click Add Variable… In the Add Member Variable Wizard, in the Variable Type combo box, select CEdit in the Category combo box, select Control in the Va riable Name edit box, type m_SquareSide © FunctionX, Inc 415 Chapter 15: Fundamental Controls Visual C++ and MFC Fundamentals Click Finish In the same way, Add a CEdit Control Variable for each edit box on the propety page and name them, from top to bottom, as m_SquarePerimeter, m_SquareArea, m_RectLength, m_RectHeight, m_RectPerimeter, and m_RectArea Open the Quadrilateral.h file and check that the variables were added: // Quadrilateral.h : header file // #pragma once #include "afxwin.h" // CQuadrilateral dialog class CQuadrilateral : public CPropertyPage { // Construction public: CQuadrilateral(CWnd* pParent = NULL); // standard constructor // Dialog Data enum { IDD = IDD_QUADRILATERAL }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support // Implementation protected: HICON m_hIcon; // Generated message map functions virtual BOOL OnInitDialog(); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() public: // The control variables for the square CEdit m_SquareSide; CEdit m_SquarePerimeter; CEdit m_SquareArea; // The control variables for the rectangle CEdit m_RectLength; 416 © FunctionX, Inc Visual C++ and MFC Fundamentals }; Chapter 15: Fundamental Controls CEdit m_RectHeight; CEdit m_RectPerimeter; CEdit m_RectArea; Save All 14.1.3 The Control’s Data Exchange After declaring the variable, you must “map” it to the control it is referring to, otherwise the variable would behave just like any other and it cannot directly access the control you wanted it to refer to To specify what control your variable refers to, you must call the DDX_Control() framework function The syntax of this function is: void AFXAPI DDX_Control(CDataExchange* pDX, int nIDC, CWnd& rControl); The first argument, pDX, is a pointer to CDataExchange The nIDC argument is the identifier of the control your variable will refer to The rControl argument is the name you gave to your variable An example of calling this function would be: DDX_Control(pDX, IDC_STATIC_ADV, stcAdvanced); The pDX argument in reality handles the mapping It creates the relationship between your rControl variable and the nIDC control that rControl must refer to Besides that, pDX insures that information can flow easily between both entities The CDataExchange class, to which the pDX argument points, is a parent-less class, meaning it is based neither on CObject nor on CWnd The DDX_Control() function can be called for each variable you intend to map to a control When calling any of these functions, the mappings must be performed in the CWnd::DoDataExchange() event Its syntax is: virtual void DoDataExchange(CDataExchange* pDX); As you can see, this event is passed a CDataExchange pointer This pointer in turn will become the first argument to the DDX_Control() function Behind the scenes, this allows the dialog box, the parent of the controls to better manage the exchange of information between the application and the controls This review helps to have an idea of how variables would be declared and associated with the intended controls In reality, this job is usually, and should always be, handled by Visual C++ for you When you create a dialog-based object, such as a dialog box, a form, a property sheet, or a property page, the CDialog::DoDataExchange() event is created and made ready for you To declare a variable you want to associate to a control, unless you have a good reason to proceed manually, use either the ClassWizard in MSVC or the Add Member Variable Wizard in MSVC to add the variable When you this, the wizard will take care of all the mapping for you After adding a variable using a wizard and once the variable mapping has been performed, if you change the name of the variable in the header file, you must manually change its name in the DoDataExchange() event © FunctionX, Inc 417 Chapter 15: Fundamental Controls Visual C++ and MFC Fundamentals Practical Learning: Checking Control Variables Open the Quadrilateral.cpp file and check the DoDataExchange event: void CQuadrilateral::DoDataExchange(CDataExchange* pDX) { CPropertyPage::DoDataExchange(pDX); DDX_Control(pDX, IDC_EDT_SSIDE, m_SquareSide); DDX_Control(pDX, IDC_EDT_SPRM, m_SquarePerimeter); DDX_Control(pDX, IDC_EDT_SAREA, m_SquareArea); DDX_Control(pDX, IDC_EDT_RLENGTH, m_RectLength); DDX_Control(pDX, IDC_EDT_RHEIGHT, m_RectHeight); DDX_Control(pDX, IDC_EDT_RPRM, m_RectPerimeter); DDX_Control(pDX, IDC_EDT_RAREA, m_RectArea); } Save All 14.1.4 Control’s Value Variables Another type of variable you can declare for a control is the value variable Not all controls provide a value variable The value variable must be able to handle the type of value stored in the control it is intended to refer to For example, because a text -based control is used to handle text, you can declare a text -based data type for it This would usually be a CString variable Here is an example: class CExchangeDlg : public CDialog { public: CStatic stcAdvanced; CString strFullName; }; On the other hand, as we will learn that some controls handle a value that can be true or false at one time, namely the check box, you can declare a Boolean variable for such controls Some other controls are used to hold a numeric value such as a natural or a floating-point number You can declare an integer-based or a float-based value variable for such controls Some other controls are not meant to hold an explicit or recognizable type of data, an example would be the tab control For such controls, there is no value variable available For such controls, you can only declare a control variable After declaring the value variable, as done with the control variable, you must “map” it to the intended control To this, you must call an appropriate framework function The functions are categorized based on the type of data held by the variable For example, if the control holds text and you had declared a CString value variable for it, you can call the DDX_Text() function to map it The DDX_Text() function is provided in various versions as follows: void void void void void void void void 418 AFXAPI DDX_Text( CDataExchange* pDX, int nIDC, BYTE& value ); AFXAPI DDX_Text( CDataExchange* pDX, int nIDC, short& value ); AFXAPI DDX_Text( CDataExchange* pDX, int nIDC, int& value ); AFXAPI DDX_Text( CDataExchange* pDX, int nIDC, UINT& value ); AFXAPI DDX_Text( CDataExchange* pDX, int nIDC, long& value ); AFXAPI DDX_Text( CDataExchange* pDX, int nIDC, DWORD& value ); AFXAPI DDX_Text( CDataExchange* pDX, int nIDC, CString& value ); AFXAPI DDX_Text( CDataExchange* pDX, int nIDC, float& value ); © FunctionX, Inc Visual C++ and MFC Fundamentals Chapter 15: Fundamental Controls void AFXAPI DDX_Text( CDataExchange* pDX, int nIDC, double& value ); void AFXAPI DDX_Text( CDataExchange* pDX, int nIDC, COleCurrency& value ); void AFXAPI DDX_Text( CDataExchange* pDX, int nIDC, COleDateTime& value ); The first argument, pDX, is a pointer to CDataExchange The second argument, nIDC, is the identifier the of the control you want to refer to The third argument, value, if the name of the value variable you had previously declared If a control holds a valid value, you can declare both a control variable and a value variable if you need them Here is an example: class CExchangeDlg : public CDialog { public: CStatic stcAdvanced; CString valAdvanced; CString strFullName; }; When mapping such variable, make a call to DDX_Control() for the control variable and a separate call to DDX_X() for the value variable Because there are various types of DDX_ functions for the controls, X stands for the type of control referred to Like the DDX_Control(), the DDX_ X() function can be called for each variable you intend to map to a control and this is performed in the CWnd::DoDataExchange() event This CDataExchange pointer is passed as the first argument to the DDX_ X() functions This argument allows the dialog boxto update the values held by the controls Once again, avoid declaring and mapping value variables manually Instead, use either (MSVC 6) the ClassWizard or (MSVC 7) the Add Member Variable Wizard to add a variable that you want to associate with a control on a dialog-based object Practical Learning: Adding Value Variables From the Resource View tab, open the IDD_CIRCULAR dialog box and adjust the locations and dimensions of the controls as you see fit To declare a value variable, on the dialog box, right-click the edit box on the right side of the top Radius and click Add Variable… © FunctionX, Inc 419 Chapter 15: Fundamental Controls Visual C++ and MFC Fundamentals Click the arrow of the Category combo box and select Value In the Variable Type combo box, if necessary, select CString In the Variable Name edit box, type m_szCircleRadi us Click Finish In the same way, Add a CString Value Variable for each edit box From top to bottom, name them m_szCircleCircumference, m_szCircleArea, m_szEllipseradius, m_szEllipseRadius, m_szEllipseCircumference, and m_szEllipseArea respectively Open the Circular.h header file and check the new value variable Also open the Circular.cpp source file and check the content of the DoDataExchange() event 14.1.5 Controls Event Handlers As we reviewed when introduced messages, an event is an action resulting from a generated message As there are various types of controls, there are also different types of messages available on controls and even the type of window considered After adding a control to your application, whether you visually added it or created it dynamically, you will also decide how the handle the possible actions that the user can perform on the control There are various ways you can write an event for a message If the message is sent from a class, first select the class in Class View Then, in the Properties window, click either the Events , the Messages , or the Overrides of member function you want to write button, depending on the type Some of the most regular actions the user can perform on a control is to click it Object that receive click messages range from static controls to command buttons, as we will see eventually To generate code for a control that is positioned on a dialog-based object, display its parent window Then, right-click the control and, on the context menu, click Add Event Handler This would display the Event Handler Wizard with as much information as you need to set or select to configure the event Once you have finish specifying the necessary items for the event, click Add And Edit This would take you to the new event in the Code Editor where you can write the rest of the code for the event If you use the wizard to generate the event, the necessary arguments and part of the code for 420 © FunctionX, Inc Visual C++ and MFC Fundamentals Chapter 15: Fundamental Controls the event would be supplied to you If you decide to write the event from scratch, you will need to especially remember the arguments of the event, otherwise the code may not work at all Practical Learning: Generating Events From the Resource View tab, display the IDD_QUADRILATERAL dialog box On the dialog, right-click the edit box on the right side of Side and click Add Event Handler… See the types of messages for the edit box Click Cancel Right-click the top Calculate control and click Add Event Handler… Read and accept everything that is set on the wizard Click Add And Edit In the same way, generate an event for all Calculate controls of the IDD_QUADRILATERAL, the IDD_CIRCULAR, and the IDD_G3D dialog boxes Save All 14.2 Controls Management 14.2.1 Control’s Identification So far, we have seen various techniques of visually adding or dynamically creating controls for your application We also saw that every control should have identification In fact, if you want to avoid providing an identification for a control, you may have to use the Win32 API’s CreateWindow() or CreateWindowEx() function to create the control We saw that the identifier can be created either using the Properties window, using the Resources Symbols dialog box, using the String Table, or when dynamically creating the control We also saw how to declare or add a control variable for an MFC control © FunctionX, Inc 421 Chapter 15: Fundamental Controls Visual C++ and MFC Fundamentals Imagine a control with its identifier has already been added to a dialog box but you want to change that identifier, for any reason you judge necessary To this, you would call the CWnd::SetDlgCtrlID() method Its syntax is: int SetDlgCtrlID(int nID); This method assigns or changes the identifier of the control that called it Imagine the control variable of a control is named m_Edition, you can assign a new identifier to it as follows: BOOL CDlgControls::OnInitDialog() { CDialog::OnInitDialog(); // TODO: Add extra initialization here m_Edition.SetDlgCtrlID(0x1882); } return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE If a control has been created and you need its identifier, you can find it out by calling the CWnd::GetDlgCtrlID() method Its syntax is: int GetDlgCtrlID() const; This method simply returns the identifier of the control that called it The value is returned as an integer You will mostly use this method to find out if the identifier of a control matches the one the user has just accessed Practical Learning: Using Controls Identifiers Delete the TODO line, the OK and the Cancel buttons From the Controls toolbox, add four Custom Controls horizontally on the dialog box as follows: 422 Start a new MFC Application named Identification and create it as Dialog Based without an About Box Set the Class name of the top three custom controls to ScrollBar and draw them © FunctionX, Inc Visual C++ and MFC Fundamentals Chapter 15: Fundamental Controls Set the Class name of the bottom custom control to Static and change its Caption to Burkina Faso Add a CScrollBar Control Variable to the top control and name it m_ScrollRed (if you are using some versions of MSVC and the class name is not selected as CScrollBar, you may have to type it; if you are using MSVC 6, you should only select the class name as CScrollBar) Add a CScrollBar Control Variable to the second control from top and name it m_ScrollGreen Add a CScrollBar Control Variable to the third control fro m top and name it m_ScrollBlue A CStatic Control Variable (if you are using some versions of MSVC and the class name is not selected as CStatic, you can to type it; if you are using MSVC 6, you should select the CStatic class name) to the bottom control and name it m_Country 10 To change the identifiers of the top three controls, access the OnInitDialog() event of the dialog box and call the SetDlgCtrlID() method for each control giving the 1804, 6255, and 42 values respectively: BOOL CIdentificationDlg::OnInitDialog() { CDialog::OnInitDialog(); // Set the icon for this dialog The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here m_ScrollRed.SetDlgCtrlID(1804); m_ScrollGreen.SetDlgCtrlID(6255); m_ScrollBlue.SetDlgCtrlID(42); return TRUE; // return TRUE unless you set the focus to a control } 11 When the user clicks one of the scroll bars, we will need to know which one was clicked and display a message accordingly Using the Messages button , generate the WM_HSCROLL message for the dialog box to an OnHScroll event 12 Implement it as follows: void CIdentificationDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { // TODO: Add your message handler code here and/or call default CFont font; // Change the font of the label based on which scroll bar was clicked if( pScrollBar->GetDlgCtrlID() == 1804 ) { font.CreatePointFont(260, "Garamond"); m_Country.SetFont(&font); } else if( pScrollBar->GetDlgCtrlID() == 6255 ) { font.CreatePointFont(220, "Arial"); m_Country.SetFont(&font); © FunctionX, Inc 423 Chapter 15: Fundamental Controls Visual C++ and MFC Fundamentals } else if( pScrollBar->GetDlgCtrlID() == 42 ) { font.CreatePointFont(180, "Wingdings"); m_Country.SetFont(&font); } CDialog::OnHScroll(nSBCode, nPos, pScrollBar); } 13 Test the application by executing it and trying to click the arrow buttons on the scroll bar controls 14 Close the application and return to MSVC 14.2.2 The Client Area Besides the identifier, we learned that, to create a control, you must provide it with “physical” presence If you add the control visually, it assumes the position where you place it If you are programmatically creating the control, you must know how much space its parent window is making available to its children before even deciding about its location and dimensions To provide its parental support to the child controls, the parent window allocates an amount of space in a rectangular shape called the client area: 424 © FunctionX, Inc Visual C++ and MFC Fundamentals Chapter 15: Fundamental Controls The controls you add are confined to the client area offered by the parent window After visually adding a control to a parent window, it assumes a location and takes some dimensions in this area The origin of the rectangular client area is on the upper-left corner of the parent window The horizontal measurements move from the origin to the right The vertical measurements move from the origin to the bottom: A control can be added only in the client area To find out how much room a parent window is making available to its children, that is, to get the dimensions (and screen location) of the client area, you can call the CWnd::GetClientRect() member function Its syntax is: void GetClientRect(LPRECT lpRect) const; This member function takes as argument a RECT or CRect variable and stores the location and dimension of the client rectangular area in it In the following example, the © FunctionX, Inc 425 ... 380 Visual C++ and MFC Fundamentals Return to MSVC © FunctionX, Inc Visual C++ and MFC Fundamentals Chapter 12: Dialog-Based Windows Chapter 13: Control Design ? Forms ? Dialog Box’ Messages and. .. application 363 Chapter 12: Dialog-Based Windows Visual C++ and MFC Fundamentals Close it and return to MSVC 12.2.2 The MFC Wizard for a Dialog-Based Application Microsoft Visual C++ provides... a dialog box 366 © FunctionX, Inc Visual C++ and MFC Fundamentals Chapter 12: Dialog-Based Windows Practical Learning: Adding a Dialog Box to an Existing Application Start a new MFC Application