CATEGORII DOCUMENTE |
Asp | Autocad | C | Dot net | Excel | Fox pro | Html | Java |
Linux | Mathcad | Photoshop | Php | Sql | Visual studio | Windows | Xml |
Creating Menus for Your Application
* Menus
* Menu Styles
* Keyboard Shortcut-Enabling Menus
* Menu Standards and Conventions
* Designing Menus
* Creating a Menu
* Creating the Application
* Adding and Customizing a Menu
* Attaching the Menu to Your Dialog Window
* Attaching Functionality to Menu Entries
* Creating Pop-Up Menus
* Creating a Menu with Accelerators
* Summary
* Q&A
* Workshop
* Quiz
* Exercises
Most Windows applications use pull-down menus to provide the user a number of functions without having to provide buttons on the window. This enables you to provide your users a large amount of functionality while preserving most of your valuable screen real estate for other stuff.
Today you will learn
* How to create menus for your Visual C++ application
* How to attach a menu to your application's main dialog window
* How to call application functions from a menu
* How to create a pop-up menu that can be triggered with the right mouse button
* How to set up accelerator keys for keyboard shortcuts to menus
Menus
Back when the first computer terminals were introduced and users began using computer software, even on large mainframe systems software developers found the need to provide the users with some sort of menu of the functions that the computer could perform. These early menus were crude by today's standards and were difficult to use and navigate. Menus have progressed since then; they've become standardized in how they are used and easy to learn.
The software designers that first came up with the idea of a graphical user interface (GUI) planned to make computer systems and applications easier to learn by making everything behave in a consistent manner. Menus used for selecting application functionality were one part of the GUI design that could be more easily learned if they all worked the same. As a result, a number of standard menu styles were developed.
Menu Styles
The first menu styles that were standardized are the pull-down and cascading menus. These are the menus with the categories all listed in a row across the top of the application window. If you select one of the categories, a menu drops down below the category, with a number of menu entries that can be selected to trigger various functions in the application.
A variation on this menu style is the cascading menu, which has another submenu that opens to the right of a menu entry. This submenu is similar to the pull-down menu, with a number of entries that trigger application functions. The menu designers placed no limit on how many cascading menus can be strung together, but it quickly became clear to most developers that more than two cascading levels is a little unwieldy.
Eventually, a third style of menu was developed, called a pop-up or context menu--a menu that pops up in the middle of the application area, floating freely above the application work area. This is also called a context menu because the specific menu that pops up is dependent on the selected object or workspace area where the cursor or mouse pointer is.
Keyboard Shortcut-Enabling Menus
When users began working with keyboard-intensive applications, such as word processors, it was discovered that taking your hands off the keyboard to use the mouse to make menu selections dramatically reduced productivity. Software designers decided that they needed to add keyboard shortcuts for the various menu entries (especially the most frequently used menu options). For this reason, keyboard shortcuts (accelerators) and hotkeys were added.
Hotkeys are letters that are underlined in each menu entry. If you press the Alt key with the underlined letter, you can select the menu entry that contains the underlined letter. This is a means of navigating application menus without taking your hands off the keyboard.
For more advanced users, application designers added keyboard shortcuts, or accelerators. An accelerator is a single key combination that you can press to trigger an application function instead of having to navigate through the application menus. This allows advanced users to avoid the overhead of using menus for the most common application functions. To enable users to learn what accelerators are available in an application, the key combination is placed on the menu entry that it can be used to replace, positioned at the right edge of the menu window.
Menu Standards and Conventions
Although there are no standards in how menus are designed, there are a number of conventions for how they are designed and organized. All these conventions are available in Windows Interface Guidelines for Software Design, published by Microsoft for use by Windows software developers. The purpose of this publication is to facilitate the development of consistent application behaviors, which will help accomplish one of the primary goals behind the development of GUI systems. The conventions are as follows:
* Use single-word menu categories across the top menu bar. A two-word category can easily be mistaken for two one-word categories.
* The File menu is located as the first menu on the left. It contains all file-oriented functions (such as New, Open, Save, Print, and so on), as well as the Exit function. The Exit option is located at the bottom of the menu, separated from the rest of the menu entries by a border.
* The Edit menu is next to the File menu. The Edit menu contains all editing functions such as Copy, Cut, Paste, Undo, Redo, and so on.
* The View menu contains menu entries that control and affect the appearance of the application work area.
* The Window menu is used in Multiple Document Interface (MDI) style applications. This has functions for controlling the child windows, selecting the current window, and altering the layout. This menu is the next-to-last menu from the right end of the menu bar.
* The Help menu is the final menu on the right end of the menu bar. It contains menu entries that provide instruction or documentation on the application. If the application has any copyrighted or corporate information that needs to be available for viewing, this should be located as the final entry on this menu, labeled About <application name>.
Designing Menus
Menus are defined as a resource in Visual C++ applications. Because they are a resource, you can design menus in the Visual C++ editor through the Resource View tab on the workspace pane. When you first create a dialog-style application, there won't be a menu folder in the resource tree, but you can change that.
NOTE: Various aspects of Windows applications are considered to be resources, including window layouts, menus, toolbars, images, text strings, accelerators, and so on. All these features are organized in what is known as a resource file, which is used by the Visual C++ compiler to create these objects from their definitions. The resource file is a text file with an .rc filename extension and contains a textual description of all the various objects, including IDs, captions, dimensions, and so on.
Some resources, such as images and sounds, cannot be described in text, but have to be stored in a binary format. These resources are stored in individual files, with the filenames and locations included in the resource file.
Creating a Menu
Creating a menu is not difficult. You will follow several steps:
1. Create the application that will house the menu.
2. Add a menu resource to your project.
3. Customize the menu resource to include the menu items for your application.
4. Add functionality to your menu by connecting routines to your menu items.
Creating the Application
For the example in this chapter, you will create a simple dialog-style application that contains a single button and a menu. To create your application, do the following:
1. Create a new MFC AppWizard application, naming the project Menus.
2. Select the default AppWizard settings on all screens. For the dialog title, enter Menus.
3. When the AppWizard has generated your application shell, delete all the controls from the dialog.
4. Add a single button to the dialog. Name the button IDC_EXIT, and specify the caption as E&xit.
5. Add a function to the button using the Class Wizard. Change the code in this function to call OnOK. Remember, the OnOK function causes the application to close.
NOTE: If you don't remember how to add the OnOK function, review the section 'Closing the Application' on Day 2, 'Using Controls in Your Application,' for an example.
Adding and Customizing a Menu
Now that you have the basic application built, it's time to start creating a menu for the application. To create a menu, you will first add a menu resource to your project. When you add the resource, Visual C++ automatically invokes the Menu Designer, which allows you to customize the menu. The following steps show you how to add and customize a menu:
1. Select the Resource View tab in the workspace pane.
2. Select the project resources folder at the top of the tree; in your example, this is Menus.
3. Right-click the mouse to bring up a pop-up menu.
4. Select Insert from the pop-up menu.
5. In the Insert Resource dialog that opens, select Menu from the list of available resources, as in Figure 6.1. Click the New button.
FIGURE 6.1. The Insert Resource dialog.
6. The Menu Designer opens in the editing area of Developer Studio. The first menu spot is highlighted, as shown in Figure 6.2.
FIGURE 6.2. An empty menu.
At this point, you have created the menu resource and you are ready to customize it by adding menu items. To add a menu item, follow these steps:
1. Right-click the mouse on the highlighted area and select Properties from the pop-up menu.
2. Enter the menu item's Caption. For this example, enter &File and close the Properties dialog.
NOTE: You are in the menu Properties dialog to specify the text that the user will see on the menu bar while the application is running. Because the Pop-up check box is checked (by default on any menu items on the top-level menu bar), this menu element doesn't trigger any application functionality and thus doesn't need to have an object ID assigned to it.
3. The first drop-down menu location is highlighted. To add this menu item, right-click the mouse again on the highlighted area and select Properties from the pop-up menu.
4. Enter an ID and caption for the menu item. For this example, enter IDM_FILE_HELLO for the ID and &Hello for the Caption. Close the dialog.
NOTE: This time in the menu Properties dialog, you not only specify the text that the user will see when the menu is opened from the menu bar, but you also specify the object ID that will be used in the event message handler to determine what function receives each of the menu events.
At this point you have created a menu with a single menu item. You can continue to add menu items by repeating steps 3 and 4 of the preceding list for each of the highlighted areas. You can also add separators onto the menu. A separator is a dividing line that runs across the menu to separate two functional areas of menu selections. To add a separator, perform the following steps:
FIGURE 6.3. Specifying a menu separator.
1. Select the highlighted area where you want the separator to be placed. In the example you created, the second drop-down menu location should be highlighted. Open the properties dialog as you did in step 3 in the preceding list. To add a separator, simply select the Separator option, as shown in Figure 6.3, and close the dialog.
To complete your sample program, follow the same steps I just described to add an Exit item to your File menu and a second menu called Help with one menu item called About. The following steps, which resemble the preceding list of steps, walk you through adding these additional items:
1. Open the properties dialog for the third drop-down location and specify the ID as IDM_FILE_EXIT and the caption as E&xit. Close the dialog.
2. Select the second top-level menu location and open the properties dialog. Specify the caption as &Help and close the dialog.
3. Open the properties dialog for the first drop-down location on the second top-level menu. Specify the ID as ID_HELP_ABOUT and the caption as &About. Close the dialog.
At this point, your menu is created; however, it is not attached to your application.
Attaching the Menu to Your Dialog Window
You now have a menu that you can use in your application. If you compile and run your application at this point, however, the menu doesn't appear. You still need to attach the menu to your dialog window. You can attach a menu by following these steps:
1. Open the dialog painter by double-clicking the primary application dialog in the Dialog folder in the Workspace pane. For this example, double-click on IDD_MENUS_DIALOG.
2. Select the entire dialog window, making sure that no controls are selected, and open the dialog's properties dialog. (What you are doing is opening the properties for the dialog window itself, not for any of the controls that might be on the window.)
3. Select the menu you have designed from the Menu drop-down list box, as shown in Figure 6.4.
FIGURE 6.4. Attaching the menu to the dialog window.
If you compile and run your application, you find that the menu is attached to the application dialog, as shown in Figure 6.5. You can select menu entries as you do with any other Windows application--with one small difference. At this point, when you select one of the menu entries, nothing happens. You still need to attach functionality to your menu.
FIGURE 6.5. The menu is now part of the application dialog.
Attaching Functionality to Menu Entries
Now that you have a menu as part of your application, it sure would be nice if it actually did something. Well, before your menu can do anything, you have to tell it what to do, just like everything else in your Visual C++ applications. To attach some functionality to your menu, follow these steps:
FIGURE 6.6. The menu is now part of the application.
1. Open the Menu Designer to your menu.
2. Open the Class Wizard from the View menu.
3. The Adding a Class dialog is displayed for you, just as it was yesterday when you added a second dialog. Leave the dialog selection on Select an Existing Class and click OK (see Figure 6.6).
Yesterday, when you were adding a second dialog window to your application, you needed to create a new C++ class for that window. For today's menu, you want to attach it to the existing C++ class for the dialog window to which the menu is attached.
4. Choose the C++ class of the primary dialog window from the list of available classes in the Select Class dialog. For this example, select CMenusDlg, as shown in Figure 6.7. This tells Visual C++ that all the functionality that you will call from the various menu entries is part of the same dialog class of the window that it's attached to.
FIGURE 6.7. The Select Class dialog.
For the menu elements that you want to use to trigger new functions in your application, you can add event-handler functions through the Class Wizard, just as you can with controls that you place on the dialog window.
For this example, add a function for the IDM_FILE_HELLO object (the Hello menu) on the COMMAND event message. Name the function OnHello and add the code in Listing 6.1 to the function.
LISTING 6.1. THE ONHELLO FUNCTION.
1: void CMenusDlg::OnHello()
2:
NOTE: The COMMAND event message is the message that is passed to the application window when a menu entry is selected. Placing a function on this event message has the same effect as placing a function on the menu entry selection.
You can call existing event handlers from menu elements by adding the existing function to the menu COMMAND event. You can do this by adding a function to the menu object ID and then specifying the existing function name instead of accepting the suggested function name.
To reuse the OnExit function for the Exit menu element, reopen the Menu Designer and then reopen the Class Wizard. When the Class Wizard is displayed, add a function for the IDM_FILE_EXIT object on the COMMAND event message. Do not accept the default function name presented to you by the Class Wizard. Enter the function name OnExit. This automatically attaches the existing OnExit function that you created with your Exit button earlier.
To round out your example's functionality, add a function to the ID_HELP_ABOUT object on the COMMAND event message. Edit the function as in Listing 6.2.
LISTING 6.2. THE ONHELPABOUT FUNCTION.
1: void CMenusDlg::OnHelpAbout()
2:
You attached the File | Exit menu entry to an existing function that closes the application. On the File | Hello, you added a new function that called the MessageBox function to display a simple message to the user. With Help | About, you added another function that declared an instance of the About dialog window and called its DoModal method.
If you compile and run your application, you find that all the menu entries are working. If you select Help | About, as shown in Figure 6.8, you see the application About dialog (see Figure 6.9). If you select File | Hello, you see a Hello there message box, as shown in Figure 6.10. And if you select File | Exit, your application closes.
FIGURE 6.8. The Help | About menu entry.
FIGURE 6.9. The About dialog.
FIGURE 6.10. The Hello there message box.
Creating Pop-Up Menus
Most Windows applications have what are called either pop-up or context menus, which are triggered by the user right-clicking an object. These are called pop-up menus because they pop up in the middle of the application area, not attached to a menu bar, the window frame, or anything else on the computer screen (not counting the mouse pointer). These menus are often referred to as context menus because the contents of a menu depend on the context in which it is opened; the elements available on the menu depend on what objects are currently selected in the application or what the mouse pointer is positioned over.
To provide a pop-up menu in your application, you have two approaches available. You can either design a menu specifically for use as a pop-up menu, or you can use one of the pull-down menus from the primary menu that you have already designed. If you design a menu specifically for use as a pop-up menu, you will need to skip the top-level, menu bar element by placing a space or some other text in the caption, knowing that it will not be seen. You will see how this works when you build a custom menu specifically for use as a pop-up menu on Day 11, 'Creating Multiple Document Interface Applications,' in the section 'Adding a Context Menu.'
Every drop-down portion of a menu can also be used as a pop-up menu. To use it in this way, you must get a handle to the submenu (the drop-down menu) and then call the TrackPopupMenu function on the submenu. The rest of the pop-up menu functionality is already covered in the other menu building and coding that you have already done. To add a pop-up menu to your application, follow these steps:
1. Using the Class Wizard, add a function for the WM_CONTEXTMENU event message in your dialog window.
NOTE: There are two dialog event messages that you can use to trigger your context menu. The event that you'd expect to use is the WM_RBUTTONDOWN event, which is triggered by the user right-clicking. The other event that can (and should) be used is the WM_CONTEXTMENU event, which is intended for use specifically to trigger a context menu. This event is triggered by a couple user actions: One of these is the release of the right mouse button, and another is the pressing of the context menu button on one of the newer Windows-enabled keyboards.
2. Edit the function, adding the code in Listing 6.3.
LISTING 6.3. THE ONCONTEXTMENU FUNCTION.
1: void CMenusDlg:: OnContextMenu(CWnd* pWnd, CPoint point)
2:
In Listing 6.3, the first thing that you did was make a copy of the mouse position. This mouse position is a relative position within the window area. It must be converted to an absolute position on the entire screen area for displaying the pop-up menu. If you don't convert the position coordinates, you can't predict where your pop-up menu will appear.
After you convert the position to an absolute position, you get a pointer to the window menu. This pointer should always be a local pointer within the function where you are going to use it because the location of the menu might change as the application runs. From the menu pointer, you next get a pointer to the first drop-down menu (submenu numbering begins with 0, like just about everything else in C/C++). After you have a pointer to the submenu, you can treat it as a regular CMenu class instance.
The final piece in this puzzle is the call to the CMenu member function TrackPopupMenu. This function takes five arguments and uses them to determine where and how to show the pop-up menu. The first argument is a combination of two flags. The first flag, TPM_CENTERALIGN, centers the pop-up menu on the mouse point. You can also use TPM_LEFTALIGN or TPM_RIGHTALIGN instead. These flags line up the left or right edge of the pop-up menu with the mouse position. The second part of this flag combination is TPM_LEFTBUTTON, which makes the pop-up menu trigger from the left mouse button. You can also use TPM_RIGHTBUTTON to make the menu trigger from the right mouse button.
The second and third arguments to the TrackPopupMenu function specify the screen position for the pop-up menu. This is the absolute position on the screen, not a relative position within the window area. The fourth argument is a pointer to the window that receives the menu command messages. The final argument is a rectangle that the user can click without closing the pop-up menu. By passing NULL, you specify that if the user clicks outside the pop-up menu, the menu closes. This code enables you to include a pop-up menu in your application, as shown in Figure 6.11.
FIGURE 6.11. The pop-up menu in action.
Creating a Menu with Accelerators
One of the original keyboard shortcuts for selecting menu entries were accelerator keys. As mentioned earlier in the chapter, accelerator keys are specific key combinations, usually the Ctrl key combined with another key, or function keys, that are unique within the entire application. Each of these key combinations triggers one menu event function.
The way that accelerator keys work is similar to the way menus work. They are also an application resource that is defined in a table in the resource tab of the workspace pane. Each table entry has an object ID and a key code combination. After you define the accelerators, you can attach functionality to the object IDs. You can also assign accelerator entries the same object ID as the corresponding menu entry so that you have to define only a single entry in the application message map.
After you define all your accelerator keys, you can add the key combination to the menu entry so that the user will know about the accelerator key combination. Add t to the end of the menu entry caption, followed by the key combination. The t is replaced in the menu display by a tab, which separates the menu caption from the accelerator key combination.
Unfortunately, accelerator keys don't work in dialog-style windows, so you cannot add them to today's application. You will learn how to attach accelerator keys to menus in a few days when you learn about single and multi-document interface style applications.
Summary
Today you learned about menus in Visual C++ applications. You learned how to use the tools in Visual C++ to create a menu for use in your application and then how to attach the menu to a window in your application. After you had the menu attached to your window, you learned how to attach functionality to the various menu entries. Later in the day, you learned how you can use a portion of your menu as a pop-up, or context, menu. Finally, you learned how accelerator keys are added to most applications.
Q&A
Q Do I have to name my menu items the same names everyone else uses? For example, a lot of applications use File and Help. Can I name my menus something else?
A You can name your top-level menus anything you want. However, there are ac-cepted menu name conventions that place all file-oriented functionality under a menu labeled File and all help-related functionality under a menu labeled Help. If you have a menu with entries such as Broccoli, Corn, and Carrots, you will probably want to call the menu Vegetables, although an equally valid label would be Food or Plants. In general, if you want to make your application easy for your users to learn, you will want to use menu labels that make sense for the entries o n the pull-down portion of the menu.
Q Why can't I specify a single character as an accelerator key?
A The single character would trigger the WM_KEY messages, not the menu messages. When the designers of Windows were deciding how accelerator keys would work, they decided that single-character keys would most likely be input to the active application. If they had allowed single-character accelerators, Windows wouldn't be able to determine whether the character was input or a shortcut. By requiring a key combination (with the exception of function keys), the designers ensured that Windows won't have to make this determination.
Workshop
The Workshop provides quiz questions to help you solidify your understanding of the material covered and exercises to provide you with experience in using what you've learned. The answers to the quiz questions are provided in Appendix B, 'Answers.'
Quiz
1. What event message does a menu selection send to the window message queue?
2. How do you attach a menu to a dialog window?
3. Which existing class do you specify for handling event messages for the menu?
4. What event message should a pop-up menu be triggered by?
Exercises
1. Add a button to the main window and have it call the same function as the Hello menu entry.
2. Add a pop-up menu to your application that uses the Help drop-down menu as the pop-up menu.
Teach Yourself Visual C++ 6 in 21 Days -- Ch 7 -- Working with Text and Fonts
Teach Yourself Visual C++ 6 in 21 Days
Working with Text and Fonts
* Finding and Using Fonts
* Listing the Available Fonts
* Using a Font
* Using Fonts
* Creating the Application Shell
* Building a List of Fonts
* Setting the Font Sample Text
* Selecting a Font to Display
* Summary
* Q&A
* Workshop
* Quiz
* Exercises
* In Review
In most Windows applications, you don't need to worry about specifying fonts, much less their weight, height, and so on. If you don't specify the font to be used, Windows supplies a default font for your application. If you do need to use a particular font, you can specify a font to use for a particular dialog window through the dialog properties. Sometimes, however, you want or need to control the font used in your application. You might need to change the font being used or allow the user to select a font to use in a particular instance. It is for those circumstances that you will learn how to change and list fonts today. Among the things that you will learn are
* How to build a list of available fonts.
* How to specify a font for use.
* How to change fonts dynamically.
Finding and Using Fonts
One of the first things that you need to know when working with fonts is that not every system that your applications run on will have the same fonts installed. Fonts are specified in files that can be installed and removed from Windows systems with relative ease. Every computer user can customize his system with whatever combination of fonts he wants. If you specify a font that doesn't exist on the system, Windows will choose either the system default font or what the operating system considers to be a reasonably close alternative font.
What you can do instead is ask the operating system what fonts are available. This method allows you to make your own decisions on which font to use or let the user make the decision. When you ask what fonts are available, you can limit the types of fonts that are listed, or you can choose to list them all and select various fonts based on various attributes.
Listing the Available Fonts
To get a list of all available fonts on a computer, you call a Windows API (Application Programming Interface) function called EnumFontFamiliesEx. This function tells Windows that you want a list of the fonts on the system. Before you start using this function and expecting it to pass you a big list of available fonts, you need to understand how it gives you the list.
Callback Functions
One of the key arguments to the EnumFontFamiliesEx function is the address of another function. This second function is what is known as a callback function, which is called by the operating system. For almost every enumeration function in the Windows operating system, you pass the address of a callback function as an argument because the callback function is called once for each of the elements in the enumerated list. In other words, you have to include a function in your application to receive each individual font that is on the system and then build the list of fonts yourself.
When you create this function to receive each font and build your list, you cannot define your callback function in any way you want. All callback functions are already defined in the Windows API. You have to use a specific type of callback function to receive the list of fonts. For getting a list of fonts, the function type is EnumFontFamProc. This function type specifies how your function must be defined, what its arguments must be, and what type of return value it must return. It does not specify what your function should be named or how it needs to work internally. These aspects are left completely up to you.
The EnumFontFamiliesEx Function
The EnumFontFamiliesEx function, which you call to request the list of available fonts, takes five arguments. A typical use of this function follows:
// Create a device context variable
CClientDC dc (this);
// Declare a LOGFONT structure
LOGFONT lLogFont;
// Specify the character set
lLogFont.lfCharSet = DEFAULT_CHARSET;
// Specify all fonts
lLogFont.lfFaceName[0] = NULL;
// Must be zero unless Hebrew or Arabic
lLogFont.lfPitchAndFamily = 0;
// Enumerate the font families
::EnumFontFamiliesEx((HDC) dc, &lLogFont,
(FONTENUMPROC) EnumFontFamProc, (LPARAM) this, 0);
The first argument is a device context, which can be an instance of the CClientDC class. Every application running within the Windows operating system has a device context. The device context provides a lot of necessary information to the operating system about what is available to the application and what is not.
The second argument is a pointer to a LOGFONT structure. This structure contains information about the fonts that you want listed. You can specify in this structure which character set you want to list or whether you want all the fonts in a particular font family. If you want all the fonts on the system, you pass NULL in the place of this argument.
The third argument is the address of the callback function that will be used to build your list of fonts. Passing the address of your callback function is a simple matter of using the function name as the argument. The Visual C++ compiler takes care of replacing the function name with the function address. However, you do need to cast the function as the type of callback function that the function requires.
The fourth argument is a LPARAM value that will be passed to the callback function. This parameter is not used by Windows but provides your callback function with a context in which to build the font list. In the example, the value being passed is a pointer to the window in which the code is being run. This way, the callback function can use this pointer to access any structures it needs to build the list of fonts. This pointer can also be the first node in a linked list of fonts or other such structure.
The fifth and final argument is always 0. This reserved argument may be used in future versions of Windows, but for now, it must be 0 so that your application passes a value that won't cause the function to misbehave.
The EnumFontFamProc Function Type
When you create your callback function, it must be defined as an independent function, not as a member of any C++ class. A typical EnumFontFamProc function declaration follows:
int CALLBACK EnumFontFamProc(
LPENUMLOGFONT lpelf,
LPNEWTEXTMETRIC lpntm,
DWORD nFontType,
long lParam)
The first argument to this function is a pointer to an ENUMLOGFONTEX structure. This structure contains information about the logical attributes of the font, including the font name, style, and script. You may have numerous fonts listed with the same name but different styles. You can have one for normal, one for bold, one for italic, and one for bold italic.
The second argument is a pointer to a NEWTEXTMETRICEX structure. This structure contains information about the physical attributes of the font, such as height, width, and space around the font. These values are all relative in nature because they need to scale as the font is made larger or smaller.
The third argument is a flag that specifies the type of font. This value may contain a combination of the following values:
* DEVICE_FONTYPE
* RASTER_FONTYPE
* TRUETYPE_FONTYPE
Finally, the fourth argument is the value that was passed into the EnumFontFamiliesEx function. In the example, it was a pointer to the dialog on which the list of fonts is being built. If you cast this value as a pointer to the dialog, the function can access a list box control to add the font names.
The return value from this function determines whether the listing of fonts continues. If 0 is returned from this function, the operating system quits listing the available fonts. If 1 is returned, the operating system continues to list the available fonts.
Using a Font
To use a particular font in an application, you call an instance of the CFont class. By calling the CreateFont method, you can specify the font to be used, along with the size, style, and orientation. Once you've created a font, you can tell a control or window to use the font by calling the object's SetFont method. An example of this process follows:
CFont m_fFont; // The font to be used
// Create the font to be used
m_fFont.CreateFont(12, 0, 0, 0, FW_NORMAL,
0, 0, 0, DEFAULT_CHARSET, OUT_CHARACTER_PRECIS,
CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH |
FF_DONTCARE, m_sFontName);
// Set the font for the display area
m_ctlDisplayText.SetFont(&m_fFont);
TIP: The CFont variable used in the previous code should be declared as a member variable of the class in which this code is placed. In the sample code, it is declared above where it is used to show how it is declared. This variable should not be declared or used as a local variable in a function.
Seems simple enough--just two function calls--but that CreateFont function needs an awful lot of arguments passed to it. It is these arguments that make the CreateFont method a flexible function with a large amount of functionality. Once you create the font, using it is a simple matter of passing the font to the SetFont method, which is a member of the CWnd class and thus available to all window and control classes in Visual C++. This means that you can use this technique on any visible object within a Visual C++ application.
To understand how the CreateFont function works, let's look at the individual arguments that you have to pass to it. The function is defined as
BOOL CreateFont(
int nHeight,
int nWidth,
int nEscapement,
int nOrientation,
int nWeight,
BYTE bItalic,
BYTE bUnderline,
BYTE cStrikeOut,
BYTE nCharSet,
BYTE nOutPrecision,
BYTE nClipPrecision,
BYTE nQuality,
BYTE nPitchAndFamily,
LPCTSTR lpszFaceName);
The first of these arguments, nHeight, specifies the height of the font to be used. This logical value is translated into a physical value. If the value is 0, a reasonable default value is used. If the value is greater or less than 0, the absolute height is converted into device units. It is key to understand that height values of 10 and -10 are basically the same.
The second argument, nWidth, specifies the average width of the characters in the font. This logical value is translated into a physical value in much the same way as the height is.
The third argument, nEscapement, determines the angle at which the text will be printed. This value is specified in 0.1-degree units in a counterclockwise pattern. If you want to print vertical text that reads from bottom to top, you supply 900 as the value for this argument. For printing normal horizontal text that flows from left to right, supply 0 as this value.
The fourth argument, nOrientation, determines the angle of each individual character in the font. This works on the same basis as the previous argument, but it controls the output on a character basis, not a line-of-text basis. To print upside-down characters, set this value to 1800. To print characters on their backs, set this value to 900.
The fifth argument, nWeight, specifies the weight, or boldness, of the font. This can be any value from 0 to 1000, with 1000 being heavily bolded. You can use constants defined for this argument to control this value with ease and consistency. These constants are listed in Table 7.1.
TABLE 7.1. FONT WEIGHT CONSTANTS.
Constant Value
FW_DONTCARE 0
FW_THIN 100
FW_EXTRALIGHT 200
FW_ULTRALIGHT 200
FW_LIGHT 300
FW_NORMAL 400
FW_REGULAR 400
FW_MEDIUM 500
Constant Value
FW_SEMIBOLD 600
FW_DEMIBOLD 600
FW_BOLD 700
FW_EXTRABOLD 800
FW_ULTRABOLD 800
FW_BLACK 900
FW_HEAVY 900
The actual interpretation and availability of these weights depend on the font. Some fonts only have FW_NORMAL, FW_REGULAR, and FW_BOLD weights. If you specify FW_DONTCARE, a default weight is used, just as with most of the rest of the arguments.
The sixth argument, bItalic, specifies whether the font is to be italicized. This is a boolean value; 0 indicates that the font is not italicized, and any other value indicates that the font is italicized.
The seventh argument, bUnderline, specifies whether the font is to be underlined. This is also a boolean value; 0 indicates that the font is not underlined, and any other value indicates that the font is underlined.
The eighth argument, cStrikeOut, specifies whether the characters in the font are displayed with a line through the character. This is another boolean value using a non-zero value as TRUE and 0 as FALSE.
The ninth argument, nCharSet, specifies the font's character set. The available constants for this value are listed in Table 7.2.
TABLE 7.2. FONT CHARACTER SET CONSTANTS.
Constant Value
ANSI_CHARSET 0
DEFAULT_CHARSET 1
SYMBOL_CHARSET 2
SHIFTJIS_CHARSET 128
OEM_CHARSET 255
The system on which your application is running might have other character sets, and the OEM character set is system dependent, making it different for systems from different manufacturers. If you are using one of these character sets, it is risky to try to manipulate the strings to be output, so it's best to just pass along the string to be displayed.
The tenth argument, nOutPrecision, specifies how closely the output must match the requested font's height, width, character orientation, escapement, and pitch. The available values for this argument are
* OUT_CHARACTER_PRECIS
* OUT_DEFAULT_PRECIS
* OUT_DEVICE_PRECIS
* OUT_RASTER_PRECIS
* OUT_STRING_PRECIS
* OUT_STROKE_PRECIS
* OUT_TT_PRECIS
The OUT_DEVICE_PRECIS, OUT_RASTER_PRECIS, and OUT_TT_PRECIS values control which font is chosen if there are multiple fonts with the same name. For instance, if you use the OUT_TT_PRECIS value and specify a font with both a TrueType and raster version, then the TrueType version is used. In fact, the OUT_TT_PRECIS value forces the system to use a TrueType font, even when the specified font does not have a TrueType version.
The eleventh argument, nClipPrecision, specifies how to clip characters that are partially outside of the display area. The values for this argument are
* CLIP_CHARACTER_PRECIS
* CLIP_DEFAULT_PRECIS
* CLIP_ENCAPSULATE
* CLIP_LH_ANGLES
* CLIP_MASK
* CLIP_STROKE_PRECIS
* CLIP_TT_ALWAYS
These values can be ORed together to specify a combination of clipping techniques.
The twelfth argument, nQuality, specifies the output quality and how carefully the GDI (Graphics Device Interface) must attempt to match the logical font attributes to the physical font output. The available values for this argument are
* DEFAULT_QUALITY
* DRAFT_QUALITY
* PROOF_QUALITY
The thirteenth argument, nPitchAndFamily, specifies the pitch and family of the font. This value consists of two values that are ORed together to create a combination value. The first set of available values is
* DEFAULT_PITCH
* VARIABLE_PITCH
* FIXED_PITCH
This value specifies the pitch to be used with the font. The second set of available values specifies the family of fonts to be used. The available values for this portion of the argument are
* FF_DECORATIVE
* FF_DONTCARE
* FF_MODERN
* FF_ROMAN
* FF_SCRIPT
* FF_SWISS
The font family describes in a general way the appearance of a font. You can use the font family value to choose an alternative font when a specific font does not exist on a system. The final argument, lpszFacename, is a standard C-style string that contains the name of the font to be used. This font name comes from the font information received by the EnumFontFamProc callback function.
Using Fonts
Today you will build an application that allows the user to select from a list of available fonts to be displayed. The user will be able to enter some text to be displayed in the selected font, allowing the user to see what the font looks like.
Creating the Application Shell
To begin today's application, follow these steps:
1. Create a new project workspace using the MFC AppWizard. Name the project Day7.
2. Use the same defaults that you used for the previous day's projects, giving the application a title of Fonts.
3. Design the main dialog as in Figure 7.1, using the properties in Table 7.3.
FIGURE 7.1. The main dialog layout.
TABLE 7.3. CONTROL PROPERTY SETTINGS.
Object Property Setting
Static Text ID IDC_STATIC
Caption &Enter Some Text:
Edit Box ID IDC_ESAMPTEXT
Static Text ID IDC_STATIC
Caption &Select a Font
List Box ID IDC_LFONTS
Group Box ID IDC_STATIC
Caption Font Sample
Static Text ID IDC_DISPLAYTEXT
(inside group box; size to Caption Empty string
fill the group box)
Command Button ID IDC_EXIT
Caption E&xit
4. Using the Class Wizard, add the variables in Table 7.4 to the controls on the dialog.
TABLE 7.4. CONTROL VARIABLES.
Object Name Category Type
IDC_DISPLAYTEXT m_ctlDisplayText Control CStatic
m_strDisplayText Value CString
IDC_LFONTS m_ctlFontList Control CListBox
m_strFontName Value CString
IDC_ESAMPTEXT m_strSampText Value CString
5. Attach a function to the IDC_EXIT button to close the application, as in the previous day's applications.
Building a List of Fonts
To be able to create your list of fonts, you need to add your callback function to get each font list and add it to the list box that you placed on the dialog window. To do this, edit the Day7Dlg.h header file and add the function declaration in Listing 7.1 near the top of the file. This function cannot be added through any of the tools available in Visual C++. You need to open the file and add it yourself.
LISTING 7.1. THE CALLBACK FUNCTION DECLARATION IN THE Day7Dlg.h HEADER FILE.
1: #if _MSC_VER > 1000
2: #pragma once
3: #endif // _MSC_VER > 1000
4:
5: int CALLBACK EnumFontFamProc(LPENUMLOGFONT lpelf,
6: LPNEWTEXTMETRIC lpntm, DWORD nFontType, long lParam);
7:
8: //////////////////////////////////////////////////////////////////// 9: // CDay7Dlg dialog
11: class CDay7Dlg : public CDialog
Once you add the function declaration to the header file, open the Day7Dlg.cpp source-code file, scroll to the bottom of the file, and add the function definition in Listing 7.2.
LISTING 7.2. THE CALLBACK FUNCTION DEFINITION IN THE Day7Dlg.cpp SOURCE FILE.
1: int CALLBACK EnumFontFamProc(LPENUMLOGFONT lpelf,
2: LPNEWTEXTMETRIC lpntm, DWORD nFontType, long lParam)
3:
Now that you have the callback function defined, you need to add a function to request the list of fonts from the operating system. To add this function, follow these steps:
1. Select the Class View tab on the project workspace pane.
2. Select the CDay7Dlg class, right-click the mouse, and select Add Member Function from the pop-up menu.
3. Specify the function type as void, the function declaration as FillFontList, and the access as Private. Click the OK button to close the dialog and add the function.
4. Edit the function definition as in Listing 7.3.
LISTING 7.3. THE FillFontList FUNCTION.
1: void CDay7Dlg::FillFontList()
2:
5. Edit the OnInitDialog function to call the FillFontList function, as in Listing 7.4.
LISTING 7.4. THE EDITED OnInitDialog FUNCTION.
1: BOOL CDay7Dlg::OnInitDialog()
2:
If you compile and run your application now, you should find that your list box is filled with the names of all the fonts available on the system. However, there's one aspect of this list that you probably don't want in your application. Figure 7.2 shows many duplicate entries in the list of fonts in the list box. It would be nice if you could eliminate these duplicates and have only one line per font.
FIGURE 7.2. Listing all the fonts in the system.
It turns out that the EnumFontFamiliesEx function call is synchronous in nature. This means that it doesn't return until all the fonts in the system are listed in calls to your callback function. You can place code in the FillFontList function to remove all the duplicate entries once the list box is filled. To do this, modify the FillFontList function as in Listing 7.5.
Listing 7.5. The modified FILLFONTLIST function.
1: void CDay7Dlg::FillFontList()
2:
// Set the previous font name to the current font name
strPrevFont = strCurFont;
}
Notice that the for loop started at the end of the list and worked backward. This allowed you to delete the current entry without worrying about manipulating the loop counter to prevent skipping lines in the list box. If you compile and run your application, there shouldn't be any duplicate entries in the list of available fonts.
Setting the Font Sample Text
Before you can display the font for the user, you need to place some text into the display area. The edit box near the top of the dialog is where the user enters text to be displayed in the font selected. To add the functionality, do the following:
1. Edit the OnInitDialog function to add code to initialize the edit box and display text, as in Listing 7.6.
LISTING 7.6. THE MODIFIED OnInitDialog FUNCTION.
1: BOOL CDay7Dlg::OnInitDialog()
2:
2. Using the Class Wizard, add a function on the EN_CHANGE event message for the IDC_ESAMPTEXT edit box control.
3. Edit the function you just added, adding the code in Listing 7.7.
LISTING 7.7. THE OnChangeEsamptext FUNCTION.
1: void CDay7Dlg::OnChangeEsamptext()
If you compile and run your application, you should be able to type text into the edit box and see it change in the font display area in the group box below.
Selecting a Font to Display
Before you can start changing the font for the display area, you'll need to have a CFont member variable of the dialog class that you can use to set and change the display font. To add this variable, follow these steps:
1. In the Class View of the workspace pane, right-click the mouse on the CDay7Dlg class. Select Add Member Variable from the pop-up menu.
2. Specify the variable type as CFont, the variable name as m_fSampFont, and the access as Private. Click the OK button to close the dialog box and add the variable.
When adding the code to use the selected font, you'll add it as a separate function that is not attached to a control. Why you do this will become clear as you proceed further through building and running today's application. To add the function to display and use the selected font, follow these steps:
1. In the Class View of the workspace pane, right-click the mouse on the CDay7Dlg class. Select Add Member Function from the pop-up menu.
2. Specify the function type as void, the function declaration as SetMyFont, and the access as Private. Click the OK button to close the dialog and add the function.
3. Edit the function, adding the code in Listing 7.8.
LISTING 7.8. THE SetMyFont FUNCTION.
1: void CDay7Dlg::SetMyFont()
2:
4. Using the Class Wizard, add a function to the LBN_SELCHANGE event message for the IDC_LFONTS list box. Edit the function, adding the code in Listing 7.9.
LISTING 7.9. THE OnSelchangeLfonts FUNCTION.
1: void CDay7Dlg::OnSelchangeLfonts()
2:
In the SetMyFont function, you first checked to make sure that a font had been selected. Next, you retrieved the area of the static text control that will be used to display the font. This enables you to specify a font height just slightly smaller than the height of the area you have available to display the font in. After you calculated the height of the static text control and made sure that it is a positive value, you created the selected font and told the static text control to use the newly created font.
In the OnSelchangeLfonts function, you copy the control values to the attached variables and then call the SetMyFont function to use the selected font. If you compile and run your application, you should be able to select a font and see it displayed in the sample static text control, as in Figure 7.3.
FIGURE 7.3. Displaying the selected font.
Summary
Today you learned how to use fonts in Visual C++ applications. You learned how to get a list of the available fonts that are loaded on the system and then how to create a font for use on a display object. You learned how you can create and use callback functions to get a list of resources from the Windows operating system. You also learned how you can access controls from the callback function using a window pointer that you passed to the function requesting the resource list.
Q&A
Q The CreateFont function has a lot of arguments to specify and pass. Is there any other alternative to using this function?
A Yes, there is, although you still specify all of the same information. A structure called LOGFONT contains all the same attributes that are passed to the CreateFont function. You can declare an instance of this structure, initializing the attributes to default values, and then pass this structure to the CreateFontIndirect function. If you make numerous font changes, this approach is preferable because you could use the same instance of the structure, modifying those attributes that are changing from the current settings and using it to create the various fonts.
The way that you use this alternative way of creating the font is to declare an instance of the LOGFONT structure as a member of the dialog class and then initialize all the attributes before calling the SetMyFont function. In the SetMyFont function, you modify it as shown in Listing 7.10.
LISTING 7.10. THE MODIFIED SetMyFont FUNCTION.
1: void CDay7Dlg::SetMyFont()
2:
Q How can I limit the fonts in my list to just the TrueType fonts?
A You can check the nFontType argument to your callback function to determine the font type. For instance, if you want to include only TrueType fonts in your list of fonts, you modify your callback function to mask the nFontType argument with the TRUETYPE_FONTTYPE constant and check to see if the resulting value equals the TRUETYPE_FONTTYPE value, as in the following:
int CALLBACK EnumFontFamProc(LPENUMLOGFONT lpelf,
LPNEWTEXTMETRIC lpntm, DWORD nFontType, long lParam)
// Return 1 to continue font enumeration
return 1;
Workshop
The Workshop provides quiz questions to help you solidify your understanding of the material covered and exercises to provide you with experience in using what you've learned. The answers to the quiz questions and exercises are provided in Appendix B, 'Answers.'
Quiz
1. How can you specify that the text is to be underlined?
2. How can you print your text upside down?
3. How many times is the EnumFontFamProc callback function called by the operating system?
Exercises
1. Add a check box to switch between using the entered text to display the font and using the font name to display the font, as in Figure 7.4.
2. Add a check box to display the font sample in italics, as in Figure 7.5.
FIGURE 7.4. Displaying the selected font with the font name.
FIGURE 7.5. Displaying the selected font in italics.
In Review
Well, you've made it through the first week. By this point, you've gotten a good taste for what's possible when building applications with Visual C++. Now it's time to look back over what's been covered and what you should have learned up to this point.
What you might want to do at this point, to cement your understanding of how you can use these elements in your own applications, is to try designing and building a couple of simple applications of your own. You can use a variety of controls and add some additional dialogs, just so you can make sure that you do understand and are comfortable with these topics. In fact, you might want to try out all the topics that I've covered up to this point in small applications of your own design. That's the true test of your understanding of how the concepts work. You might also want to dive into the MFC documentation to learn a little about some of the more advanced functionality that I haven't covered to see if you can figure out how you can use and incorporate it into your applications.
One of the most important things that you should understand at this point is how you can use controls and dialog windows in your applications to get and display information to the user. This is an important part of any Windows application because just about every application interacts with the user in some way. You should be able to place any of the standard controls on a dialog in your application and be able to incorporate them into your application without any problem. Likewise, you should be comfortable with using the standard message box and dialog windows provided to your application by the Windows operating system. You should also be able to create and incorporate your own custom dialog windows into any application you might want to build. If you don't feel comfortable with any of these topics, you might want to go back and review Day 2 to get a better understanding of how you can use controls and Day 5 to understand how you can incorporate standard and custom dialog windows into your applications.
Another key skill that you will be using in the majority of your applications is the ability to build and incorporate menus into your applications. You need to have a firm understanding of how to design a good menu, how to make sure that there are no conflicting mnemonics, and how you can attach application functionality to the menu selections. At this point, you should be able to create your own customized menus, with entries for each of the various functions that your application performs, and integrate it with your application with no problems. If you aren't 100% comfortable with this topic, you might want to go back and study Day 6 a little more.
You will find that there are various situations in which you need to have some means of triggering actions on a regular basis or in which you need to keep track of how long some process has been running. For both of these situations, as well as numerous others, you'll often find yourself turning to the use of timers in your application. If you are even slightly foggy on how you can integrate timers into your applications, you will definitely want to go back and review Day 4.
Understanding how you can use text and fonts in your applications will allow you to build more flexibility into the appearance of your applications--to give your users the ability to customize the appearance as they want. You will be able to examine the available fonts on the computer on which your application is running and, if a font that you want to use in your application isn't available, choose another font that is close to use instead. If you still have any questions on how the font infrastructure in Windows works and how you can use it in your applications, you'll want to go back and review Day 7 once more.
Depending on the nature of your application, being able to capture and track mouse and keyboard actions by the user can be very important. If you are building a drawing application, this is crucial information. If you are building an application that needs to include drag-and-drop capabilities, this is important once again. There are any number of situations in which you'll want to include this functionality into your applications. By this point, you should understand how you can capture the various mouse events and determine which mouse buttons are involved in the event. You should also be able to capture keyboard events in situations where the keyboard input isn't captured by any controls that are on the window. If you don't feel like you have a complete grasp of this, you should take another look at Day 3.
Finally, you should be familiar with the Visual C++ development environment, the Developer Studio. You should have a good understanding of what each area of the environment is for and how you can use the various tools and utilities in building your applications. You should be comfortable with using the workspace pane to navigate around your application project, locating and bringing into the various editors and designers any part of your application. You should be comfortable with locating and redesigning the icon that will be displayed to represent your application and with finding any member functions or variables in any of your application's classes.
By now you should be getting fairly comfortable working with Visual C++. If you feel like you understand all the topics that I've covered so far, you are ready to continue forward, learning more about the various things that you can do, and functionality that you can build, using Visual C++ as your programming tool. With that said, it's on to the second week
Politica de confidentialitate | Termeni si conditii de utilizare |
Vizualizari: 801
Importanta:
Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved