APPENDIX D VC++ Windows Programming The "Visual" part of the title means that VC++ not only provides the PDE functions mentioned in the previous appendix, but will actually write a significant part of your program itself if you choose to have a Graphical User Interface or GUI. Any time you use a program which allows the user to operate using mouse click buttons, pull-down menus, or pop-up windows, you are using a GUI. This approach to programming has become the standard and any competent software developer is expected to be able to produce applications with nice GUIs. The manual coding of GUIs for the display of buttons, pop-up windows, menus, etc. is very complex programming. VC++ addresses this problem with a GUI Wizard. Using the mouse and menus of options, VC++ a) allows a developer to layout an application GUI by dragging and dropping icons and then b) constructs the code necessary to display and manage the given layout operation. As a result, most of the user interface is written by VC++ and the developer is freed to work on the application programming itself. The complete coverage of VC++ GUI programming is beyond this text. In fact, such complete coverage may be beyond the printing industry's ability to bind such a large book. This discussion will provide a cookbook demonstration of a simple type of very general GUI known as a dialog. Using the concepts of this demonstration, a wide variety of programming applications can be constructed. D.1 Demonstration Consider the following simple demonstration calculator program (Fig. D.1) FIGURE D.1. Demonstration calculator program GUI The user types a number into each of the two "Argument" boxes and then clicks on one of the four function buttons. The result of the indicated operation appears in the "RESULT" box. This simple program requires hundreds of C++ code lines and the executable program is over 105,000 bytes in size. Using VC++, I was able to write this program in about 10 minutes and actually only provided 12 lines of code myself -- the rest was added by VC++ wizards. This concept will become more clear as we continue... Many GUIs utilize the same basic functionality; buttons for operations, labels for annotation, and display boxes where users many input values or values may be displayed. This is known as a single window dialog GUI. Each button or label or display box is called a control -- which may be a poor choice of words, but the purpose will be more clear later on. While there are other types of GUIs and many programs utilize more than one type, this is a good place to start. D.2 Project files The first step in creating a VC++ GUI program is to create the project. Select File/New in the top menu to bring up the project creation menu. This will bring up a menu. For a windows GUI program, select "MFC AppWizard.exe". The MFC stands for Microsoft Foundation Classes -- the basic library of classes containing the functionality of GUI operations. Next, enter a name for your project in the "Project name:" box and change the "Location" to reflect where on your disk you wish the project directory to be created. Now click OK and the project wizard will begin. A GUI project wizard provides many more options and selections than were seen in the previous appendix for simple console programs. Each menu window has four buttons at the bottom: You may page back and forth through the menu windows with Back and Next buttons at the bottom until you have made all your choices. The Finish button is chosen last and completes the wizard menus. For this discussion, select only the following options: · · · · · Dialog based (in English) 3-D controls MFC standard Yes please (I would like to generate source file comments) As a shared DLL (how I would like to use the MFC library) The last option (shared DLL) may not be presented depending on which edition of VC++ you are using. In that case, the default is exactly the same. When you click on Finish, the wizard writes as much code as is possible for your program without knowing the purpose of your program. As was mentioned in the previous appendix, this is called boilerplate. While the boilerplate for a simple console application is just a few lines, the boilerplate for a windows GUI may consist of hundreds of fairly confusing C++. Just ignore it for now. The PDE now presents you with a skeleton dialog GUI containing three controls (Fig. D.2). In the center is a simple label to inform you that this is where additional controls may be added. The first step is to delete this label. Click on it and hit the delete key. In the upper right are two default control buttons; OK and Cancel. If for some reason, you lose track of this dialog GUI layout display, you can always bring it to the front of the display by double-clicking on the GUI icon in the Dialog directory under the Resource tab of the workspace on the left of the PDE display. FIGURE D.2. D.3 Calculator program GUI layout workspace GUI layout Appearing now on the PDE display you will find the controls menu. This is simply a list of currently available controls for your GUI. To layout the calculator GUI, we simply drag controls from the menu to the skeleton dialog. Place the cursor on a control for a moment and the name of the control will be displayed. This demo will only be using three of these controls: Static text, Button, and Edit box. D.3.a Labels Static text is used for annotation. Drag the static text control to the GUI and position it at the top. Next (with this control still selected), type text for the label: "ADDING MACHINE DEMO". You may resize the label and move it in the standard Windows manner by dragging the box or its edges or corners. Notice that the font does not change when you make the box bigger. In the same manner, add static text for the two "Argument" labels and the "RESULT" label. D.3.b Buttons Next, add the buttons. Drag the button icon to the GUI and size appropriately. With the button still selected, type in new text to replace the default caption so that your button indicates "ADD". Now left click on the button and select `Properties' to change the default ID or name (Fig.D.3). The ID allows you to refer to this button in the program code. The default ID will be "IDC_BUTTON1". Most of VC++ boilerplate creates names and IDs with a prefix to attempt to describe the type and use of a name; the IDC prefix stands for IDentification Control. It is generally best to leave this prefix alone. Change the name to something more meaningful by changing the ending to "IDC_BUTTON_ADD". Repeat this same procedure for the four buttons (with similar meaningful captions and names). FIGURE D.3. Button control properties menu D.3.c Edit boxes Lastly, add the Edit boxes. Simply drag the Edit box control to the appropriate place and size. Again open the properties menu for the box and rename the ID to something more meaningful. In place of the default "IDC_EDIT1", use "IDC_EDIT_ARG1". We need to associate a variable with each Edit box. This variable will be used (by the boilerplate code written by VC++) to copy information from (or to) the box to (or from) your program. To associate a variable with an Edit box, again right-click on the control and this time choose the `ClassWizard' menu option (Fig. D.4). Select the Member Variables tab at the top of the window and then select the Edit box ID (IDC_EDIT_ARG1). At the right of the window, choose Add Variable. A subordinate window will pop up asking for the variable's name, category, and type. Again, a prefix is suggested by the wizard for the variable name; "m_". Complete the name as "m_arg1". The category default should be Value. Select int as the variable type. Repeat this process to associate variables with the "IDC_EDIT_ARG2" and "IDC_EDIT_RESULT" Edit boxes. FIGURE D.4. Associating a variable with an Edit box. At this point, the GUI is complete! You can (and should) build the program and execute just to insure that the GUI is correct. The boilerplate code written by the wizard along with the Windows 95/98/NT/XT operating system represent a fully functionally GUI with workable buttons and data editable boxes -- pressing the buttons and entering values into the boxes just won't accomplish anything. You can move your GUI around the screen as with other windows applications. When another window is opened on top of it and then removed, it nicely restores its appearance. (We'll ignore minimizing for now). Notice that the buttons even change shape when pressed to help the user. Your program will terminate when you click on OK or CANCEL or when you press the Return key after entering a value into an Edit box. Remember this last feature: By default, the user should click on an Edit box to select it and then use the keyboard to enter a value. The Backspace, Del, Insert, and arrow keys may all be used to help enter a value. After entering the value, use the mouse to select another Edit box or choose a button. The Return key should not be used for this simple demo. D.4 Message handlers So far, we have a fully functional GUI and we haven't written a single line of code. The last step is to associate code with each of the four operator buttons. When writing code for a VC++ GUI, you are adding code to the boilerplate provided by the wizards. The simplest way of thinking of it is adding functions for a main() program written by someone else to call. We need to add four functions -- one to be invoked when the user clicks on each of the operator buttons. These functions are known as message handlers or control notification handlers. Double clicking on a button in the GUI layout will cause the wizard to suggest a function name (the suggested defaults are usually fine) Accept the suggested name by clicking on OK and the wizard will write an empty function and place the cursor within that function for you to edit or complete. There will even be a helpful comment: //TODO: Add you control notification handler code here This is where you place the code to be executed when this button is pressed by the user. For the "IDC_BUTTON_ADD" button, the code should add the values currently in the "IDC_EDIT_ARG1" and "IDC_EDIT_ARG2" boxes and display the value in the "IDC_EDIT_RESULT" box. Here it is: UpdateData(TRUE); m_result = m_arg1 + m_arg2; UpdateData(FALSE); // add operation The function called in the first and third lines is very useful. When the parameter is the word (system defined constant) TRUE, the values in all the Edit boxes being copied into all their associated variables. When the parameter is the word FALSE, just the opposite; the associated variables are displayed in their associated Edit boxes. Notice that this performs more work than we needed. We only needed values copies from the first two argument boxes and then into the result box -- but no harm done. Now, if you add similar code to each of the other three operator buttons, your calculator program is complete. You actually only wrote 12 lines -- and 8 of these lines were just calls to `UpdateData()'. The basic steps then to creating a functional dialog GUI are the following; 1. Create a dialog project using the MFC AppWizard. 2. Place desired controls in the dialog window – using drag & drop from the controls menu. 3. Name buttons and edit boxes by right-clicking on the control and opening the properties window. (It is best to leave the suggested prefix in place.) 4. Place the appropriate labels on buttons by selecting the control and typing the desired text. 5. Add member variables to edit boxes by right-clicking on the control and using the class wizard. 6. Add code to the button event functions by double clicking on a button. D.5 Windows concepts Now, what about understanding all the boilerplate code written by the wizard? One significant advantage of using C++ is that it allows a programmer to easily use (or re-use) code written by others without the need to completely understand that code. Don't worry about it for now. A couple of concepts may be interesting however. Interesting at this point -- not necessary. The following discussion is somewhat simplified, but a good foundation. In a simple console application, the operating system passes control to main() which subsequently controls the execution of the program. You may have noticed that there is no `main()' in the boilerplate code of the example above. In a sense, the Windows operating system is the `main()' program and each application is simply added or linked into it. The basic concept of windows applications is that the program is controlled by events. Some basic events are a keystroke, a mouse click on a button, or resizing window. An event is defined by an action and the location of that action. A notification handler (or event handler) is simply a function which is to be called when the event occurs. To add an event handler, the function not only needs to be written, but the handler needs to be registered. In other words, the operating system needs to be told which handler to call for each event (or set of events). When an event occurs, the operating system looks in a table of registered event handlers and invokes the correct one. If no handler is registered for an event, the event is ignored. For example, when your calculator program is covered by another window, the calculator GUI is overwritten on the screen. When the calculator GUI is uncovered (an event), the event handler `OnPaint()' is invoked to re-paint the GUI on the screen. Notice that the boilerplate of your calculator contains such a function. If it did not, the uncover event would be ignored and your calculator would not be re-displayed. There are literally hundreds of events that can be handled for a windows application, giving the program a great deal of diversity and flexibility in GUI design. Some are quite standard and can be provided by the wizards -- such as the `OnPaint()' handler. Others are specific to a particular application -- such as your ADD button handler. Writing a windows application is a matter of writing event handlers and registering them. The detection of events and the calling of event handlers is usually standard and may not change between sets of applications. The details of this boilerplate are quite complex.