与 VCF 集成帮助






3.59/5 (11投票s)
2006 年 5 月 8 日
14分钟阅读

32482

225
一篇关于使用 Visual Component Framework (VCF) 处理、创建和集成帮助的文章。
引言
Visual Component Framework (VCF) 通过使用现有的平台帮助 API 来提供帮助支持。macOS 和 Windows 都使用 HTML 来存储帮助内容,并提供大致相似的 API 来显示它以及导航到帮助内容的各种子部分。
本文将主要关注 Windows 的帮助集成,但基本步骤(除了特定的内容创建步骤)在 macOS 上是相同的。在 Windows 上,VCF 使用 HTML Help API,并要求将帮助内容处理成已编译的帮助文件(.chm)。此外,本文还将更深入地探讨框架如何实际处理原始的 Windows 消息并决定如何调用 HTML Help API 函数。
我们将处理几种不同类型的帮助,从显示帮助索引和/或目录页,到上下文相关的帮助,最后,是我将称之为“是什么”帮助或弹出帮助。
您的基本帮助将由一个或多个页面、一个主页、一个目录以及(希望)一个索引组成。显示时,内容将显示在 Windows 标准 HTML Help Viewer 中,该查看器随任何 Windows 98(或更高版本)或 Windows 2000(或更高版本)的安装程序一起提供。如果出于某种原因,您的系统中没有 HTML Help,您可以在这里安装:HTML Help Downloads。
使用该框架,应用程序能够调出显示主页、目录或帮助“图书”中特定部分的帮助。帮助要么由开发人员通过编程方式触发,要么响应用户触发的某些事件,例如按 F1 键。
上下文相关的帮助
上下文相关的帮助是指根据用户在应用程序中当前正在执行的操作而显示的帮助。这可能与用户鼠标光标的位置有关。例如,绘图包可能会在用户将鼠标光标悬停在矩形上并按下 F1 键时显示有关编辑矩形形状的帮助,或者在选择了贝塞尔曲线形状时显示有关编辑曲线的帮助。显示的帮助仍然在 HTML Help 查看器中显示,但它会显示帮助图书中的特定部分。
“是什么”帮助
“是什么”帮助是一个小弹出窗口,当用户单击特定 UI 控件时显示。标题栏右上角带有“?”按钮的对话框将在用户下次单击某个控件时触发此事件。框架会捕获此通知,然后检查活动控件并使用其“是什么”帮助字符串来显示弹出帮助窗口的文本。
创建帮助内容
首先,您需要创建帮助内容。对于基本内容,您可以使用任何喜欢的编辑器来生成帮助文件。它们需要是 HTML 格式,因此如果您使用 Microsoft Word 之类的软件来编辑内容,请确保将其导出为 HTML。
有了核心内容后,您需要创建一个 HTML Help 项目文件。一种简单的方法是使用 Microsoft 的 HTML Help Workshop。如果您不喜欢 HTML Help Workshop,还有其他编辑器(不可否认,HTML Help Workshop 不是一个非常用户友好的应用程序,而且它有 bug 且容易崩溃)。您最终应该会得到一个以“.hhp”结尾的文件。这个文件就是 HTML Help 编译器用来生成最终的 .chm 文件的。
打开项目后的 HTML Help Workshop
接下来,您应该制作某种目录。HTML Help Workshop 可以帮助您完成此操作。完成后,您应该用各种条目填充它,请记住,其中一些条目将由上下文相关的帮助用于查找帮助查看器中的正确部分。条目可以使用“#”字符来表示 URL 中的名称引用,例如“FoobarGadgets.html#common-gadgets”。“common-gadgets”由框架用来形成传递给 HTML Help 系统的完整 URL。
HTML Help Workshop 目录
目录文件以“.hhc”扩展名结尾,它只是一个普通的 HTML 文件,使用 <UL>
、<LI>
和 <OBJECT>
标签定义大纲。类似这样
<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>
“Name
”和“Local
”参数用于命名显示项及其引用的内容。
除了目录之外,您可能还想添加一个索引。与目录一样,HTML Help Workshop 可以为您创建此文件,您可以根据需要添加条目。与目录一样,索引只是一个以“.hhk”扩展名结尾的 HTML 文件,它由 <UL>
、<LI>
和 <OBJECT>
标签组成。HTML Help Workshop 可以处理此文件的编辑,或者您可以手动编辑。
HTML Help Workshop 索引
拥有所有这些之后,您需要编译帮助,以便拥有一个最终编译的 HTML Help 文件,该文件应以“.chm”结尾。最后一步是确定 .chm 文件将放在哪里。框架会尝试使用几种不同的方法来查找帮助文件,其中一些您可以自定义。如果您什么都不做,那么框架会期望内容位于与可执行文件同级的名为“Help”的目录中。例如
[app directory]/
FooBar.exe
Help/
FooBar.chm
实际帮助文件的默认名称是应用程序的名称(不一定与实际可执行文件相同),或者可以由开发人员自定义。在 Windows 上,“.chm”扩展名会附加到最终帮助文件名后面。
搜索帮助
当开发人员请求显示帮助时,框架必须加载实际的帮助文件。它通过几个步骤来完成此操作,其中一些步骤可以自定义以提供帮助文件的特定位置。框架执行的第一步是检查是否存在 Application
实例,如果存在,则调用 Application::getHelpInfo()
函数,该函数接受两个字符串引用作为参数。第一个字符串是帮助图书名称,第二个字符串是帮助目录。稍后将把它们合并成一个最终的帮助文件名。如果其中任何一个字符串为空,则框架会尝试加载应用程序的 ProgramInfo
资源并从中提取帮助图书和帮助目录。如果该资源不可用,或者字符串仍然为空,则帮助图书将被分配为应用程序名称(如果应用程序实例是非 NULL
的)或可执行文件的基本名称。如果帮助目录为空,则将其分配为默认名称“Help”。
因此,作为想要自定义帮助查找的开发人员,您的选择是
- 覆盖
Application::getHelpInfo()
函数并为您的应用程序返回指向您帮助位置的有效字符串。 - 将此信息存储在您的
ProgramInfo
资源中。由于 Windows 的VS_VERSION_INFO
资源结构不支持这一点,您需要使用 Info.xml(或 Info.plist)文件,并为您的应用程序提供一个。例如可能如下所示<?xml version="1.0" encoding="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>
这将导致帮助从可执行文件的目录加上“Help Books/HelpName.chm”加载。
显示帮助
您可以通过几种不同的方式显示帮助。最简单的方法是显示索引或目录。要显示索引,只需调用 UIToolkit::displayHelpIndex()
。要显示目录,请调用 UIToolkit::displayHelpContents()
。框架将尝试确定正确的帮助文件,如“搜索帮助”部分所述。您可以根据需要自定义此项。
下一个显示帮助的方法是指定要显示的特定帮助部分。该部分指示要从帮助目录或资源(在我们的例子中是 .chm 文件)加载的 HTML 文件。对于 HTML 帮助,这意味着框架会将您的部分附加到 CHM 文件名后面,例如:“c:\Program Files\MyApp\Help\MyApp.chm::/foo.html”。要显示一个部分,请调用 UIToolkit::displayHelpSection()
函数并传入帮助部分(以及可选的帮助图书和目录)。
显示上下文相关的帮助
显示帮助的另一种方法是使用上下文相关的帮助。上下文相关的帮助是指与 UI 中的控件相关的帮助。框架会检测何时请求帮助事件(例如用户按下 F1 键),然后触发几个可能的事件,允许使用控件的“是什么”文本,或者,如果不存在,则允许显示其他上下文相关的帮助。这可能涉及根据鼠标悬停的控件的上下文以及鼠标在控件边界内的位置(如果适用)打开帮助到特定部分。
要显示上下文相关的帮助,您需要向您希望监视的控件的 HelpRequested
委托添加事件处理程序。此控件可能具有其他子控件,这些子控件可能与决定显示什么帮助相关。当您的事件处理程序被调用时,您将收到一个 HelpEvent
实例,然后您将为其分配一个帮助部分。您还可以分配帮助图书和帮助目录。框架然后使用此信息来显示实际帮助。
框架所需的信息是帮助图书名称、帮助图书目录和帮助部分。如果它们为空,则框架会使用特定的启发式方法来确定它们。最后一部分是部分名称,换句话说,是您想转到的帮助“图书”中的特定部分。这通常采用“filename.html#section”的形式。这假设您的内容已经创建了有效的名称部分。
这所有内容发生的机制允许在确定哪个控件响应帮助事件以及如何显示帮助方面具有相当大的灵活性。事件序列由框架收到操作系统帮助事件的通知启动;在 Windows 上,这意味着接收到 WM_HELP message
。然后调用 UIToolkit
的 UIToolkit::displayContextHelpForControl()
。这反过来会执行以下操作:
- 调用
displayContextHelpForControl()
的平台特定实现。 - 使用控件的“是什么”帮助字符串作为事件数据创建
WhatsThisHelpEvent
,然后将其在控件的ControlHelpRequested
委托上触发。 - 如果
WhatsThisHelpEvent
的帮助字符串不为空,则调用HtmlHelp()
API 函数,使用HH_DISPLAY_TEXT_POPUP
命令。这将导致小的弹出“是什么”帮助出现。此时,UIToolkit::displayContextHelpForControl()
完成,整个过程结束。 - 如果
WhatsThisHelpEvent
的帮助字符串为空,则UIToolkit
创建一个HelpEvent
并将事件在控件的HelpRequested
委托上触发。此事件具有可以分配部分名称、帮助图书和帮助目录的字段。 - 如果部分名称字符串为空,则
UIToolkit
会沿着父子层次结构向上遍历(从控件的父级开始),直到遇到NULL
父级,或者最终使HelpEvent
的帮助部分字段分配一个非空字符串。 - 如果找到非空帮助部分,则通过调用
UIToolkit::displayHelpSection()
函数来显示帮助。
换句话说,第一步是触发一个事件,允许程序显示“是什么”帮助。如果没有采取任何操作,则框架会尝试显示上下文相关的帮助。这从框架接收到 WM_HELP
消息时处于活动状态的控件开始,然后向上传播到第一个响应 HelpEvent
的父控件。
显示“是什么”帮助
最后一种帮助是“是什么”帮助(一些文档称之为上下文相关的帮助)。这是您通常在对话框中看到的帮助,它会弹出一个小工具提示,其中包含有关特定控件功能的某种信息。这通常通过单击对话框标题栏上的问号按钮来触发,或者在某些对话框中,当您右键单击控件并选择“是什么?”上下文菜单项时触发。无论哪种方式,这都会生成一个框架响应的 Windows 帮助消息。
“是什么”帮助
使用上下文菜单的“是什么”帮助
要在您的应用程序中启用对此的支持,您无需显式调用任何内容,框架将处理窗口消息,然后允许您通过几种方式对此作出响应。最简单的方法是确保您已通过调用 Control::setWhatsThisHelpString()
为所有适用的控件设置了“是什么”帮助字符串。另一种方法是将事件处理程序添加到控件的 ControlHelpRequested
委托。当从 Windows 收到适当的帮助消息时,框架会将 WhatsThisHelpEvent
实例发送到委托。然后,您可以为事件的 helpString
值填充适当显示的内容。框架使用此字符串通过 HH_DISPLAY_TEXT_POPUP
命令调用 HtmlHelp()
函数。
如果您以前在应用程序中使用 HtmlHelp
集成过弹出帮助,您会注意到这与通常的做法略有不同。传统上,您会创建一个 popups.txt 文件,并为每个帮助 ID 和要显示的字符串值添加特定条目,然后是另一个文件,将帮助 ID 映射到对话框编辑器分配的控件的帮助 ID。这相当繁琐且容易出错。使用 VCF,您可以简单地为控件的“是什么”帮助字符串分配帮助字符串。然后,如果您想支持本地化,只需在资源文件夹中提供额外的字符串翻译。
整合
包含的示例演示了如何在应用程序中显示和集成帮助的各种方法。让我们快速看一下显示目录和索引。我们将使用默认的帮助目录,因此我们创建一个名为“Help”的目录中的 .chm 文件,该目录与我们的 Help.exe 在同一级别。
[app directory]
Help.exe
Help/
Help App.chm
我们将 CHM 文件命名为“Help App.chm”,只是为了展示如何自定义帮助“图书”名称。为了确保框架可以找到它,我们需要确保我们的应用程序命名得当
class HelpApplication : public Application {
public:
//rest of code omitted
virtual bool initRunningApplication(){
bool result = Application::initRunningApplication();
setName( "Help App" );
//rest of code omitted
return result;
}
};
这是确保框架正确识别我们的帮助文件的最简单方法。
构建好 UI 后,让我们添加一些事件处理程序来处理目录和索引的显示
class HelpWindow : public Window {
public:
//rest of code omitted
void showContents( Event* e ) {
UIToolkit::displayHelpContents();
}
void showIndex( Event* e ) {
UIToolkit::displayHelpIndex();
}
};
当调用 showContents()
事件处理程序函数时,我们只需调用 UIToolkit::displayHelpContents()
,使用默认的空参数。框架将负责其余部分并显示帮助。showIndex()
函数也一样。
调用 HelpWindow::showContents() 的结果
调用 HelpWindow::showIndex() 的结果
为了演示显示“是什么?”帮助,让我们显示一个已为对话框上的控件设置了适当条目的对话框。
class MyDialog : public Dialog {
public:
MyDialog() {
//rest of code omitted
TextControl* edit1 = new TextControl();
//set your what's this help description here.
edit1->setWhatThisHelpString( "Type in your name here." );
//rest of code omitted
CheckBoxControl* checkBox = new CheckBoxControl();
checkBox->setCaption( "Yes I am!" );
//rest of code omitted
//set your what's this help description here.
checkBox->setWhatThisHelpString( "This indicates whether "
"or not you're a Mergatroid. "
"\nThink carefully before answering." );
//rest of code omitted
CommandButton* okBtn = new CommandButton();
okBtn->setWhatThisHelpString( "Click this to accept "
"the changes and close the dialog!" );
//rest of code omitted
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" );
}
};
当显示对话框,用户单击对话框的问号按钮时,我们设置的文本将显示在弹出窗口中。
显示的对话框
显示的控件“是什么”帮助
为了显示我们的上下文相关的帮助,我们向窗口的 HelpRequested
委托添加了一个事件处理程序
class HelpWindow : public Window {
public:
HelpWindow() {
setCaption( "Help" );
//rest of code omitted
//grab the context help notifications!
HelpRequested +=
new GenericEventHandler<HelpWindow>(this,
&HelpWindow::onContextHelp,
"HelpWindow::onContextHelp" );
}
};
我们的事件处理程序 HelpWindow::onContextHelp()
看起来是这样的
class HelpWindow : public Window {
public:
//rest of code omitted
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;
}
}
};
我们的 UI 由四个标签组成,我们将它们存储在四个不同的成员变量中。当我们的 HelpWindow::onContextHelp()
函数被调用时,我们确定当前鼠标位置,然后确定鼠标在哪一个标签上。如果找到匹配项,我们将相应的字符串值赋给 HelpEvent
的 helpSection
字段。然后框架使用此信息来显示帮助。
显示的标签 2 的上下文相关帮助
关于构建示例的说明
您需要安装最新版本的 VCF(至少 0-9-0 或更高版本),并且需要确保您已构建 VCF 的静态库(而不是 DLL 版本)。示例配置为静态链接到 VCF。有关构建 VCF 的更多文档,请参阅 VCF 在线文档的:Building the VCF。
除了 VCF,您还需要一个最新版本的 HTML Help 编译器来构建此示例。据我所知,唯一的合法获取方式是通过 Microsoft 的 HTML Help downloads section 下载并安装 HTML Help Workshop。
将 BuildHelp.bat.txt 重命名为 BuildHelp.bat,将 build_bcc.bat.txt 重命名为 build_bcc.bat,这些文件需要“混淆”才能通过我的 ISP 的邮件过滤器。
结论
我们已经涵盖了使用 VCF 在应用程序中处理、创建和集成帮助的基础知识。希望这对您使用 VCF 编写获奖的下一代应用程序有所帮助!
欢迎提出关于框架的问题,您可以在这里或我们的论坛中发帖。如果您有关于如何改进这些方面的建议,我们非常乐意听取!