Simple GUI programming with MFC

advertisement
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.
Download