15. microsoft foundation classes - simon fraser … russell tront, 2000 . page 15-3 section table of...

45
© Copyright Russell Tront, 2000 . Page 15-1 15. Microsoft Foundation Classes This section of the notes presumes the student has already been familiarized with some general principles of GUI programming, and with a very brief introduction to MS- Windows programming using C, rather than C++ and MFC. It will therefore dive directly into a simple MFC program. © Copyright Russell Tront, 2000 . Page 15-2 Readings : Chapter 2, 3, and 4, of [Deitel2000] References: Note that [Deitel2000] above is just a small, inexpensive intro to MFC. There are also dozens of large books on MFC. Most have titles containing the phrase “Visual C++”. These usually have nothing to do with learning C++ and are mostly about using the Microsoft Visual Studio and MFC to write Windows programs. Most of them are probably ok. Here are some of the books on MFC that I think are very good: [Prosise99] “Programming Windows with MFC, 2nd ed.” by Jeff Prosise, Microsoft Press, 1999. This is a huge, well- respected bible on MFC programming. [Schildt98] “MFC Programming from the Ground Up, 2nd ed.” by Herbert Schildt, McGraw-Hill, 1998. I have always liked Schildt’s books and we used his non-MFC Windows programming book as a textbook in earlier offerings of Cmpt 212.

Upload: dangdang

Post on 02-Apr-2018

222 views

Category:

Documents


2 download

TRANSCRIPT

Page 1: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-1

15. Microsoft Foundation Classes

This section of the notes presumes the student has already been familiarized with some general principles of GUI programming, and with a very brief introduction to MS-Windows programming using C, rather than C++ and MFC. It will therefore dive directly into a simple MFC program.

© Copyright Russell Tront, 2000 . Page 15-2

Readings:

Chapter 2, 3, and 4, of [Deitel2000]

References:

Note that [Deitel2000] above is just a small, inexpensive intro to MFC. There are also dozens of large books on MFC. Most have titles containing the phrase “Visual C++”. These usually have nothing to do with learning C++ and are mostly about using the Microsoft Visual Studio and MFC to write Windows programs. Most of them are probably ok.

Here are some of the books on MFC that I think are very good:

[Prosise99] “Programming Windows with MFC, 2nd ed.” by Jeff Prosise, Microsoft Press, 1999. This is a huge, well-respected bible on MFC programming.

[Schildt98] “MFC Programming from the Ground Up, 2nd ed.” by Herbert Schildt, McGraw-Hill, 1998. I have always liked Schildt’s books and we used his non-MFC Windows programming book as a textbook in earlier offerings of Cmpt 212.

Page 2: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-3

Section Table Of Contents 15. MICROSOFT FOUNDATION CLASSES ...... 1

15.1 A FIRST MFC PROGRAM............................................ 4

15.2 MICROSOFT CODING CONVENTION .......................... 11

15.3 ANOTHER NOTE ....................................................... 12

15.4 WINDOWS MESSAGES............................................... 14

15.5 USING A WM_PAINT PROGRAM ARCHITECTURE .... 15

15.6 HANDLING MOUSE EVENTS ..................................... 26

15.7 SENDMESSAGE VS. POSTMESSAGE .......................... 36

15.8 MESSAGE BOXES...................................................... 37

15.9 MENUS..................................................................... 42

15.9.1 Closing A Program................................................... 57

15.10 DIALOG BOXES......................................................... 58

15.11 CONTROLS................................................................ 59

15.12 HANDLING CONTROLS WITHIN A DIALOG BOX ......... 62

15.13 CONTROLS ARE CHILD WINDOWS............................. 63

15.14 DEFINING A DIALOG RESOURCE............................... 64

15.15 DIALOG BOX COMPLETION HANDLING..................... 70

15.16 THE LIFE OF A DIALOG............................................. 72

15.17 A COMPLETE DIALOG EXAMPLE............................... 76

15.18 DIALOG DATA EXCHANGE (DDE)/DD VALIDATION.. 86

15.19 MODELESS DIALOG BOXES AND DESTRUCTION........ 90

© Copyright Russell Tront, 2000 . Page 15-4

15.1 A First MFC Program

Because this section presumes you have seen the non-MFC Windows program written in C in a previous section of the lectures, let’s dive right in and look at a program with similarly functionality. In particular, we will be looking to see if it is simpler and shorter because it can use object-orientation and MFC.

To minimize the size of your final .exe file, and to speed up compiles/builds, it is best to adjust your project to use the MFC code in a dynamic link library, rather than compiling and hard linking it all into your program. To do this use the Visual C++ menu:

Project > Settings > (make sure the root of the tree on the left is selected) > General Tab (on the right) > Microsoft Foundation Classes > Use MFC in a Shared DLL.

The above setting may not be suitable for running the final application on a machine that does not have Visual C++ installed on it, but is certainly the best setting for development (Note that on the left there is a drop down box that allows you to change the setting for a release rather than debug/development build).

Now, here’s the code. MFC code is usually in two parts, a .h file and a .cpp file.

Page 3: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-5

//Filename: minimal.h //Provides a minimal MFC program. //------------------------------ //The application is an instance of: class CMyApp : public CWinApp { public: virtual BOOL InitInstance (); }; //------------------------------ //Main window frame is an instance of: class CMyMainWindow : public CFrameWnd { public: CMyMainWindow (); };

© Copyright Russell Tront, 2000 . Page 15-6

//Filename: minimal.cpp #include <afxwin.h> #include "minimal.h" //Define global variable of type CMyApp //which is the actual application! CMyApp myApp; //================================= // CMyApp member functions. BOOL CMyApp::InitInstance () { //overridden to create my //class of main window frame. m_pMainWnd = new CMyMainWindow; m_pMainWnd->ShowWindow (m_nCmdShow); m_pMainWnd->UpdateWindow (); return TRUE; } //================================= // CMyMainWindow constructor. CMyMainWindow::CMyMainWindow (){ Create (NULL, _T ("Minimal MFC Skeleton"), WS_OVERLAPPEDWINDOW, CRect(100,200,600,300)); }

Page 4: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-7

Ok, now for some analysis of this code.

Note that you must #include <afxwin.h> in your .cpp file. And you must do it before #including your header file (otherwise you will have to include it into your header file as well). <afxwin.h> is the master header file containing most of MFC. I believe that “afx” stands for Application Frameworks Extension (a large class library is sometimes called a frameworks). This header file in turn likely includes <windows.h>.

You normally need at least two classes in an MFC program. You must pay close attention for the next few hours of lectures as to whether I am referring at any given moment to the ‘application’ class or the ‘window’ class.

One class in the code above, CMyApp (or whatever name you want), represents your program (and not any particular window class, not even your main window class). CMyApp is a subclass of CWinApp that is provided by MFC. I suspect that it is in a .cpp module that also contains or has access to a WinMain( ) function provided by MFC.

The second class, CMyMainWindow (or whatever name you want), defines the particular nature of your main application window (e.g. does it have a thick border, does it have a toolbar, what application data does it show inside the window).

© Copyright Russell Tront, 2000 . Page 15-8

Here is how your program starts: 1) Notice that the .cpp file defines an actual variable of type

CMyApp. This triggers the CMyApp constructor while the program is loading and before the WinMain function is even called. Since we have not defined a CMyApp constructor, the implicit constructor for our application class chains to the base class constructor from CWinApp. This constructor apparently stores the address of our instance, myApp, somewhere where it can be found by WinMain( ). Perhaps it is stored in some suitably-named global variable that application programmers do not know about, but the WinMain( ) that comes with MFC does know about.

2) WinMain( ) is called. For an MFC application, this function is provided by MFC. This function is pre-programmed to find our myApp object and call its InitInstance( ) function.

3) Within the InitInstance( ) function, which we have overridden from the parent CWinApp class, we write:

m_pMainWnd = new CMyMainWindow; This creates an instance of the main window class that we have written.

4) The constructor is programmed to call the inherited Create( ) member function. The first two parameter give the window a containment (not inheritance) parent (use NULL for the desktop) and a title. Note that the macro _T is to make the string character set neutral (ASCII or UNICODE). During a build, if you used this macro, you can specify what type of character set application you want. If you don’t use the _T macro, the string will be ASCII (which is nice because it will run on either Windows 98 or Windows NT/2000/XP, but unfortunately will run slightly slower on NT/2000/XP

Page 5: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-9

because it has to be continually converting the ASCII to UNICODE).

5) The second two parameters of the Create( ) function are optional (remember optional parameters?) and indicate a window type, and a location/size.

CRect(100,200,600,300)); The above statement triggers a constructor of the CRect class that returns a CRect object that contains the x-position of the top left corner of the window to be created, the y-position of the top left corner of the window measured down from the top of (in this case) the screen, the width, and the vertical size of the window to be created, measured in pixels. This anonymously named object is passed to the Create( ) function as the 4th parameter.

6) The next three statements of the InitInstance( ) function of our CMyApp (not CMyMainWindow) class actually draw the main window.

m_pMainWnd->ShowWindow (m_nCmdShow); m_pMainWnd->UpdateWindow (); return TRUE;

Note that m_pMainWnd is an inherited member attribute of our application object that is a pointer to the main window, and which we ourselves set to point at the window we were constructing.

We tell our window to show itself. m_nCmdShow tells the ShowWindow function whether we want it to show minimized, full screen, or normal sized. Note the size we specified above just indicated what normal size would be.

