Too many programmers neglect Help entirely. Even those who add Help to an application tend to leave it to the end of a project, and when the inevitable time squeeze comes, guess what? There's no time to write the Help text or make the software adjustments that arrange for that text to be displayed when the user requests Help. One of the reasons people do this is because they believe implementing Help is really hard. But with Visual C++, it's a lot easier than it could be. Visual C++ even writes some of your Help text for you! This chapter is going to add Help after the fact to the ShowString application built into Chapter 17, "Building Menus and Dialogs."
There are a variety of ways of characterizing Help. This section presents four different questions you might ask about Help:
None of these questions has a single answer. There are at least nine different ways for a user to invoke Help, three standard Help appearances, and three different programming tasks you must implement in order to display Help. These different ways of looking at Help can help you understand why the implementation has a number of different techniques, which can be confusing at first.
The first way of characterizing Help is to ask, "How does the user bring it up?" There are a number of ways to bring up Help:
For the first three actions in this list, the user does one thing (chooses a menu item, presses f1, or clicks a button) and Help appears immediately. For the next five actions there are two steps: typically one click to get into "Help mode" (more formally called "What's This?" mode) and another to indicate what Help is required. Users generally divide Help into single-step Help and two-step Help, accordingly.
NOTE |
You will get confused if you try to use Developer Studio itself to understand Help in general. Much of the information is presented by InfoViewer, though there are some circumstances under which more traditional Help appears. Use simple utilities and accessories that come with your operating system, or use your operating system itself, to follow along. If you have old versions of software like Word or Excel, they probably don't follow the Windows 95 guidelines for Help either, because these are quite different than the old Help guidelines. |
The second way of characterizing Help is to ask, "How does it look?" There are a number of different-looking ways of showing Help:
A third way of characterizing Help, is according to the user's reasons for invoking it. Microsoft categorizes Help in this way and lists these kinds of Help:
These describe the content of the material presented to the user. While these content descriptions are important to a Help designer and writer, they are not very useful from a programming point of view.
The final way of characterizing Help, and perhaps the most important to a developer, is by examining the code behind the scenes. There are three Windows messages that are sent when the user invokes Help in any of these ways:
When the user chooses a Help item from a menu, or clicks the Help button on a dialog box, the system sends a WM_COMMAND message as always. To display the associated Help, you catch these messages and call the WinHelp system.
When the user right-clicks an element of your application, a WM_CONTEXTMENU message is sent. You catch the message and build a shortcut menu on the spot. Because in most cases you will want a shortcut menu with only one item on it, What's This? you can use a prebuilt menu with just that item, and delegate the display of that menu to the Help system. More on this later, in the "Programming for Context Help" section.
When the user brings up Help in any other way, the framework handles most of it. You do not catch the message that puts the application into What's This? mode, you do not change the cursor, and you do not deal with clicks while in that mode. You catch a WM_HELP message that identifies the control, dialog box, or menu for which Help is required, and you provide that Help. Whether the user pressed f1 or went into What's This? mode and clicked the item does not matter, and in fact you cannot tell from within your application.
The WM_HELP and WM_CONTEXTMENU messages are handled almost identically, so from the point of view of the developer, there are two kinds of help. We'll call these command help and context help. Each is discussed later in this chapter, in the "Programming for Command Help" and "Programming for Context Help" sections, but keep in mind that there is no relationship between this split (between command and context help) and the split between one-step and two-step Help that users think of.
As you might expect, a large number of files interact to make online Help work. The final product, which you deliver to your user, is the Help file, with the .hlp extension. It is built from component files. In the list that follows, appname refers to the name of your application's .exe file. If no name appears, there may be more than one file with a variety of names. The component files produced by AppWizard are as follows:
While being used, the Help system generates other files. When you uninstall your application be sure to look for and remove the following files, in addition to the .hlp file:
Help Topic IDs are the connection between your Help text and the Help system. Your program eventually directs the Help system to display a Help topic, using a name like HID_FILE_OPEN, and the system looks for this Help topic ID in the Help file, compiled from the .rtf files, including the .rtf file that contains your Help text for that Help topic ID. (This process is illustrated in Figure 21.4.) These topic IDs have to be defined twice-once for use by the Help system and once for use by your program. When the Help system is displaying a topic or the Help Topics dialog box, it takes over displaying other Help topics as the user requests them, with no work on your part.
When you build an MDI application (no database or OLE support) with AppWizard, and choose the Context Sensitive Help option, here's what you get:
With this solid foundation, the task of implementing Help for this application breaks down into three steps:
NOTE |
On large projects, the Help text is often written by a technical writer rather than a programmer. This will require careful coordination; for example, you will have to provide Topic IDs to the Help writer, and may have to explain some functions so that they can be described in Help. You will have to work closely throughout the project and respect each other's area of expertise. |
Developing 'Help is like developing your software. You shouldn't do it without a plan. And, strictly speaking, you shouldn't do it last. A famous experiment decades ago split a programming class into two groups. One group was required to hand in a completed user manual for a program before writing the program, the other to finish the program before writing the manual. The group that wrote the manual first produced better programs: it noticed design errors early, before they were carved in code, and it found the program much easier to write, as well.
If your application is of any size, the work involved in developing a Help system for it would fill a book. If you need further information on how to do this, consider the book Designing Windows 95 Help: A Guide to Creating Online Documents, by Mary Deaton and Cheryl Lockett Zubak, published by Que. In this section, there is only room for a few basic guidelines.
The result of this planning process is a list of Help topics and the primary way they will be reached. The topics you plan are likely to include:
While that may seem like a lot of work, remember that all the boilerplate resources have been documented already in the material provided by AppWizard. That includes menu items, common dialog boxes, and more.
After you have a complete list of material and the primary way each page is reached, think about links between pages (for example, the AppWizard-supplied help for File, Open mentions using File, New, and vice versa) and pop-up definitions for jargon and keywords.
In this section you will plan Help for ShowString, the application introduced in Chapter 17, "Building Menus and Dialogs." This simple application displays a string that the user can set. The string may be centered vertically or horizontally, and can be black, green, or red. There is a new menu (Tools,) with one item (Options) that brings up a dialog box on which the user can set all these options at once. The Help tasks you need to tackle include:
The remainder of this chapter tackles this list of tasks.
Command help is actually quite simple from a developer's point of view. (Of course, you probably still have to write the explanations, so don't get too relaxed.) As you've seen, AppWizard added the Help Topics menu item and the message map entries to catch it, and the MFC class CMDIChildFrame has the member function that will process it, so you have no work to do for that. But if you choose to add another menu item to your Help menu, you do so just like any other menu, using the Resource View. Then have your application class, CShowStringApp, catch the message. Say, for example, that ShowString deserves an item on the Help menu called Understanding Centering. Add this item to both menus, and let Developer Studio assign it the resource ID ID_HELP_UNDERSTANDINGCENTERING. Actually, this is one occasion where a slightly shorter resource ID wouldn't hurt, but this chapter will present it with the longer ID.
Use Class Wizard to arrange for CShowStringApp to catch this message, as discussed in Chapter 17, "Building Menus and Dialogs." (You may want to open the Help version of this project now and follow along, or make a copy of the ShowString you built in Chapter 17 and make these changes as you read.) The new function looks like this:
void CShowStringApp::OnHelpUnderstandingcentering() { WinHelp(HID_CENTERING); }
This single line of code fires up the Help system, passing it the Help topic ID HID_CENTERING. For this to compile, that Help topic ID has to be known to the compiler, so in showstring.h, add this line:
#define HID_CENTERING 0x01
The help topic IDs in the range 0x0000 to 0xFFFF are reserved for user-defined Help topics, so 0x01 is a fine choice. Now the C++ compiler is happy, but when this runs, the call to WinHelp() is not going to find the topic that explains centering. You need to add a help mapping entry. This should be done in a new file, named showstringx.hm (the x is for extra.) Choose File, New, Text File, and type in this line:
HID_CENTERING 0x01
Save the file as showstringx.hm in the hlp folder of the ShowString project. Next you need to edit the Help project file, Showstring.hpj. If you double-click this from a folder such as Windows 95 Explorer, it will be opened with the Help Compiler. In this case you actually want to edit it as text, so you should open it with Developer Studio. In Developer Studio's Project Workspace Window, click the FileView tab, then open Showstring.hpj by double-clicking it in the File View (and you wondered what the File View was good for) and add this line at the very bottom:
#include <ShowStringX.hm>
Now both the Help system and the compiler know about this new Help topic ID. When you write the Help text, don't forget to add a section that explains centering, and connect it to this Help topic ID.
NOTE |
Microsoft is now recommending that all context numbers have identifiers that start with the letters IDH; in the past the convention had been HIDD for dialogs, HIDR for resources, and so on. Unfortunately, the MAKEHELP.BAT generated by AppWizard still creates context numbers using the old convention, so you'll have to choose between using the old convention or changing MAKEHELP.BAT. For clarity, the sample code presented in this chapter all uses the old convention; MAKEHELP.BAT has not been changed. |
The other common use of command help is to add a Help button to a dialog box that gives an overview of the dialog box. This used to be standard behavior but is now recommended only for large dialog boxes, especially those with complex interactions between the various controls. Simply use the steps you followed to add the menu item Help, Understanding Centering, but add a button rather than a menu item. Do not create a new .hm file; add the button's Help topic ID to ShowStringX.hm, which will continue to grow in the next section.
Your first task in arranging for context help is to get a Question button onto the Options dialog box, since AppWizard already added one to the toolbar. Open the Options dialog box by double-clicking it in the Resource View, then choose Edit, Properties. Click the Extended Styles tab, then check the Context Help checkbox, as shown in Figure 21.5.
As mentioned earlier, there are two messages sent in context help: WM_HELP when a user clicks something while in What's This? mode, and WM_CONTEXTMENU when a user right-clicks something. You need to arrange for your dialog class, COptionsDialog, to catch these messages. You do so by adding entries outside the special ClassWizard comments. The message map in OptionsDialog.h should look like this:
// Generated message map functions //{{AFX_MSG(COptionsDialog) // NOTE: the ClassWizard will add member functions here //}}AFX_MSG afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo); afx_msg void OnContextMenu(CWnd* pWnd, CPoint point); DECLARE_MESSAGE_MAP()
The message map in OptionsDialog.cpp should look like this:
BEGIN_MESSAGE_MAP(COptionsDialog, CDialog) //{{AFX_MSG_MAP(COptionsDialog) // NOTE: the ClassWizard will add message map macros here //}}AFX_MSG_MAP ON_WM_HELPINFO() ON_WM_CONTEXTMENU() END_MESSAGE_MAP()
These macros arrange for WM_HELP to be caught by OnHelpInfo(), and for WM_CONTEXTMENU to be caught by OnContextMenu(). The next step is to write those functions. They both need to use a table to connect resource IDs to Help topic IDs. Add these lines at the beginning of OptionsDialog.cpp:
static DWORD aHelpIDs[] = { IDC_OPTIONS_STRING, HIDD_OPTIONS_STRING, IDC_OPTIONS_BLACK, HIDD_OPTIONS_BLACK, IDC_OPTIONS_RED, HIDD_OPTIONS_RED, IDC_OPTIONS_GREEN, HIDD_OPTIONS_GREEN, IDC_OPTIONS_HORIZCENTER, HIDD_OPTIONS_HORIZCENTER, IDC_OPTIONS_VERTCENTER, HIDD_OPTIONS_VERTCENTER, IDOK, HIDD_OPTIONS_OK, IDCANCEL, HIDD_OPTIONS_CANCEL, 0, 0 };
The Help system uses this array (you pass the address to the WinHelp() function) to connect resource IDs and Help topic IDs. The compiler, however, has never heard of HIDD_OPTIONS_STRING, so add these lines to OptionsDialog.h:
#define HIDD_OPTIONS_STRING 2 #define HIDD_OPTIONS_BLACK 3 #define HIDD_OPTIONS_RED 4 #define HIDD_OPTIONS_GREEN 5 #define HIDD_OPTIONS_HORIZCENTER 6 #define HIDD_OPTIONS_VERTCENTER 7 #define HIDD_OPTIONS_OK 8 #define HIDD_OPTIONS_CANCEL 9
The numbers are chosen arbitrarily. Now, as before, the compiler is happy, since all these constants are defined, but the Help system doesn't know what's going on, because these topics are not in the help mapping file yet. So, add these lines to ShowStringX.hm:
HIDD_OPTIONS_STRING 0x02 HIDD_OPTIONS_BLACK 0x03 HIDD_OPTIONS_RED 0x04 HIDD_OPTIONS_GREEN 0x05 HIDD_OPTIONS_HORIZCENTER 0x06 HIDD_OPTIONS_VERTCENTER 0x07 HIDD_OPTIONS_OK 0x08 HIDD_OPTIONS_CANCEL 0x09
Be sure to use the same numbers as in the #define statements in OptionsDialog.h. The stage is set; all that remains is to write the functions. Here's what OnHelpInfo() looks like:
BOOL COptionsDialog::OnHelpInfo(HELPINFO *lpHelpInfo) { if (lpHelpInfo->iContextType == HELPINFO_WINDOW) // must be for a control { // have to call SDK WinHelp not CWinApp::WinHelp // because CWinApp::WinHelp doesn't take a // handle as a parameter. ::WinHelp((HWND)lpHelpInfo->hItemHandle, AfxGetApp()->m_pszHelpFilePath, HELP_WM_HELP, (DWORD)aHelpIDs); } return TRUE; }
This function just calls the SDK WinHelp function and passes the handle to the control, the path to the Help file, the command HELP_WM_HELP to request a context-sensitive pop-up Help topic, and the table of resource IDs and Help topic IDs built earlier. There's no other work for your function to do after kicking WinHelp() into action.
TIP |
If you've never seen the :: scope resolution operator used without a class name before it, it means call the function that is not in any class, and in Windows programming that means the SDK function. |
NOTE |
The third parameter of this call to WinHelp directs the Help system to put up a certain style of Help window. HELP_WM_HELP gets you a pop-up menu, as does HELP_WM_CONTEXTMENU. HELP_CONTEXT gets an ordinary Help window, which can be resized and moved, and allows Help navigation. HELP_FINDER brings up the Help Topics dialog box. HELP_CONTENTS and HELP_INDEX are obsolete and should be replaced with HELP_FINDER if you maintain code that uses them. |
OnContextMenu() is even simpler:
void COptionsDialog::OnContextMenu(CWnd *pWnd, CPoint /*point*/) { ::WinHelp((HWND)*pWnd, AfxGetApp()->m_pszHelpFilePath, HELP_CONTEXTMENU, (DWORD)aHelpIDs); }
This function doesn't need to check that the right-click is on a control as OnHelpInfo() did, so it just calls the SDK WinHelp. WinHelp() takes care of displaying the shortcut menu with only a What's This item, and then displays Help when that item is chosen.
To check your typing, build the project by choosing Build, Build, then compile the Help file by giving focus to showstring.hpj and choosing Build, Compile. (You can also right-click on Showstring.hpj in the File View of the Project Workspace Window and choose Compile from the shortcut menu.) There's not much point in testing it though; the AppWizard stuff is sure to work, and without Help content connected to those topics, none of the code you just added will succeed in displaying content.
You write Help text in an RTF file, using special formatting codes, which mean something rather different than they usually do. The traditional way to do this was in Word, but a large crop of Help authoring tools have sprung up that are far easier to use than Word. Rather than teach you yet another tool, this section presents instructions for writing Help text in Word. However, do keep in mind that there are easier ways, and on a project of a decent size you easily save the time and money you invested in choosing a Help authoring tool. There is an entire chapter in Designing Windows 95 Help on choosing an authoring tool.
Figure 21.6 shows afxcore.rtf open in Word. Choose View, Footnotes to display the footnotes across the bottom of the screen-they are vital. This is how the text connects to the Help topic IDs. Choose Tools, Options, select the View tab, and make sure the hidden text checkbox is selected. This is how links between topics are entered. The topics are separated by page breaks.
There are eight kinds of footnotes and each has a different meaning. Only the first three footnote types in the following list are in general use:
The double-underlined text, followed by hidden text, identifies a jump to another Help topic. If a user clicks to follow the link, this Help topic leaves the screen. If the text before the hidden text was single-underlined, following the link brings up a pop-up over this Help topic, perfect for definitions and notes. (You may also see Help text files in which strikethrough text is used; this is exactly the same as double-underlined, a jump to another topic.) In all three cases, the hidden text is the topic ID of the material to be jumped to or popped up.
Figure 21.7 shows how the File New Help material appears from within ShowString. To display it yourself, run Showstring by choosing Build, Execute from within Developer Studio, then choose Help, Help topics in ShowString. Open the menus book, double-click the File menu topic, and click New.
Figure 21.7 : ShowString displays the boilerplate help generated by AppWizard.
With the programming out of the way, it's time to tackle the list of Help To Dos for ShowString from earlier in this chapter. These instructions assume you are using Word.
To change the placeholder strings left behind by AppWizard in the boilerplate help files, open afxcore.rtf in Word. (It's in the hlp folder of the ShowString project folder.) Then follow these steps:
Open afxprint.rtf and repeat these steps.
Switch back to afxcore.rtf, and look through the text for << characters (use Edit, Find if you wish, and remember that Shift+f4 is the shortcut to repeat your previous Find.) These identify places where you must make a change or a decision. For ShowString, the changes in afxcore.rtf are:
That completes the extensive changes required to the boilerplate afxcore.rtf file generated by AppWizard. In the other boilerplate file, afxprint.rtf, simply scroll to the bottom and remove the Page Setup topic.
Would you like to test all this work? Save afxcore.rtf and afxprint.rtf within Word. Switch to Developer Studio and choose Build, Build to bring the project up to date. Then open showstring.hpj and choose Build, Compile. This pulls all the .rtf files together into showstring.hlp. Choose Build, Execute to run Showstring, and choose Help, Help Topics from the ShowString menus. As you can see in Figure 21.8, the Window menu topic is now substantially shorter. You can check that your other changes have been made as well.
When you are adding new topics, you don't add new topics to the boilerplate files that were provided. Those files should stay untouched unless you want to change the description of File, Open or other boilerplate topics. Instead, create a new file by choosing File New in Word and saving it in the hlp folder of the ShowString project folder as ShowString.rtf. (Make sure to change the Save File As Type list box selection to Rich Text Format.) If this was a large project, you could divide it up into several .rtf files, but one will suffice for ShowString. In Developer Studio, open showstring.hpj by double-clicking it in the FileView tab, and find the section headed [FILES]. Add this line at the end of that section:
showstring.rtf
The Tools Menu Back in Word, switch to afxcore.rtf and copy the topic for the File menu into the Clipboard, then switch back to showstring.rtf and paste it in. (Don't forget to include the page break after the topic in the selection when you copy.) Choose View, Footnotes to display the footnotes and Tools, Options, View tab, Hidden Text to display the hidden text. Now you are going to edit the copied File topic to make it the Tools topic. Change the footnotes first. They are as follows:
In the topic, change File to Tools on the first two lines, and delete all the rows of the table but one. Change the underlined text of that row to Options, the hidden text immediately following to HID_TOOLS_OPTIONS, and the right column of that row to Changes string, color, and centering. Figure 21.9 shows the way Showstring.rtf looks in Word after these changes.
Figure 21.9 : Change the Showstring.rtf file to explain the new menu item.
TIP |
If you can't remember the Help topic IDs your project is using, check your .hm files. The ones added by Developer Studio, such as HID_TOOLS_OPTIONS for the menu item with resource ID ID_TOOLS_OPTIONS, are in Showstring.hm, while ShowStringx.hm contains the Help topic IDs added by hand for context help. |
The Tools, Options Menu Item Switch back to afxcore, copy the File New topic, and paste it into showstring.rtf as before. The topic and its footnotes are copied together. Watch carefully to be sure you are working with the footnotes for the Tools Options topic and not the ones for the Tools menu. Follow these steps:
Use this command to change the appearance of the ShowString display with the Options dialog. The string being displayed, color of the text, and vertical and horizontal centering are all controlled from this dialog.
If you want to test this, too, save the files in Word, compile the Help project, run ShowString, and choose Tools. Highlight the Options item by moving the highlight with the cursor keys, but do not click Options to select it; press f1 instead. Figure 21.10 shows the Help window that is displayed.
Each Control on the Options Dialog Copy the File New topic into showstring.rtf again and cut it down drastically. To do this, follow these steps:
Copy this block into the Clipboard and paste it in seven more times, so that you have a skeleton for each control on the dialog box. Remember to copy the page break, too. Then edit each skeleton to document the following topic IDs:
Change the topic ID and add a sentence or two of text. Be consistent. The samples included with this chapter are all a single sentence that starts with an imperative verb like Click or Select and ends with a period(.). If you would rather choose a different style for your pop-up boxes, use the same style for all of them. It confuses the user if pop-up boxes are inconsistent, and tends to make them believe your coding is sloppy too.
Understanding Centering In showstring.rtf, paste in another copy of the File New topic. Make the following changes:
Test this change in the usual way, and when you choose Help Understanding Centering from the ShowString menus, you should see something like Figure 21.11. Try following the links; you can use the Back button to return to the centering topic.
Figure 21.11 : Display a teaching Help topic by choosing it from the Help menu.
AppWizard already provided a How To Modify Text topic at the bottom of afxcore.rtf that needs to be edited to explain how ShowString works. It is displayed when the user selects the view area for context help. Replace the text with a much shorter explanation that tells the user to choose Tools, Options. To add a link to that topic (short though it is),type HID_TOOLS_OPTIONS immediately after the word Options in the Help topic. While you're at it, type menu_tools immediately after the word Tools. Select the word Options and press CTRL+SHIFT+D to double-underline it, then do the same for Tools. Select HID_TOOLS_OPTIONS and press CTRL+SHIFT+H to hide it, then do the same for menu_tools.
TIP |
There cannot be any spaces between the double-underlined text and the hidden text, or at the end of the hidden text. Word can give you some trouble about this, because the smart cut and paste feature that works so nicely with words can insert extra spaces where you didn't want them, or make it impossible to select only half a word. You can turn the feature off in Word by choosing Tools Options, the Edit tab, and by unselecting the Automatic Word Selection and Use Smart Cut and Paste boxes. |
Ready to test again? Save the files in Word, compile the Help project file, execute ShowString, choose Tools, Options, click the Question button, and then click a control. Figure 21.12 shows the context help for the String edit box.
This tiny little application is almost entirely documented now. You need to add the Tools menu and Understanding Centering to the Contents, and check the index. The easiest way to tackle the Contents is with Help Workshop. Close all the Help-related files that are open in Developer Studio and Word, and bring up Help Workshop (it's in the Developer Studio folder). Open showstring.cnt by choosing File Open and working your way through the Open dialog box. This is the Contents file for ShowString.
In the first open book, click the View item and then click the Add Below button. (Alternatively, click the Window item and then the Add Above button.) The Edit Contents Tab Entry dialog box, shown in Figure 21.13, appears. Fill it in as shown; by leaving the last two entries blank, the default Help File and Window Type are used. Click OK.
Click the placeholder book, and click Add Above again. When the Edit Contents Tab Entry dialog box appears, select the Heading radio button from the list across the top. As shown in Figure 21.14, only the title can be changed here. Do not use Understanding Centering, since that is the title of the only topic under this heading. Click OK.
Add a topic below the new heading for Understanding Centering, whose ID is HID_CENTERING, and remove the placeholder heading and topic. Save your changes, close Help Workshop, compile showstring.hpj in Developer Studio again, and test your Help. Choose Help Help Topics and you should see something like Figure 21.15.
While you have the Help Topics dialog box open, click the Index tab. Figure 21.16 shows how the K footnotes you entered throughout this section have all been added to the index. If it looks a little sparse, you can always go to the .rtf files and add more keywords, remembering to separate them with semicolons.
Figure 21.16 : The index has been built from the K footnotes in the .rtf files.
This chapter has only scratched the surface of building a Help system. Although you have seen all of the types of Help, the different ways the user invokes it, and the behind-the-scenes code that brings it up on the screen, there is much more to cover. If you have a large system to document, you need a book just on Help, to give you a good perspective on designing and writing your Help content. Other parts of this book that might interest you include: