Introduction
The Visual Component Framework (VCF) provides help support by using the existing platform Help APIs to do so. Both OS X and Windows use HTML to store help content, and provide roughly similar APIs for displaying it and navigating to various sub sections of the help content.
This article will primarily focus on help integration for Windows, but the basic steps (with the exception of the specific content creation steps) are the same on OS X. On Windows, the VCF uses the HTML Help API, and requires that the help content be processed into compiled help files (.chm). In addition, the article will also delve a bit deeper into how the framework actually processes the raw Windows messages and decides how to make calls to the HTML Help API functions.
We'll deal with several different types of help, from displaying the help index and/or table of contents page, to context sensitive help, and finally, with what I'll refer to as "What's This" help or popup help.
Your basic help will consist of one or more pages, a main page, a table of contents, and hopefully an index. When displayed, the content will show up in Windows standard HTML Help Viewer that comes with any installation of Windows 98 (or better) or Windows 2000 (or better). If for some reason, you don't have HTML Help on your system, you can install it here: HTML Help Downloads.
Using the framework, the application is able to bring up help that shows either the main page, its table of contents, or a specific section in the help "book". Help is triggered either programmatically by the developer, or in response to certain events triggered by the user, such as pressing the F1 key.
Context Sensitive Help
Context sensitive help is help that is shown to the user based on what the user is currently doing in the application. This may be related to where the user's mouse cursor is. For example, a drawing package might show help on editing rectangular shapes if the user's mouse cursor is over a rectangle when they hit the F1 key, or bring up help on edit curves if a Bezier curve shape is selected. The help that is shown is still displayed in the HTML Help viewer, but it will show a specific section in the help book.
"What's This" Help
"What's This" help is a little popup window that is displayed when the user clicks on a specific UI control. Dialogs that have the little "?" button in the upper right caption bar will trigger this event when the user next clicks on some control. The framework captures this notification, and will then check the active control and use it's What's This help string to display the text for the popup help window.
Creating the Help Content
First, you need to create your help content. For the basic content, you can use any editor you like to produce your help files with. They do need to be in HTML format, so if you're using something like Microsoft Word to edit the content, make sure to export it as HTML.
Once you have your core content, you'll need to create an HTML Help project file. A simple way to do this is to use Microsoft's HTML Help Workshop. If you don't like HTML Help Workshop, there are other editors out there (HTML Help Workshop is, admittedly, not a very user friendly application, and it's buggy and prone to crashing). You should end up with a file that ends in ".hhp". This is the file that the HTML Help Compiler will use to produce the final .chm file at the end of all this.
HTML Help Workshop with project open:
Next, you should make some sort of Table of Contents. The HTML Help Workshop can help with this. Once done, you should populate it with various entries, keeping in mind that some of these entries will be used by the context sensitive help to locate the proper section in the Help Viewer. An entry can use the "#" character to denote a name reference in the URL, like "FoobarGadgets.html#common-gadgets". The "common-gadgets" is used by the framework to form the complete URL to pass to the HTML Help system.
HTML Help Workshop Table of Contents:
The table of contents file ends with a ".hhc" extension, and is simply a plain HTML file that defines the outline using <UL>
, <LI>
, and <OBJECT>
tags. Something like this:
<HTML>
<HEAD>
</HEAD><BODY>
<OBJECT type="text/site properties">
<param name="ImageType" value="Folder">
</OBJECT>
<UL>
<LI> <OBJECT type="text/sitemap">
<param name="Name" value="Intro">
<param name="Local" value="index.html">
</OBJECT>
</UL>
</BODY></HTML>
The "Name
" and "Local
" params are used to name the display item and the content it references.
In addition to a table of contents, you'll probably want to add an index. As with the table of contents, the HTML Help Workshop can create this for you, and you can add entries as you see fit. Like the table of contents, the index is just an HTML file that ends with the ".hhk" extension, and is made up of <UL>
, <LI>
, and <OBJECT>
tags. The HTML Help Workshop can handle editing this, or you can edit by hand.
HTML Help Workshop Index:
Once you have all this, you need to compile the help so that you have a final compiled HTML Help file which should end with ".chm". The final step is determining where the .chm file will go. The framework attempts to look up the help file using several different approaches, some of which you can customize. If you do nothing at all, then the framework expects the content to be located in a directory named "Help" at the same level as the executable. For example:
[app directory]/
FooBar.exe
Help/
FooBar.chm
The default name of the actual help file is the name of the application (not necessarily the same as the actual executable), or it may be customized by the developer. On Windows, the ".chm" extension is appended to the final help file name.
Searching for Help
When the the developer requests for help to be displayed, the framework has to load the actual help file. It does this in several steps, some of which can be customized to provide specialized locations for where the help is located. The first step the framework makes is to check if an Application
instance exists, and if it does, to call the Application::getHelpInfo()
function which takes two string references as parameters. The first string is the help book name, and the second string is the help directory. These will be combined later into one for the final name of the help file. If either of these strings are empty, then the framework attempts to load the application's ProgramInfo
resource and extract the help book and help directory from that. If this resource is not available, or if the strings are still empty, then the help book is assigned either the application name (if the application instance is non-NULL
) or the base name of the executable. If the help directory is empty, it's assigned the default name of "Help".
So, your options as a developer who wants to customize the help lookup are:
- Override the
Application::getHelpInfo()
function and return valid strings for your application that point to where your help is.
- Store this information in your
ProgramInfo
resource. Since this is not something that's supported by Window's VS_VERSION_INFO
resource structure, you'll need to use the Info.xml (or Info.plist) files and supply one with your application. An example might look something like this:
="1.0"="UTF-8"
<plist >
<dict >
<key >
Executable
</key>
<string >
FooBar.exe
</string>
//rest omitted for space...
<key >
HelpName
</key>
<string >
MyHelp
</string>
<key >
HelpDirectory
</key>
<string >
Help Books
</string>
</dict>
</plist>
This would cause the help to be loaded from the executable's directory plus "Help Books/HelpName.chm".
Displaying Help
You can display help in several different ways. The easiest way is to display either the index or table of contents. To display the index, you just need to call the UIToolkit::displayHelpIndex()
. To display the table of contents, call UIToolkit::displayHelpContents()
. The framework will attempt to figure out the correct help file as described in the "Searching for Help" section. You can customize this as you see fit.
The next way to display help is to specify a particular help section to display. The section indicates an HTML file to load from the help directory or resource (in our case the .chm file). For HTML help, this means the framework will append your section to the CHM filename, something like this: "c:\Program Files\MyApp\Help\MyApp.chm::/foo.html". To display a section, call the UIToolkit::displayHelpSection()
function and pass in the help section (and optionally, the help book and directory).
Displaying Context Sensitive Help
Another way to display help is using context sensitive help. Context sensitive help is help that is specific to a control in the UI. The framework detects when a help event is requested (such as the user pressing the F1 key) and then fires several possible events, allowing the "What's This" text of the control to be used, or, if that doesn't exist, allowing other context appropriate help to be displayed. This can involve opening the help to a specific section, based on the context of which control the mouse is over, and where the mouse is within the bounds of the control (if useful).
To display context sensitive help, you add an event handler to the HelpRequested
delegate of the control you wish to monitor. This control may have other child controls that may be relevant in deciding what help to display. When your event handler is called, you will be sent a HelpEvent
instance that you then assign a help section to. You can also assign the help book and help directory as well. The framework then uses this information to display the actual help.
The information the frameworks needs are the help book name, the help book directory, and the help section. If these are blank, the framework uses specific heuristics to determine them. The last piece is the section name, in other words, the specific section in the help "book" that you want to go to. This is generally of the form "filename.html#section". This assumes that your content has been created with valid name sections in it.
The mechanics of how this all occurs allows a fair amount of flexibility in deterring which control responds to the help event and how the help is displayed. The sequence of events is started by the framework being notified of an OS help event; on Windows, this means a WM_HELP message
is received. The UIToolkit
's UIToolkit::displayContextHelpForControl()
is then called. This, in turn, does the following:
- It calls the platform specific implementation of
displayContextHelpForControl()
.
- A
WhatsThisHelpEvent
is created using the the control's "What's This" help string as the event data, and then fired on the control's ControlHelpRequested
delegate.
- If the
WhatsThisHelpEvent
's help string is not empty, a call is made to the HtmlHelp()
API function, using the HH_DISPLAY_TEXT_POPUP
command. This will cause the little popup "What's This" help to appear. At this point, the UIToolkit::displayContextHelpForControl()
is complete, and the whole process is done.
- If the
WhatsThisHelpEvent
's help string is empty, then the UIToolkit
creates a HelpEvent
and fires the event on the control's HelpRequested
delegate. This event has fields that may be assigned the section name, help book, and the help directory.
- If the section name string is empty, then the
UIToolkit
walks up the parent-child hierarchy (starting with the control's parent) until it hits a NULL
parent, or finally has the help section field of the HelpEvent
assigned a non-empty string.
- If a non-empty help section is found, the help is then displayed by calling the
UIToolkit::displayHelpSection()
function.
In other words, the first step is to fire an event that allows the program to display a "What's This" help. If no action is taken, then the framework attempts to display context sensitive help. This starts with the control that's active when the WM_HELP
message is received by the framework, and percolates up to the first parent control that responds to the HelpEvent
.
Displaying "What's This" Help
The final type of help is the "What's This" help (some documents refer to this as context sensitive help). This is the help you typically see in dialogs that pop up a little tooltip with some sort of information about what the specific control does. This is generally triggered by clicking on the dialog's question mark button in the caption bar, or, on some dialogs, when you right click on the control and click on the "What's This?" context menu item. Either way, this generates a Windows help message that the framework responds to.
What's This help:
What's This help using a context menu:
To enable support for this in your application, you don't need to call anything explicitly, the framework will handle the window message and then allow you to respond to this in several ways. The simplest way is to make sure you have set the the What's This help string on all the appropriate controls by calling Control::setWhatsThisHelpString()
. Another way is to add an event handler to the control's ControlHelpRequested
delegate. The framework will send a WhatsThisHelpEvent
instance to the delegate when an appropriate help message is received from Windows. You can then fill in the event's helpString
value to something appropriate to display. The framework uses this string to make the call to the HtmlHelp()
function using the HH_DISPLAY_TEXT_POPUP
command.
If you've integrated popup help into your app before using HtmlHelp
, you'll note that this is slightly different than how you might normally do things. Traditionally, you would create a popups.txt file and add specific entries for each Help ID and the string value to display, then another file that maps the Help ID to the control's help ID that the dialog editor assigned. It's rather cumbersome and easy to break. With the VCF, you can simply assign the help string of the control's What's This help string. Then, if you want to support localization, simply provide additional string translations in your resources folder.
Putting It All Together
The example included demonstrates the various ways in which help can be displayed and integrated into your application. Let's quickly look at displaying the Table of Contents and Index. We're going to use the default help directory, so we make sure to create a .chm file in a directory named "Help", at the same level as our Help.exe is at.
[app directory]
Help.exe
Help/
Help App.chm
We'll name our CHM file "Help App.chm" just to show how to customize the help "book" name. To make sure the framework can find this, we'll need to make sure our application is named appropriately:
class HelpApplication : public Application {
public:
virtual bool initRunningApplication(){
bool result = Application::initRunningApplication();
setName( "Help App" );
return result;
}
};
This is the easiest way to make sure that the framework will identify our help file correctly.
Once the UI is built, let's add some event handlers to handle displaying the Table of Contents and Index:
class HelpWindow : public Window {
public:
void showContents( Event* e ) {
UIToolkit::displayHelpContents();
}
void showIndex( Event* e ) {
UIToolkit::displayHelpIndex();
}
};
When the showContents()
event handler function is called, we just call UIToolkit::displayHelpContents()
, using the default empty arguments. The framework will take care of the rest and display the help. The same for the showIndex()
function.
Results of calling HelpWindow::showContents():
Results of calling HelpWindow::showIndex():
To demonstrate displaying the "What's This?" help, let's display a dialog that has the appropriate entries set for the controls on the dialog.
class MyDialog : public Dialog {
public:
MyDialog() {
TextControl* edit1 = new TextControl();
edit1->setWhatThisHelpString( "Type in your name here." );
CheckBoxControl* checkBox = new CheckBoxControl();
checkBox->setCaption( "Yes I am!" );
checkBox->setWhatThisHelpString( "This indicates whether "
"or not you're a Mergatroid. "
"\nThink carefully before answering." );
CommandButton* okBtn = new CommandButton();
okBtn->setWhatThisHelpString( "Click this to accept "
"the changes and close the dialog!" );
CommandButton* cancelBtn = new CommandButton();
cancelBtn->setWhatThisHelpString( "Click this close "
"the dialog without relaying any information "
"to the SSDC*!\n\n\n\t*Super Secret Decoder Club" );
}
};
When the dialog is displayed, and the user clicks on the dialog's question mark button, the text we set is shown in a popup window.
Dialog displayed:
Control's "What's This" help displayed:
To display our context sensitive help, we've added an event handler to the window's HelpRequested
delegate:
class HelpWindow : public Window {
public:
HelpWindow() {
setCaption( "Help" );
HelpRequested +=
new GenericEventHandler<HelpWindow>(this,
&HelpWindow::onContextHelp,
"HelpWindow::onContextHelp" );
}
};
Our event handler, HelpWindow::onContextHelp()
, looks like so:
class HelpWindow : public Window {
public:
void onContextHelp( Event* e ) {
HelpEvent* he = (HelpEvent*)e;
Point pt = Desktop::getDesktop()->getCurrentMousePosition();
Rect r = l1->getBounds();
l1->getParent()->translateToScreenCoords(&r);
if ( r.containsPt( &pt ) ) {
he->helpSection = "index.html#Complex";
return;
}
r = l2->getBounds();
l2->getParent()->translateToScreenCoords(&r);
if ( r.containsPt( &pt ) ) {
he->helpSection = "index.html#figure1";
return;
}
r = l3->getBounds();
l3->getParent()->translateToScreenCoords(&r);
if ( r.containsPt( &pt ) ) {
he->helpSection = "index.html#fig1expl";
return;
}
r = l4->getBounds();
l4->getParent()->translateToScreenCoords(&r);
if ( r.containsPt( &pt ) ) {
he->helpSection = "index.html#yada";
return;
}
}
};
Our UI is made up of, among other things, four labels, which we store in four different member variables. When our HelpWindow::onContextHelp()
function gets called, we determine the current mouse position, and then figure out which of the four labels the mouse is over. If we find a match, we then assign the appropriate string value to the HelpEvent
's helpSection
field. This is then used by the framework to display the help.
Context sensitive help for label 2 displayed:
Notes on Building the Example
You'll need to have the most recent version of the VCF installed (at least 0-9-0 or better), and you'll need to make sure you have built the static libraries for the VCF (as opposed to the DLL version). The examples are configured to link to the VCF statically. For more documentation on building the VCF, see: Building the VCF, at the VCF online documentation.
In addition to the VCF, you will need a recent version of the HTML Help compiler to build this example. As far as I know, the only legitimate way to get that is through the HTML Help downloads section at Microsoft and downloading and installing HTML Help Workshop.
Rename the BuildHelp.bat.txt to BuildHelp.bat and build_bcc.bat.txt to build_bcc.bat, the files needed to be "mangled" to get through my ISP's email filter.
Conclusion
We've covered the basics of how to work with, create, and integrate help into your application using the VCF. Hopefully, this will prove useful to you as you go on to write award winning, next generation applications using the VCF!
Questions about the framework are welcome, and you can post them either here or in our forums. If you have suggestions on how to make any of this better, we'd love to hear them!