© Copyright Russell Tront, 2000 . Page 15-10

7) Finally in the InitInstance( ) function, we call UpdateWindow( ), which is a very common call in MS-Windows. Remember Model/View/Controller (MVC)? When the controller updates the model, it tells all the view windows to update themselves by calling UpdateWindow( ) on each one. This causes a paint message to be send to each window, which in MFC will cause the OnPaint( ) instance member function of each particular window to be called.

So that is how the start-up of an MFC Windows program flows. You see that you need to understand C++ thoroughly in order to follow what is happening. Later you will see more heavy duty C++ coding including initialization lists, etc.

Note that there is a slightly more detailed description of how an MFC application starts in [Prosise1999] on page 21-22.

Page 6: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-11

15.2 Microsoft Coding Convention

Microsoft has adopted a coding convention that is copied by many programmers. It is controversial, as not everyone likes it. There are several aspects to it: � All class names begin with an upper case C. All embedded

words, including the first word following the ‘C’ begin with an upper case letter.

� Function names also begin with an upper case letter. � Member attribute (often also called data member) names in a

class are usually prefixed with “m_” to differentiate them from local, module, and global variables.

� Almost all variable names use so-called Hungarian notation, which was probably developed by some Hungarian person with a perhaps otherwise unpronounceable name.

Hungarian notation is considered helpful by some programmers because it allows you to tell whether a variable called, say, myObject, is an actual object or a pointer to an object (e.g. pMyObject). Please read carefully Section 2.5 of [Deitel2000] to understand the prefixes commonly used. These prefixes are often compounded together resulting in such awful variable names as: m_lpszCustomer

which means member attribute which is a long pointer to a string that uses a zero (i.e. nul ‘\0’) termination delimiter. You don’t have to follow this convention, but you may have to read it, and use MFC supplied names like this.

© Copyright Russell Tront, 2000 . Page 15-12

15.3 Another Note

Your textbook architects the header and .cpp file slightly differently.

First, it is not necessary to have a .h file at all. You can declare/define both classes in one .cpp file. However, if you have several different window classes (for each different type of window in your application), they are often each put in their own separate module’s .h and .cpp files. And they may need the name of the main window class, so the main window class name is usually exported from the main .cpp file by an associated main .h file. Note I am talking about C++ classes, here, and not just the concept of window types.

Your textbook author also does NOT declare the application class in main program header file. I am not sure whether there is every a reason for it to be exported, but several other textbooks, including [Prosise1999] from Microsoft Press, do at least ‘declare’ it in the main .h file.

Also, your textbook declares and defines the application class, and in the very same statement defines an instance of it. This is very messy coding. e.g.

Page 7: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-13

class CWelcomeApp : public CWinApp { public: BOOL InitInstance() { m_pMainWnd = new CWelcomeWindow(); m_pMainWnd->ShowWindow( m_nCmdShow ); m_pMainWnd->UpdateWindow(); } } myApp; // instantiate application

This declares, names, and defines the functions of the application class, and at the very end also instantiates one. It would have been a bit nicer to write these separately: class CWelcomeApp : public CWinApp { public: BOOL InitInstance() { m_pMainWnd = new CWelcomeWindow(); m_pMainWnd->ShowWindow( m_nCmdShow ); m_pMainWnd->UpdateWindow(); } }; CWelcomeApp myApp; // instantiate application

On the other hand, most of this application class code is the same in every Windows application, and he admirably writes it very compactly and puts it at the very end of the main .cpp file, because rarely does anyone want to look at it or change it anyway!

© Copyright Russell Tront, 2000 . Page 15-14

15.4 Windows Messages

Windows sends to your program (i.e. to the message loop and subsequently the individual window class’s) many messages about (mainly input) events that have happened onscreen. Some were actually done to windows of another program, but required action also by your program. e.g. Another program’s window being killed, thus uncovering your window.

Windows has many message types, each specified by an integer. Rather than memorizing these integers, each has been defined as a symbolic macro constant with a meaningful name. All begin with “WM_”. Examples are:

� WM_PAINT - request for a repaint . � WM_MOVE - notification of a window being moved. � WM_CLOSE - notification of a window instance being closed. � WM_COMMAND - menu leaf activated. � WM_SIZE - window has been resized. � WM_LBUTTONDOWN - left mouse button has been pressed. � WM_LBUTTONUP - left mouse button released. � WM_CHAR - keyboard character has been pressed.

Page 8: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-15

15.5 Using a WM_PAINT Program Architecture

Note: The above windows message name has an underscore in it: WM_PAINT.

In the constructor of our main window instance, we could code to display stuff in the window. However, if the user were to minimize that window, then restore it, the stuff would NOT be re-drawn because the constructor is not rerun. This is also because Windows does NOT make the effort to remember every pixel of every minimized or obscured window.

There are exceptions to this non-update behavior, as shown in Figure 2.8 of [Deitel2000], because that example uses a child window IN FRONT of (and owned by) our main window. When our window becomes re-visible, the window tells the child to re-display itself, and Microsoft has programmed that kind of static text control child so it responds properly to a WM_PAINT. However, if you were to write or draw directly on the main window (from the constructor or other function), it would not display itself properly any time it becomes visible.

You can get an example of this from [Schildt98]. Get the file below:

© Copyright Russell Tront, 2000 . Page 15-16

http://www.osborne.com/products/0078825733/0078825733_code.zip

Then, extract the file called Chap3.lst, and pull out listing 6 (message1.h) and listing 7 (message1.cpp). This example handles key press messages, and puts them on the screen, however, it does not handle re-paints properly. Your instructor might demo this.

To get around this problem, you have to program your window class to respond to a particularly important message that Windows sends with re-displaying a window. Following the Model/View/Controller paradigm, the window system will send your application a WM_PAINT message if it is restored from a minimized state, or uncovered, etc. Now we want to see how we can write our program to handle this message.

Page 9: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-17

//Filename: paint.h //A program that repaints properly. //------------------------------ //The application is an instance of: class CMyApp : public CWinApp { public: virtual BOOL InitInstance (); }; //------------------------------ //Main window frame is an instance of: class CMyMainWindow : public CFrameWnd { public: CMyMainWindow (); protected: afx_msg void OnPaint (); DECLARE_MESSAGE_MAP () };

Notice the 3 statements added just above.

© Copyright Russell Tront, 2000 . Page 15-18

//Filename: paint.cpp #include <afxwin.h> #include paint.h" //Define global variable of type CMyApp //which is the actual application! CMyApp myApp; //================================= // CMyApp member functions. BOOL CMyApp::InitInstance () { //overridden to create my //class of main window frame. m_pMainWnd = new CMyMainWindow; m_pMainWnd->ShowWindow (m_nCmdShow); m_pMainWnd->UpdateWindow (); return TRUE; } //================================= // CMyMainWindow constructor. CMyMainWindow::CMyMainWindow (){ Create (NULL, _T ("Minimal MFC Skeleton"), WS_OVERLAPPEDWINDOW, CRect(100,200,600,300)); }

Page 10: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-19

//---------------------------------- //Message Map for CMyMainWindow class. BEGIN_MESSAGE_MAP(CMainWindow,CFrameWnd) ON_WM_PAINT () END_MESSAGE_MAP () //---------------------------------- void CMainWindow::OnPaint () { CPaintDC dc (this); CRect rect; GetClientRect (&rect); dc.DrawText(_T ("Hello, MFC"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER); }

Note that we have added a declaration for something called a message map in the .h file, and an message map implementation in the .cpp file. A message map is a way to avoid having lengthy virtual dispatch tables for every class for every possible message type that might be received (and Windows has lots of different message types). They are implemented as a bunch of C++ pre-processor macros. Note that message maps are not necessary to do C++ object-

© Copyright Russell Tront, 2000 . Page 15-20

oriented Windows programming. For instance, Borland C++ Builder does not use them for its Object Windows Library (which is a competitor to MFC). Message maps are unique to MFC.

A message map must be declared in the .h file using the: DECLARE_MESSAGE_MAP ()

macro function and must not be private.

The beginning and end of the message map is delimited by similar macro functions in the .cpp file. e.g. BEGIN_MESSAGE_MAP(CMainWindow,CFrameWnd) ON_WM_PAINT () END_MESSAGE_MAP ()

The beginning of the message map must be parameterized by the window class name, and the window’s parent class name.

Between the beginning and end of a message map, the programmer lists the names of special macros associated with each kind of Windows message that she/he want to specifically handle (as opposed to let Windows handle in whatever way Windows defaults to).

If you know the symbolic names of the message you want to handle (e.g. WM_CHAR), add a message map entry for that

Page 11: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-21

message type named ON_WM_CHAR( ). Note the “ON_” prefix, and the parentheses.

What this does, ironically, is cause MFC to change the message into a call to the virtual function named OnChar( ). Note the naming, the absence of “_WM_”, and the case of the letters in the name. Given any WM_ message name, you can reliably infer the name of the message map macro needed and the name of the virtual function needed in your program. A Windows program my have many window classes, each class has a message map, and for each entry in that message map there is a corresponding virtual function definition. If the message concerned a particular window instance called, perhaps myWin, then the message would be changed into the function call:

myWin->OnChar( )

One trick to declaring a message handling member function in the header file is figuring out what the parameters will be. If you look up the function name in Visual C++ online help, you will quickly get this information. � For OnPaint( ), the parameter list is empty. � OnChar(UINT ch, UINT count, UINT flags); � OnLMouseButtonDown(UNIT flags, CPoint location);

UINT is a macro preprocessor constant representing an unsigned integer of a variable size (changes with the platform you are compiling for (16-bit Windows 3.1, 32-bit

© Copyright Russell Tront, 2000 . Page 15-22

Windows XP). Microsoft defined macro preprocessor constants for various data types that could easily be changed by conditional compilation for different platforms. Ironically, for this particular one, C++ generally changes the size of the int for you anyway.

OnChar has three parameters: the character key that was pressed, the number of times it occurred because the key was held down, and the flags (generally a 32 bit integer composed of bits that represent whether the ‘alt’ key was down when the key was pressed, the raw keycode, etc.).

The code above demonstrates how to write a message to the middle of the particular window when a WM_PAINT message is received. CPaintDC dc(this); CRect rect; GetClientRect (&rect); dc.DrawText(_T ("Hello, MFC"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);

Before you write to a window, you have to obtain and perhaps modify the ‘device context’ for the window (or printer). The device context contains things like what font and color to write in. The statement: CPaintDC dc(this);

Page 12: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-23

is basically a way to construct a new CPaintDC object named ‘dc’ by triggering the constructor that takes a MFC window instance as its only parameter. Recall that: CPaintDC dc;

will create an instance of class CPaintDC using the default constructor. The constructor used about is a non-default constructor that allows the device context object to know which window instance it is related to.

Next, we create a CRect object, and pass it by address to GetClientRect(&rect), which fills the rect with the sub-area of the window that the user can write into (excluding title bar, menu, borders, etc.).

Finally, we can call one of several device contest functions for putting text in the window. dc.DrawText(_T ("Hello, MFC"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);

The one shown above, DrawText, is particularly flexible in that it allows you to specify whether the text can wrap onto two lines using a carriage return or linefeed embedded in the string, and whether you want Windows to figure out how to center it (rather than you having to do complex calculations

© Copyright Russell Tront, 2000 . Page 15-24

based on the rect object’s attributes and the length of the string in that device context’s font size).

Note the use of the _T macro again, for the first parameter. The second parameter is the length of the string (or -1 to indicate it is nul terminated), then comes the address of the sub-area of the window, and finally some DrawText flags that bit-wise ORed together so they can be passed as one value.

The C/C++ bit-wise OR operator is ‘|’, a vertical bar (though it sometimes appears slightly different on your keyboard key. Let’s look what bit-wise OR operator really means. Though the above constants are likely longer, consider the following 8-bit example of the operator: EIGHT 00001000 TWO 00000010 EIGHT | TWO = 00001010

Finally, we are done. Every time a user minimizes and restores, or otherwise uncovers this particular window, the DrawText function is called again to restore the contents of the window.

Run the above program and see the behavior. Start the program, and note the window contents. Now cover and

Page 13: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-25

uncover the window, or minimize it and restore it. Does it re-paint the window when needed?

In summary, a common Windows program architecture is to have an OnPaint( ) function that properly draws the contents of the window in the first place (when the window is first shown by the call to UpdateWindow( ) in the InitInstance( ) function). Later, this function is re-triggered by two possible sources: 1) The user obscuring and making the window visible again.

This requires no programmer intervention as the window system itself will generate the WM_PAINT message and fire it at your program.

2) When the user of your specific program has triggered a message by either a menu selection, key press, mouse click, etc. YOUR message handlers for those events should update the underlying data model for the window (perhaps what text it is to show in the center, or the location of 3 corners of a triangle to be drawn in the window). Before returning, those handlers should call Invalidate( ) inherited from the CWnd class. This tells Windows to queue a WM_PAINT message to be sent to the window in the near future. This will in turn will cause the redraw of the so-called ‘client area’ of the window using your other, already-written general purpose update function, OnPaint( ). It is best not to call OnPaint( ) directly from your menu, key, and mouse message handlers.

© Copyright Russell Tront, 2000 . Page 15-26

15.6 Handling Mouse Events

A major aspect of GUI programs is handling mouse events (left button down, drag, left button up, double click, etc.). Windows has good facilities to make this easy. First, most mouse actions on title bar minimize/maximize/close buttons, and menus (once you design the menus) are handled by MFC either through inheritance or other avenues. Second, Windows had a well-defined set of pre-defined named message constants for this:

� WM_LBUTTONDOWN � WM_LBUTTONUP � WM_LBUTTONDBLCLK � WM_MBUTTONDOWN � WM_MBUTTONUP � WM_MBUTTONDBLCLK � WM_RBUTTONDOWN � WM_RBUTTONUP � WM_RBUTTONDBLCLK � WM_MOUSEMOVE

In addition, the Window OS and Windows GUI system will automatically trap mouse hardware events and convert and send them to your program message loop as the above standard event messages.

The message map macros for the above messages are just “ON_” prefixed to the above message constants.

Page 14: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-27

The message handling functions are just named similar to the message map macros, except for case and the missing internal substring “_WM_”. For example, the virtual member function that you need to override for left button down is: afx_msg void OnLButtonDown(UINT nflags, CPoint point)

All mouse event handler functions take the same two parameters. The second parameter passed to your overriding function is the location of the mouse event stored in a CPoint object. The mouse drag/move function passes to you only the position where the drag stopped.

To find out if either the control key, or the shift key, or both, were pressed at the same moment that the user clicked the mouse button, the nflags parameter passes you a ‘set’ of possible booleans. These though, in a somewhat compact and C-style manner, are crammed all together into one formal parameter, the UINT nflags. E.g.

00000000 00000000 00000000 00010010

might mean both shift and control keys were down (I am not sure of the exact bit positions for shift and control; I am just guessing. You would have to look them up in windows.h to really know, however no one bothers to do that for the following reason.

© Copyright Russell Tront, 2000 . Page 15-28

There is a named pre-processor constant for each bit position in the nflags parameter. For instance, the fifth bit from the right (i.e. 00010000) might be defined in windows.h as either:

#define MK_CONTROL 0x10

#define MK_CONTROL 16

To figure out if the control key was down, you need to do this: if (nflags & MK_CONTROL) dc.TextOut(1,1,”Control was down”, 16)

This does a bit-wise and between the MK_CONTROL bit field and the nflags parameter. Only if nflags had a ‘1’ bit in the control position would the control expression for this ‘if’ statement be non-zero, and thus considered true.

Now let’s look at some code from your textbook [Deitel2000].

Page 15: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-29

// Fig 3.3: CMouseWin.h // Mouse coordinates display // Copyright 1999 by Deitel & Associates // and Prentice Hall. class CMouseWin : public CFrameWnd { public: CMouseWin(); // display mouse coordinates and mouse status void showPoint( UINT uFlags, CPoint point ); // mouse button handlers afx_msg void OnLButtonDown( UINT uFlags, CPoint point ); afx_msg void OnRButtonDown( UINT uFlags, CPoint point ); private: DECLARE_MESSAGE_MAP() };

© Copyright Russell Tront, 2000 . Page 15-30

// Fig 3.3: mouse.cpp // Mouse coordinates display #include <afxwin.h> #include <strstrea.h> #include "CMouseWin.h" CMouseWin::CMouseWin() { Create( NULL, "Mouse Example", WS_OVERLAPPEDWINDOW ); } // display mouse coordinates and mouse status void CMouseWin::showPoint( UINT uFlags, CPoint point ) { CClientDC dc( this ); // get display context static char sText[ 64 ]; static ostrstream s( sText, sizeof( sText ) ); s.seekp( 0 ); // reset to start of string. // format data to display in sText buffer s << "(" << point.x << ", " << point.y << ")"; // at point (x, y) on screen, //display (x, y) value. dc.TextOut( point.x, point.y, sText, s.pcount() );

Page 16: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-31

s.seekp( 0 ); // reset to start of string. s << ( uFlags & MK_LBUTTON ? '*' : ' ' ) << " Left " << ( uFlags & MK_RBUTTON ? '*' : ' ' ) << " Right "; // at 1,1 on screen, display mouse state dc.TextOut( 1, 1, sText, s.pcount() ); } // left mouse button handler afx_msg void CMouseWin::OnLButtonDown( UINT uFlags, CPoint point ) { showPoint( uFlags, point ); } // right mouse button handler afx_msg void CMouseWin::OnRButtonDown( UINT uFlags, CPoint point ) { showPoint( uFlags, point ); } BEGIN_MESSAGE_MAP( CMouseWin, CFrameWnd ) ON_WM_LBUTTONDOWN() ON_WM_RBUTTONDOWN() END_MESSAGE_MAP()

© Copyright Russell Tront, 2000 . Page 15-32

// load main application window. class CMouseApp : public CWinApp { public: BOOL InitInstance() { m_pMainWnd = new CmouseWin; m_pMainWnd->ShowWindow( m_nCmdShow ); m_pMainWnd->UpdateWindow(); //force refresh. return TRUE; //report success. } } mouseApp;

Your instructor will now discuss this with you. Notice several things: � Notice right above that Deitel defines the body of the

CMouseApp class and instantiates it all in one statement. � Notice that Deitel felt that how the code responded to a left or

right mouse had a lot in common, so he centralized that code in a function called ShowPoint( ). The two message handling functions that receive the mouse events just forward the two parameters to ShowPoint( ) (which, interestingly, can distinguish between a left and mouse click just from the nflags parameter).

� The ShowPoint( ) function uses new style I/O for formatted writing to a string. In C, this can be done with sprintf( ), and in Windows making accommodation for wide Unicode characters with wsprintf( ). However, the new style I/O way of doing this is to construct a new output stream (sort of like cout, but of type ostrstream rather than type ostream). The constructor needs a character array to be written into. Then the programmer just writes into that like it was cout.

Page 17: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-33

� The above code is NOT written in the WM_PAINT architecture, so it does NOT keep a (possibly infinite) list of past mouse click positions that would otherwise be needed.

� Note that if not using the WM_PAINT architecture, there is no need to use CPaintDC (which redraws a whole frame including the frame border and title bar). Instead, you can just use CClientDC that updates the client area directly.

The way that ShowPoint distinguishes between left and right mouse button clicks (which go to separate callback functions) is that the nflags parameter passed to your callback function can have quite a lot of information stuffed into one word: � MK_CONTROL � MK_SHIFT � MK_LBUTTON � MK_MBUTTON � MK_RBUTTON

Because each of OnLButtonDown( ) and OnRButtonDown() receive and pass on the nflags parameter to ShowPoint( ), they do not have to otherwise indicate which one of the callback functions is calling. This is neither good nor bad, but just an interesting observation.

Also, I want to point out that I have not yet shown you a full WM_PAINT architecture program. As mentioned in item 2) on page 25, a WM_PAINT architecture program generally handles user events in a two-phase process. For mouse, keyboard, and other events, the data model for that window

© Copyright Russell Tront, 2000 . Page 15-34

instance is updated. Then that callback function additionally calls Invalidate( ) and returns. That causes Windows to later send a WM_PAINT message and thus call your OnPaint( ) function, it order your version of that function to put a refreshed set of pixels on screen showing in the new application data model information. In principle, for better learning I should show you such a program. However, I am going to see if you can do this in an assignment.

Page 18: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-35

Other notes: � The message map declaration: DECLARE_MESSAGE_MAP()

in the .h file should generally be put at the END of the class declaration. This is because when the macro is expanded, it may put in some access modifiers (private, protected, etc). After having done this, the macro is incapable of putting the access scope back to what it was before the macro was encountered. If you do not put the message macro definition at the bottom of the class declaration, then at least put an access modifier of your choice right after it that will appropriately set the access of the following members.

I notice that some textbook authors make the message callback functions protected. This is because they do not need to be public to other functions in your program, because only Windows (or perhaps a subclass of your window class) should call them. See the next section if you have some reason you think you should call the callback function of some window (even your own window instance function).

� I also notice that some authors even make the message map private as not even a subclass of your window class would use the parent message map (each window class must have a map of its own; they are not inherited!).

© Copyright Russell Tront, 2000 . Page 15-36

15.7 SendMessage vs. PostMessage

Sometimes you may want to call one of your own callback functions (which normally only Windows calls), because that function does something useful that you need, so why write that code again.

Rather than calling the function directly, it is better to ask Windows to do this for you, in one of either two ways: someWindow->SendMessage(UNIT message, WPARAM, LPARAM)

or someWindow->PostMessage(UNIT message, WPARAM, LPARAM)

Essentially, you ask Windows to call one of your callback functions by sending the appropriate message to your program. Through the message map, this ends up being translated into a call to your desired callback function.

SendMessage works rather directly, without going through the message loop, almost like (but not quite) a direct call. PostMessage is a bit more polite and queues the message that you are trying to generate into the message queue. Note the particular length and form of the parameter lists of these functions depend on the particular WM_<messageType> you want generated.

Page 19: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-37

15.8 Message Boxes

For outputting simple messages or questions and waiting for simple yes/no/cancel kinds of responses, message box windows are simple and ideal. Message boxes are also often used for delays to make sure user has read or done something before the application proceeds.

A message box is just a dialog box with no input fields (no input ‘control’ objects) other than the one or few buttons in it (e.g. yes/no/cancel). Nonetheless, it can display a couple of strings and you can be used for a ternary (base 3) input value. Here is an example.

Here is the code that generated the above message box: int result; result = MessageBox( "Do you want a break today?", "Caption by Russ", MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2);

© Copyright Russell Tront, 2000 . Page 15-38

Message boxes have 4 attributes: � A message string within the window (which could potentially

contains some data). � A window title bar caption (often not used). � An icon chosen from:

o stop sign - usually indicating the user is doing something wrong.

o question mark - indicating the message box is asking a question.

o exclamation mark - conveying some surprising revelation.

o a lower case “i” indicating this box is presenting information.

� A number of buttons within the window, one of which the user is invited to click as her selected response to that message.

I believe you can leave any of these attributes out if you do not want them. If you do not specify a button set, the default is a single OK button. Here is the function prototype for this window instance function inherited by the base CWnd class. int MessageBox( LPCTSTR lpszText, LPCTSTR lpszCaption = NULL, UINT nType = MB_OK );

You cannot use Create( ) to make a message box. The only way is to call the function MessageBox(). Generally, the function call is blocking: the application user cannot proceed until she clicks on one of the buttons (there are ways around this, but they are rarely used).

Page 20: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-39

A message box is a child control window contained within the window boundaries and shown in front of the parent window instance upon which that the member function was invoked.

The two strings are of type LPCSTR; this is Microsoft’s symbol for a long pointer to a constant string (assumably null terminated).

Finally, the last parameter is an unsigned integer that contains a set of bit flags. Typically the programmer bitwise-ORs together three symbolic constants, one chosen from each of the following three lists:

Symbolic constants representing the icon the user will see: MB_ICONEXCLAMATION MB_ICONQUESTION MB_ICONINFORMATION MB_ICONSTOP

Symbolic constants representing the buttons the user is to see: MB_OK MB_OKCANCEL MB_YESNO MB_YESNOCANCEL MB_RETRYCANCEL MB_ABORTRETRYIGNORE

Symbolic constants representing which button should be the default, should the user just type a carriage return: MB_DEFBUTTON1 MB_DEFBUTTON2 MB_DEFBUTTON3

Assumably, MB_DEFBUTTON1 is the left button of the 2 or 3 listed in order within the symbols in section 2) above. If you don’t

© Copyright Russell Tront, 2000 . Page 15-40

specify a button, the first/leftmost is the default (i.e. most programmers only bother specifying 2 or 3, or nothing at all).

e.g. For a message box containing a question mark, and yes/no/ cancel buttons where ‘no’ is the default, use the following as the last parameter to in the MessageBox() parameter list: MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON2

An unusual return value from the function indicates an error in the call. The Windows API help suggests that if there is not enough RAM to create the window, zero will be returned. I also suspect that if you asked for two mutually exclusive attributes (e.g. that it was to be both a yes/no and abort/retry/ignore message box), you would get a return value <= 0 as well.

If the call was successful, you are returned one of these symbolic constants indicating which button the user selected:

IDOK IDYES IDNO IDCANCEL IDABORT IDRETRY IDIGNORE

You can use an ‘if’ or ‘switch’ to act on the return value. Or you cannot even record it if you don’t assign the function return value to anything in the calling code.

Note: Message boxes are very handy when you are trying to debug a windows program without using the debugger; you

Page 21: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-41

can sprinkle message box calls throughout the suspect parts of your code and output variables in them (once you convert them to an ASCII string), or notice if they pop up indicating the code executed down that path of your program.

To convert an integer to ASCII internally (rather than using some kind of statement that writes to an output console or printer), either: � write to an ostrstream object as described in Section 2e of the

lecture notes. � use the itoa( ) function which converts an integer to ASCII.

Microsoft does not seem to have this common Unix function, but if you look for help on ‘data conversion’, does have a function:

_itoa(int value, char *string, int radix);

This will convert the int value to a string whose maximum length is 33 bytes (for radix 2, 32 bits and a null terminator). There are other similar functions available in any C or C++ compiler library.

© Copyright Russell Tront, 2000 . Page 15-42

15.9 Menus

This section of the course covers menus, one of the most common user interface paradigms around. Menus are used so that users to NOT have to memorize cryptic program commands like ‘passwd’ to change your password in Unix.

This section will rely heavily on the code in Section 2.8 of the [Deitel2000], which I will not reproduce here since you have the textbook.

First some terms:

The menu bar is also sometimes called a top-level menu.

The sub-menus that drop down on clicking a word in a top-level menu are called ‘popup’ menus. The term ‘popup’ menu is often reserved for when you right click in the middle of a window. However, many such so-called popup menus actually drop-down from the point of the mouse click just like the sub-menus of a top-level menu.

Though it is possible to have a top-level menu item that is actually a leaf command and does not actually drop down a sub-menu, apparently this is considered uncommon style.

Before looking at how to create menus, I would like to introduce the strategy that Windows uses to tell your program that a user has clicked on a menu. Basically, Windows sends you an ON_COMMAND message. And

Page 22: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-43

you can put a bunch of ON_COMMAND message entries in your message map, one for each menu leaf item. Each one is differentiated by an integer ID that is usually defined as a more readable named constant of your own choice (but which is usually exactly the same name as what is on that leaf menu button, though all capitals and preceded by an “IDM_” prefix; IDM stands for ‘ID for Menu’).

Since you can define a message map entry for each menu item, you can thus associate each menu item with a message handling callback function sensibly named by you.

Deitel’s example in Figure 2.11 has a small application that does up the bill for a restaurant.

The menu and drop down sub-menus are shown below: File Entree Beverage Order Exit Chicken Ginger Ale Show Total Fish Root Beer Clear Total

© Copyright Russell Tront, 2000 . Page 15-44

This simple application has fixed prices for Chicken, Fish, Ginger Ale, and Root Beer, and selecting any particular one will add to the bill. Order > Show Total will display the total in a message box, and Clear Total will do the obvious. File > Exit will halt the program.

Creating a menu-driven program for Windows involves 4 parts: 1) Defining numeric ID constants for each menu leaf item in a

.h file that is included into both your program and to the menu ‘resource’ file.

2) Defining a menu ‘resource’ file that contains the structure and menu item names. It can also contain one or both of Alt-shortcut keys and accelerator keys for each menu items, plus other stuff like a separator line.

3) Writing your message map so as to map WM_COMMAND messages with particular message IDs to particular callback function names.

4) Writing the callback functions mapped to by the message map.

Ok, now some code:

Page 23: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-45

//FileName: Menus_ids.h //Copyright 1999 by Deitel and PrenHall. #define IDM_EXIT 2000 #define IDM_CHICKEN 2021 #define IDM_FISH 2022 #define IDM_GINGERALE 2041 #define IDM_ROOTBEER 2042 #define IDM_SHOW_TOTAL 2051 #define IDM_CLEAR_TOTAL 2052

Note that [Prosise99] from Microsoft Press suggests that you not use numbers 57344 (0xE000) and above as menu constants because they are reserved by Windows (for the system menu) and MFC.

He suggests that MFC Technical Note #20 recommends you use 32768 - 57343. He then says that to avoid a bad Windows 95 bug you should instead use a lower range, 1 - 32767, and that it is perfectly safe to do so despite MFC Tech Note #20. And other books I have seen say it is ok to use zero as an ID!

© Copyright Russell Tront, 2000 . Page 15-46

Second, you have to define the menu name, menu structure, and menu item names in a .rc resource file. A resource file is written in resource language and is compiled by a resource compiler into a .res file.

Resource files often contain things like icons, menus, and other helper information for your application. When compiled by their own compiler, the result is then crammed into your .exe file during link or post-link time. However, it is possible to yank out a resource from a .exe file, and replace it with a menu written in say, a different spoken language (e.g. French). This can be done WITHOUT recompiling your C++, or re-linking.

When you try to open a .rc resource file in Visual C++, you get a very primitive visual resource editing tool. In order for you to understand some of the code that Visual C++’s application, class, and resource wizards/editors will generate for you (because you will likely have to edit the resultant C++ code), we will not use these editors and wizards. This is not to say they are not useful or widely used, however generated code can be hard to read until you understand it at a fundamental level. Note also that Microsoft’s visual development tools are not necessarily the best in the business. See for alternative examples Borland’s C++ Builder and IBM’s Visual Age for C++.

Page 24: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-47

//Filename: menus.rc //Copyright 1999 by Deitel and PrenHall. #include <afxres.h> #include "menus_ids.h" Food MENU{ POPUP "File" { MENUITEM "Exit", IDM_EXIT } POPUP "Entree"{ MENUITEM "Chicken", IDM_CHICKEN MENUITEM "Fish", IDM_FISH } POPUP "Beverage"{ MENUITEM "Ginger Ale", IDM_GINGERALE MENUITEM "Root Beer", IDM_ROOTBEER } POPUP "Order" { MENUITEM "Show Total", IDM_SHOW_TOTAL MENUITEM "Clear Total", IDM_CLEAR_TOTAL } }

© Copyright Russell Tront, 2000 . Page 15-48

This file is sometimes seen written with BEGIN and END keywords instead of opening and closing braces.

Notice the first line names the menu “Food” and indicates that this is a menu, not a sub-menu. The word ‘Food’ does not ever show up within your visual menu; it is just a way for a program that might have several menus (say for expert and beginner users) to refer to a particular one.

The elements of the main menu bar are mentioned next. In this case, instead of leaf elements, they are in turn further sub-menus. The keyword POPUP indicates the name of the sub-menus that will appear on the top-level menu. The content of each submenu is enumerated with a list of MENUITEMs. (Note that a sub-menu can contain a sub-sub-menu simply by nesting appropriately).

Note that each menu item is associated with one of the named constants you created in your menus_ids.h file (or whatever you want to call it. Note that you should #include <afxres.h> and then your .h file containing the named constants into your .rc file.

Page 25: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-49

Each leaf item has a name in quotes. There are some special things that you can put in a menu item name that are not shown in Deitel’s example:

� It is convention that if a menu item invokes a dialog box that

requires further input before the command can start, the name usually ends in the string “...”. You implement this by putting the “...” as part of the double quoted menu item name.

� In order to navigate menus without a mouse, as some very fast typists like to do, you can access menus with an “Alt-<shortcutKey>”. This is not a hot key or accelerator key like which will be discussed below, but is usually just an underlined letter in each menu item name. It is usually the first letter, but not if there are two items in the same list that start with the first letter. For example, on the “File” menu of most applications, you can see the “Save As...” menu item. So pressing Alt-f followed by ‘a’ will trigger a Save As command to start. To signal which letter should be the underlined short cut key, put a ‘&’ just in front of it. e.g. “Save &As...”

� Thirdly, there are accelerator keys. These are hot key combos that will trigger certain operations directly. An example is <Cntl-x> for cut, <Cntl-c> for copy, and <Cntl-v> for insert. You can see these on the Edit sub-menu of almost any kind of editor. These are indicated by a tab in the name string of a menu item. e.g. “&Copy\tCntl-E” Note that you also have to add what is called an Accelerator table, which we will unlikely cover in this course.

Now let’s look at this application’s main .h file:

© Copyright Russell Tront, 2000 . Page 15-50

//Filename: CMenusWin.h //Copyright 1999 by Deitel and PrenHall. const int TEXT_SIZE = 16; class CMenusWin : public CFrameWnd { public: CMenusWin(); void tally(int &nCount, double dAmount); afx_msg void OnExit(); afx_msg void OnDoFood(UINT nFood); afx_msg void OnShowTotal(); afx_msg void OnClearTotal(); private: int m_nChicken, m_nFish; int m_nGingerale, m_nRootbeer; double m_dTotal; char m_szText[ TEXT_SIZE ]; ostrstream m_str; DECLARE_MESSAGE_MAP() };

Page 26: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-51

And finally, the main file: //Filename: menus.cpp //Copyright 1999 by Deitel and PrenHall. #include <afxwin.h> // MFC framework #include <strstrea.h> // string stream #include <iomanip.h> // I/O manipulators #include "menus_ids.h" // message ID symbols #include "CMenusWin.h" //------------------------------ CMenusWin::CMenusWin() // construct window :m_str( m_szText, TEXT_SIZE ){ // init list ostrstream Create( NULL, "Menus Example", WS_OVERLAPPEDWINDOW, CRect( 0, 0, 200, 200 ), NULL, "Food" ); <--MENU SPECIFICATION!!!!!!! m_nChicken = m_nFish = 0; m_nGingerale = m_nRootbeer = 0; m_dTotal = 0.0; } //------------------------------- // accumulate total. void CMenusWin::tally(int &nCount, double dAmount) { nCount++; m_dTotal += dAmount; }

© Copyright Russell Tront, 2000 . Page 15-52

//------------------------------ afx_msg void CMenusWin::OnExit() { SendMessage( WM_CLOSE ); } //---------------------------- afx_msg void CMenusWin::OnDoFood(UINT nFood) { switch (nFood) { case IDM_CHICKEN: tally( m_nChicken, 2.25 ); break; case IDM_FISH: tally( m_nFish, 1.80 ); break; case IDM_GINGERALE: tally( m_nGingerale, .80 ); break; case IDM_ROOTBEER: tally( m_nRootbeer, .80 ); break; } }

Page 27: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-53

//------------------------------ afx_msg void CMenusWin::OnShowTotal() { //we use a string stream here to write into, //which allows us to convert from float //to ASCII. Note use of ends instead of endl. m_str.seekp( 0 ); // reset output string m_str << setprecision( 2 ) << setiosflags(ios::fixed | ios::showpoint) << " $" << m_dTotal << ends; // display new dialog box with output string MessageBox( m_szText, "Your total is:" ); m_dTotal = 0.0; } //-------------------------------- afx_msg void CMenusWin::OnClearTotal() { m_dTotal = 0.0; MessageBox(" $0.00", "Cleared Order"); } //-----------------------------------BEGIN_MESSAGE_MAP( CMenusWin, CFrameWnd ) ON_COMMAND( IDM_EXIT, OnExit ) ON_COMMAND_RANGE(IDM_CHICKEN, IDM_ROOTBEER, OnDoFood) ON_COMMAND( IDM_SHOW_TOTAL, OnShowTotal ) ON_COMMAND( IDM_CLEAR_TOTAL, OnClearTotal ) END_MESSAGE_MAP()

© Copyright Russell Tront, 2000 . Page 15-54

//----------------------------------- class CMenusApp : public CWinApp { public: BOOL InitInstance() //called by WinApp::CWinApp { m_pMainWnd = new CMenusWin; m_pMainWnd->ShowWindow( m_nCmdShow ); m_pMainWnd->UpdateWindow(); return TRUE; } } menusApp;

The first thing to notice is how the program is told about the menu resource file and that it should even have a menu bar. The Create call in the main frame constructor is given a longer parameter list, so you can pass the name of your menu “Food” to the create function. There are other ways to get menus into a program (including programmatically), but the standard way is to specify the name of a menu resource in the call to Create( ) from within the constructor of the main frame class.

Page 28: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-55

The second thing to notice is the message map: ON_COMMAND( IDM_EXIT, OnExit ) ON_COMMAND_RANGE(IDM_CHICKEN, IDM_ROOTBEER, OnDoFood) ON_COMMAND( IDM_SHOW_TOTAL, OnShowTotal ) ON_COMMAND( IDM_CLEAR_TOTAL, OnClearTotal )

It contains a few notable things.

The first map element is for the exit menu element in the File sub-menu. This tells the compiler to map the exit menu item to a function call to OnExit( ).

The last two lines show how to map other menu element constants to callback function names. Note how these are different from the map elements we have seen previously for mouse events and WM_PAINT events. They looked, for comparison, like this: ON_WM_RBUTTONDOWN() ON_WM_PAINT()

On the other hand, menu element map entries have pseudo parameters. The named constant for the menu element is first, followed by the name of the callback function (which unlike the previous mouse and paint callback function names can now be anything you want).

© Copyright Russell Tront, 2000 . Page 15-56

Finally, note that there is a special map entry available if you want to trap a whole range of numerical menu constants and feed them to just one callback function.

Note that menu command handling callback functions take no parameters and return no values.

Page 29: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-57

15.9.1 Closing A Program

Note above in the implementation for the OnExit( ) callback function that we halt our program by sending a WM_CLOSE message to our main frame instance.

An alternative for closing to:

SendMessage(WM_CLOSE)

is:

PostMessage(WM_CLOSE, 0, 0);

that more politely allows other pending messages in the message loop to be completed before killing the main window and program. On the other hand, there may be good reason to kill a multi-threaded program or even a normal program without waiting for other messages/calculations to be handled, in which case perhaps SendMessage is more appropriate.

© Copyright Russell Tront, 2000 . Page 15-58

15.10 Dialog Boxes

Dialog boxes are the most common way that a GUI can prompt and obtain input. This input can be use for data, or to control the program. Here is an example.

Page 30: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-59

15.11 Controls

The elements of a dialog box are often called input ‘controls’ in Microsoft Windows terminology, because through their ability to input data and settings, the user can control the program (in a different way than menus).

Note that not all controls are for input. Often text labels must also be put into dialog boxes, and these are usually called static controls even though they control nothing (they are static in that the user cannot change them in any way).

There are a number of different kinds of controls: � LTEXT - left aligned static text label. � CTEXT - left aligned static text label. � RTEXT - left aligned static text label. � PUSHBUTTON � EDITTEXT - an input string field, possibly pre-initialized. � DEFPUSHBUTTON - the one that is initially highlighted. � CHECKBOX - doesn’t automatically respond. � AUTOCHECKBOX - automatically responds. � STATE3 - a checkbox with a third state: a grayed checkmark. � AUTOSTATE3 - an auto-responding 3 state checkbox. � RADIOBUTTON � AUTOTRADIOBUTTON - use instead of RADIOBUTTON. � GROUPBOX � LISTBOX � COMBOBOX � SCROLLBAR � ICON

© Copyright Russell Tront, 2000 . Page 15-60

Note that a text area is just an EDITTEXT control with added style ES_MULTILINE. One of the best online help ways to find out about all the possible styles for each different kind of control is to look up “control control” in the Help Index of Visual C++.

Or for general information, try this Contents path:

Using Visual C++ > Visual C++ Programmers Guide > Adding User Interface Features > Overviews.

Of course, you should also read your textbook, [Deitel2000], Section 2.9 and all of Section 4.

(Note: You want to use the AUTO variants of the above controls (if they have them) because the non-auto ones do NOT change state (e.g. unchecked to checked checkbox) automatically. The early non-auto ones required you to write an OnCheckBoxClicked( ) handler that then in turn had to call the SetChecked(BST_CHECKED) function to change the visible state of the checkbox. )

We are not going to cover a lot of detail on input controls. Every textbook, and the on-line help of any development environment has plenty of information on user controls.

Page 31: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-61

Note: Use

int GetCheckedRadioButton(firstID, last ID)

to find out if one radio button in a range of them is set.

© Copyright Russell Tront, 2000 . Page 15-62

15.12 Handling Controls within a Dialog Box

Though you can put controls in your main frame, they are more commonly found in dialog boxes.

Dialog boxes often have checkboxes and buttons (like Ok and Cancel buttons).

A checkbox is really just a weird variant of a button, so BST_CHECKED mentioned above is a Button STate. Note that a regular button has state, either currently pressed or not. This same state and behaviour applies to checkboxes, except check boxes stay checked even after you release the mouse from clicking an unchecked check box.

Because check boxes are really a button variant, clicks on a check box produce a button BN_CLICKED message that you can route in the message map (of the dialog subclass) as: ON_BN_CLICKED(IDC_MYCHECKBOX, OnCheckBoxClicked)

The OnCheckBoxClicked( ) function name is of your own choosing.

Page 32: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-63

15.13 Controls are Child Windows

A control is a child window contained within another CDialog or CFrameWnd window. A child window appears in front of and within its parent, moves with its containing parent as if it was part of the parent, and is minimized and restored with the containing parent. That’s why it appears to be just part of the containing parent. A child window is created using the Create( ) function call within the C++ MFC child instance. You have to provide an extra parameter indicating which parent will contain it. You can also optionally add a parameter that gives the child a particular integer ID different from other children within the containing parent window.

Note that the ID of a control within a dialog box (e.g. IDC_MYCHECKBOX) is just the child ID of that control within the containing dialog window. They are just named constants set by either you or the dialog box visual design tool.

© Copyright Russell Tront, 2000 . Page 15-64

15.14 Defining a Dialog Resource

So that dialogs can be changed to say another spoken language without recompiling the programs source code, dialog boxes are defined in a resource file. Each dialog is given a unique name or IDD_ integer ID. Within the program, you specify the particular dialog resource used for creating your dialog when you chain back to the dialog base class constructor. e.g. MyDialog::MyDialog(int initialWingSpan, CMainFrame * pParentWnd) :CDialog(IDD_DIALOG1, pParentWnd){ //Need to chain to one of two possible //parent contructors above. One uses //dialog resource ID and parent CWnd. //Other requires dialog resource name //string and parent window //(or NULL if you want dialog to be parented //by desktop which is common for modeless dlgs). //Other initialiation here. e.g. m_WingSpan = initialWingSpan; }

As described in the comments above, you have the option of chaining to either one of two base CDialog constructor, depending on whether you want to specify the integer IDD_ or string name of the dialog within the program’s resources.

Page 33: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-65

Dialog resources are defined similarly to a menu. However, there is one problem. Unlike a menu tree which has an automatic layout preventing one menu from overwriting another, dialogs are complex and you have to specify the position and size of all controls. Doing this with X-Y coordinates is very difficult. To make it easier, the coordinates of controls within a dialog are specified not in pixels, but in 1/4s of a standard character height, and 1/8ths of a standard character height (this is why a dialog resource file must specify a font!).

Here is a dialog that we will be looking at:

© Copyright Russell Tront, 2000 . Page 15-66

Page 34: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-67

Like a menu resource, the dialog resource relies on a .h file defining a lot of constants: The dialog ID (if not using a string name), and the child IDs that you want assigned to each control.

Here is the resource.h file used for the above dialog. I used the Insert>Resource>Dialog tool to create and layout the above dialog. Some of the names given to the named constants are generated by the tool, and some I overrode with a right click on the control, then selecting properties. //Filename: resource.h //{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated. // Used by MyDialog.rc // #define IDD_DIALOG1 101 #define IDC_EDIT1 1000 #define IDC_AIRCRAFT_TYPE 1000 #define IDC_EDIT2 1001 #define IDC_CHECK1 1002 #define IDC_RESET 1003

Some of the names given to the named constants are generated by the tool, and some I overrode with a right click on the control, then selected properties.

Here is the dialog resource: © Copyright Russell Tront, 2000 . Page 15-68

IDD_DIALOG1 DIALOG DISCARDABLE 0, 0, 174, 85 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "MyDialogCaption" FONT 8, "MS Sans Serif" BEGIN EDITTEXT IDC_AIRCRAFT_TYPE, 58,15,40,14, ES_AUTOHSCROLL CONTROL "Jet?",IDC_CHECK1,"Button", BS_AUTOCHECKBOX | WS_TABSTOP, 104,15,63,14 EDITTEXT IDC_EDIT2,57,35,42,12,ES_AUTOHSCROLL PUSHBUTTON "Reset",IDC_RESET,109,34,50,14 DEFPUSHBUTTON "OK",IDOK,13,55,50,14 PUSHBUTTON "Cancel",IDCANCEL,71,55,50,14 LTEXT "Aircraft Type:",IDC_STATIC,13,18,41,8 LTEXT "Wing Span: ",IDC_STATIC,14,36,37,8 END

Note the IDD_ named constant for this particular dialog, and its position and size (position 0,0 means center it in the main frame).

The caption is for the title bar of the dialog box.

The font is needed to specify the font and unit of measure for positioning controls within the dialog.

Each control is specified by a type, and often a string that will be part of the control (e.g. label for a checkbox.).

Page 35: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-69

This is followed by the control constant (named constant) (child id?), and the location and size of the control within the dialog in 1/4s and 1/8ths font units.

Sometimes the tool will use the direct method of defining a control (e.g. AUTOCHECKBOX), whereas other times it will use the CONTROL keyword and the added BS_AUTOCHECKBOX style. Don’t let this bother you.

With the tool, you can change the size of the dialog box by dragging a corner. In addition, you drag and drop controls onto the dialog from a toolbar. If the control toolbar is not visible, use Tools>Customize>Toolbars>Controls. Then you can resize and re-position the buttons, edit boxes, et cetera, within the dialog box.

There are some important things to note about a dialog resource. First, the different between a default push button and a push button.

Second, users like to tab from one control to the next in order to enter data into the next. The tab order is dependent on the order listed above. You can change the order with an editor, or better, using the dialog tool. From the tool, just select Layout>Tab Order, then click on each control in the order you want the users tabs to move.

Note: The resource.rc file has a bunch more tool-generated lines in it as well.

© Copyright Russell Tront, 2000 . Page 15-70

15.15 Dialog Box Completion Handling

Handling regular buttons within a dialog box happens in one of two ways: 1) If the button clicked is OK or Cancel, these are

automatically mapped to CDialog::OnOK and OnCancel callback functions that you can override if desired. Both the inherited implementations call EndDialog( ) that makes the dialog box invisible, which is usually desired. Note: Clicking on the dialog close button in the top right corner, or the user pressing the Escape key, also trigger the same actions as a Cancel button. So, you don’t absolutely need a visual Cancel button because the user has other ways to abort the dialog.

2) If the button is something else, perhaps ‘Add’, then you need to provide an ON_BN_CLICKED map entry and callback function in your own subclass of CDialog. This function may pop up yet another kind of dialog box that allows a user to add something, then that other dialog perhaps dies leaving the user to still work with the original. Or an Add button may trigger an operation that will do something and then call EndDialog( ) on the first dialog.

Note: The functions OnOK( ) and OnCancel( ) are provided by the CDialog parent class in MFC. Interestingly, they have default message map entries so you do not even have to bother to put them in your dialog’s message map. However, you then have to name your message handlers exactly as

Page 36: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-71

MFC expects them if you want to override them. Also, you should not use control/child IDs of 1 or 2.

© Copyright Russell Tront, 2000 . Page 15-72

15.16 The Life of a Dialog

In MFC, you usually program a dialog box using your own subclass of the MFC-provided CDialog class. You usually have to subclass the CDialog class in order to make it unique to your program needs.

There is a complex set of interactions between your main, your MFC dialog C++ instance, and MS-Windows that holds the actual dialog window.

This is so complex, that I am not sure there is even a way to draw it in UML!

Page 37: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-73

Create

BLOCK HERE ------ FOR USER

CDialog:: OnInitDialog( )

OnInitDialog( )

CDialog:: DoModal( )

C++ Dialog

Instance

Construct Main

Actual

Windows

dialog

window

Create( )

setDlgItemText( )

SetDlgItemInt( )

OnOK( )

GetDlgItemText( )

GetDlgItemInt( )

CDialog:: OnOK( )

EndDialog( )

Kill

© Copyright Russell Tront, 2000 . Page 15-74

Note that DoModal( ) is inherited unchanged from the parent class CDialog. Some other functions used above are overridden but MUST chain back to the parent class.

Here is what happens: 1) Main code constructs a dialog box subclass C++ instance.

This does not create the actual MS-Windows window yet.

2) Often the constructor does not do much initialization, but it can set whatever additional dialog instance variables that you have defined in the subclass (these might not be necessary if you initialize the dialog box from the OnInitDialog( ) function within your dialog subclass). Note that the dialog ‘window’ itself and it’s child input controls do not exist yet! So, you canNOT in the MFC C++ instance constructor yet initialize values within the dialog box child controls.

3) Main code MUST invoke the dialog instance’s DoModal( ) function.

4) Dialog subclass should have an overriding OnInitDialog( ) function within the dialog class that FIRST chains to the parent OnInitDialog( ) which actually creates the dialog window. Your overriding OnInitDialog( ) then can initializes the dialog control items as necessary (e.g. initial text for an exit box, and which checkboxes and radio buttons are set). This is the ONLY place you can initialize the controls from. This initialization can use data from the dialog instance variables or from ANY other accessible variables in your program. You simply use the SetDlgItemText( ) or SetDlgItemInt( ) functions, though there is no set function for setting a textbox to a float! Also, to set check boxes and

Page 38: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-75

radio buttons, you first have to get a pointer to the control (as will be shown in code below).

5) The user enters text into text boxes, and clicks checkboxes and radio buttons, et cetera, then clicks the OK button (or CANCEL or close buttons, or presses escape key).

6) If the user clicks the Ok button, then before the dialog window (not C++ instance) actually disappears with its input control’s state/data, Windows calls your dialog C++ instance’s overriding OnOK( ) function. This is the ONLY place and time you can grab the dialog window’s data before it is killed. Your OnOK( ) should use GetDlgItemText( ) or other functions as necessary to grab the data/state selected/entered into the various input controls within the dialog box. At the very end of your OnOK( ), you then chain to the parent OnOK( ) function that in turn closes the dialog ‘window’ (not C++ instance) by calling EndDialog( ).

7) Finally, the DoModal( ) function originally called from the main returns. Subsequent code in the main can check the DoModal( ) return value == IDOK. If so, your main knows that the dialog’s OnOK( ) function was called before the dialog died, and that the OnOK( ) function likely provided new/updated data either in the main’s variables, or in the still existing C++ dialog instance’s attributes (i.e. where ever your OnOK( ) function copied them to).

© Copyright Russell Tront, 2000 . Page 15-76

15.17 A Complete Dialog Example

So, now let’s some source code that actually demonstrates this. Our application stores the aircraft type name and whether or not it is a jet, and displays that in the main frame. A right click pops up the above dialog image, where the user can change the aircraft type name, the wing span and whether or not that aircraft type is jet powered.

On clicking the OK, button the new wing span is displayed in a MessageBox, and when that is dismissed, the main frame is repainted with the revised aircraft type name and jet powered checkbox state (1 = checked).

The aircraft type name and jet checkbox state is retained until another dialog box is constructed and used to initialize that dialog box. This data can be retained in any number of places in your program: I chose just for variety to store the aircraft type name in a main frame static class variable, and the checkbox state in a main frame instance variable.

The wingspan is not retained, and each time the dialog box is displayed, it starts with a wing span of 200.

Note that clicking on the reset button within the dialog box will return the 3 items to their default values.

Page 39: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-77

//FileName: MainFrame.h //Copyright 2003 - Russ Tront //Mainframe class for demo dialop boxes. #ifndef _mainframe_ #define _mainframe_ //--------------------------------------- class CMainFrame : public CFrameWnd { public: CMainFrame(); //Two different places we could remember //values from dlg instantiation to next, //or could even use global variables. int checked; //for dialog checkbox. static char aircraftType[80]; protected: afx_msg void OnRButtonDown(UINT uFlags, CPoint point ); afx_msg void OnPaint(); DECLARE_MESSAGE_MAP() }; #endif

© Copyright Russell Tront, 2000 . Page 15-78

//Filename: MainFrame.cpp //Copyright 2003 - Russ Tront //Contains main frame definition and app //declaration for demo dialog box. #include <afxwin.h> #include "MainFrame.h" #include "MyDialog.h" //---------------------------------------------- //static class member: char CMainFrame::aircraftType[80] = "Boeing-747"; //----------------------------------------------- //Main frame constructor. CMainFrame::CMainFrame(){ Create( NULL, _T ("Right Button Dialog Test")); checked = 1; //checkbox initially checked. }

Page 40: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-79

//---------------------------------------------- //Right mouse button handler afx_msg void CMainFrame:: OnRButtonDown( UINT uFlags, CPoint point ){ //Trigger the dialog with wingspan set to 200, //and tell it its containing frame. Dialog //gets initial state from this class's //static aircraftType string, from the 200, //and main frame instance variable 'checked'. MyDialog dlg(200, this); //dlg.m_WingSpan = 200; <-also possible if //dlg exists and dlg.m_WingSpan public. if(dlg.DoModal() == IDOK){ //DoModal( ) returns when user dismisses //dlg. If return value IDOK, then user //has made dialog changes and clicked OK. //Here is where we grab dlg instance //attributes stored by dlg OnOK() function //that will disappear when dlg instance //destructed at end of this function. char myString[33]; MessageBox( itoa(dlg.m_WingSpan,myString,10), "Wing Span modified by Dialog:"); Invalidate( ); } }

© Copyright Russell Tront, 2000 . Page 15-80

//---------------------------------------------- afx_msg void CMainFrame:: OnPaint(){ //Paint the state of two of the user //dialog selected aircraft attributes: //aircraftType and jet checkbox. CPaintDC dc(this); dc.TextOut( 10, 10, aircraftType, strlen(aircraftType)); char checkedString[30]; dc.TextOut( 10, 100,"Checkbox state:", 15); dc.TextOut( 10, 125, itoa(checked, checkedString, 10), 1); } //-------------------------------------------- BEGIN_MESSAGE_MAP( CMainFrame, CFrameWnd ) ON_WM_RBUTTONDOWN() ON_WM_PAINT() END_MESSAGE_MAP() //============================================= class CMyApp : public CWinApp { public: BOOL InitInstance() { m_pMainWnd = new CMainFrame; m_pMainWnd->ShowWindow( m_nCmdShow ); m_pMainWnd->UpdateWindow(); return TRUE; } }; CMyApp myApp; //Filename: MyDialog.h

Page 41: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-81

//Copyright 2003 R. Tront //Declaration of dialog class. #ifndef _mydialog_ #define _mydialog_ #include "MainFrame.h" //---------------------------------- class MyDialog: public CDialog{ public: MyDialog(int initialWingSpan, CMainFrame * pParentWnd); //parent constructor needs main frame //to center in front of (NULL ok too). int m_WingSpan; //a place to store //wingspan before dialog window killed. CMainFrame * m_pParentWnd; //place to //store parent, so can write into //parent instance variables before //dialog window lost. protected: virtual BOOL OnInitDialog(); virtual void OnOK(); virtual void OnReset( ); DECLARE_MESSAGE_MAP(); }; #endif

© Copyright Russell Tront, 2000 . Page 15-82

//Filename: MyDialog.cpp //Copyright 2003 R. Tront //Definition of dialog class. #include <afxwin.h> #include "MyDialog.h" #include "MainFrame.h" //for static aircraftType. #include "resource.h" //----------------------------------------- MyDialog::MyDialog(int initialWingSpan, CMainFrame * pParentWnd) :CDialog(IDD_DIALOG1, pParentWnd) { //Need to chain to one of two possible //parent contructors above. One needs //dialog resource ID and parent CWnd. //Other requires dialog resource name //string and parent window (or NULL). //Set dlg member attributes for later use //in dialog OnInitDialog( ). m_WingSpan = initialWingSpan; m_pParentWnd = pParentWnd; //needs to be //stored if OnInitDialog() and OnOK( ) //read/write mainframe instance data. }

Page 42: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-83

//----------------------------------------------- BOOL MyDialog::OnInitDialog( ){ //This is only place to pre-set dialog window. //For simpler example, //see OnReset( ) further below. CDialog::OnInitDialog(); //must chain first. //Pre-set wingspan editbox from //dlg member attribute preset in constructor: SetDlgItemInt(IDC_EDIT2, m_WingSpan); //Pre-set aircraftType editbox from some //global var, one of this module's module //variables, or public static class variable //of some class: SetDlgItemText(IDC_AIRCRAFT_TYPE, CMainFrame::aircraftType); //Before can pre-set check box, must //get pointer to box. CButton * pCheckBox = (CButton *)GetDlgItem(IDC_CHECK1); //Using pointer, pre-set checkbox to some //literal value, or from anything else //(perhaps instance attr of the mainFrame. //pCheckBox->SetCheck(1); //1 = checked. pCheckBox->SetCheck(m_pParentWnd->checked); return TRUE; }

© Copyright Russell Tront, 2000 . Page 15-84

//---------------------------------------------- void MyDialog::OnOK( ){ //This is ONLY place to grab dialog window //input control values before window killed. //One way is to store stuff in dialog //instance attributes because dialog //instance will not die yet. m_WingSpan = GetDlgItemInt(IDC_EDIT2); //Another way to handle dialog stuff is to //get it to a global, module, or other //class's static variable. GetDlgItemText(IDC_AIRCRAFT_TYPE, CMainFrame::aircraftType, 80); //Getting stuff from check box is more //difficult. First get pointer to Checkbox: CButton * pCheckBox = (CButton *)GetDlgItem(IDC_CHECK1); //Then use Checkbox's 'get' member function. int checked = pCheckBox->GetCheck(); //Here is how to pass result back to instance //attribute of main frame, assuming we have a //pointer to the main frame: m_pParentWnd->checked = checked; //Lastly, we could actually take some action //here, rather than after DoModal( ) returns //in the main frame. For example, could start //some user desired action like printing. //More often action is taken in the main frame //after DoModal() returns rather than here. CDialog::OnOK(); //do last to kill dialog wnd. }

Page 43: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-85

//-------------------------------------------- void MyDialog::OnReset( ){ //Here is a very compact example of how to //set dialog attributes if it exists. SetDlgItemInt(IDC_EDIT2, 200); SetDlgItemText(IDC_AIRCRAFT_TYPE,"Boeing-747"); CButton * pCheckBox = (CButton *)GetDlgItem(IDC_CHECK1); pCheckBox->SetCheck(1); } //-------------------------------------------- //OnOK() and OnCancel() not needed in message //map because they are pre-supplied by MFC. BEGIN_MESSAGE_MAP( MyDialog, CDialog ) ON_BN_CLICKED(IDC_RESET, OnReset) END_MESSAGE_MAP()

© Copyright Russell Tront, 2000 . Page 15-86

15.18 Dialog Data Exchange (DDE)/DD Validation

Recall that the life cycle and implementation of a dialog box must have these steps: 1) Main code constructs a dialog box subclass C++ instance.

This does not create the actual MS-Windows window yet.

2) Often the constructor does not do much initialization, but it can set whatever additional dialog instance variables that you have defined in the subclass. Note that the dialog ‘window’ itself and it’s child input controls do not exist yet! So, you canNOT yet initialize values within the dialog box child controls in the MFC C++ instance constructor.

3) Main code MUST invoke the dialog instance’s DoModal( ) function.

4) Dialog subclass should have an overriding OnInitDialog( ) function within the dialog class that (chains to the parent OnInitDialog( ) and then) initializes the dialog control items as necessary.

5) The user interacts with dialog box.

6) If the user clicks Ok button and before the dialog window (not instance) actually disappears with its input control’s state/data, Windows calls your dialog’s overriding OnOK( ) function. OnOK should use GetDlgItemText( ) to grab the data/state selected/entered into the various input controls within the dialog box, and store it somewhere, perhaps even in the C++ dialog instance’s instance attributes. You then chain to the parent OnOK( ) function that in turn closes the dialog ‘window’ (not C++ instance) by calling EndDialog( ). The C++ instance continues to live.

Page 44: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-87

7) Finally, the DoModal( ) function originally called from the main returns. Subsequent code in the main can check the DoModal( ) return value == IDOK. If so, your main knows that the dialog’s OnOK( ) function was called before the dialog died, and that the OnOK( ) function saved the new/updated data perhaps in the still existing C++ dialog instance’s attributes (i.e. where ever your OnOK( ) function copied them).

You can automate some of this using what is called Dialog Data Exchange (DDV). This is done by creating instance variables in your dialog subclass for each useful input control in your dialog box. Then you give Windows a simple association table that maps each input control ID to each instance variable.

© Copyright Russell Tront, 2000 . Page 15-88

DDE works like this: � You instantiate a C++ dialog object and can program its

constructor to set the instance variables. � Your main calls DoModal( ). � At the start of DoModal, Windows uses your association table

to automatically (!) copy all (!) instance variables in the table to the appropriate windows controls within your dialog. The dialog box then shows.

� At the end of DoModal( ), Windows’ default CDialog::OnOK( ) uses your association table to automatically copy the states of all the controls within your dialog back to the C++ dialog instance attributes. Often, you do not need to even override OnOK( ). The dialog window disappears.

� When DoModal( ) returns, the main can check the return value is IDOK, and if so, do whatever it wants with the new values of the attributes still in the C++ dialog instance.

What is the advantage of this?

First it seems more object-like. You set the initial values for the dialog window in the C++ dialog instance constructor instead of in the OnInitDialog( ) function. And you find the results there in those instance attributes after the dialog window is closed.

Second, you do NOT have to write SetDlgItem< > calls in the OnInitDialog( ) function, AND IN ADDITION write GetDlgItem< > code in the OnOK( ) function that copies all the variables out of the controls.

Page 45: 15. Microsoft Foundation Classes - Simon Fraser … Russell Tront, 2000 . Page 15-3 Section Table Of Contents IALOG 15. MICROSOFT FOUNDATION CLASSES.....1 15.1 A F IRST MFC P ROGRAM

© Copyright Russell Tront, 2000 . Page 15-89

The drawback is that you still have to copy all the data from your main to the dialog C++ instance attributes (usually in the constructor), and later get all the results you need from the dialog C++ instance.

Nonetheless, DDE is a cool feature. You can look up details in on-line help, and in most Visual C++ books.

It is also useful for Wizard tools when generating code. See the Visual C++ Class Wizard ‘Member Variables’ tab and the Add Variable button. (This presumes the input controls are pre-defined with IDs in a .rc file).

Note: There is another feature called Dialog Data Validation (DDV) that can help to make sure data input by the user is within correct ranges. [Prosise99] has some information on this in his Dialog Boxes chapter.

© Copyright Russell Tront, 2000 . Page 15-90

15.19 Modeless Dialog Boxes and Destruction

Modeless do NOT wait for the user to dismiss them using the Ok or Cancel mechanisms. They allow you to go around them and do other stuff with the application or with other applications, even while they are still present.

Modeless dialog boxes have to be destroyed NOT by letting the CDialog::OnOK( ) and CDialog::OnCancel( ) call EndDialog( ), but by yourself programming a call to CWnd::DestroyWindow( ). In fact, you must specifically prevent calls to CDialog::OnOK( ) and CDialog::OnCancel( ) by overriding them and NOT chaining back to the base class versions.

More information on destroying non-frame windows, and modeless dialog windows (which are dynamically created on the heap rather than as local variables in the main frame code), is available from MFC Technical Note #17:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_mfcnotes_tn017.asp