65.9K
CodeProject 正在变化。 阅读更多。
Home

C# WinForms 应用程序与 HTMLHelp (.chm) 完全集成 - 帮助主题、上下文相关帮助和工具提示

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (11投票s)

2011年4月26日

CPOL

5分钟阅读

viewsIcon

68296

downloadIcon

2226

使用 HTMLHelp (.chm) 在 C# Winform 应用程序中显示帮助主题、上下文相关帮助和工具提示

引言

本文将介绍如何将 HTMLHelp (.chm) 与 C# WinForm 应用程序完全集成。标准的 .NET HelpProvider 只能显示 .chm 文件中的主题或显示静态上下文帮助。在这里,我们将展示如何从 .chm 中显示上下文相关帮助和工具提示。

背景

最初 HTMLHelp 是为 C++ 应用程序设计的,因此标准 C++ 应用程序可以充分利用 .chm 文件。通过 HtmlHelp API,它可以显示主题并自动将控件分配给 stringId,从而在需要时显示上下文相关帮助。所有这些都通过 C++ controlId 映射到 HTMLHelp stringId 并共享 .h 文件(由 HTMLHelp 编译器和 C++ 应用程序共享以帮助映射)来实现。C# 在这方面的 .chm 文件使用非常有限。通过 HelpProvider,你只能将主题或静态文本分配给控件以在请求帮助时显示。控件不再具有 C++ ID,无法映射到 HTMLHelp stringId 以进行自动上下文帮助显示。为了解决上下文相关帮助的不足,我们仍然可以通过 InteropServiceshhctrl.ocx 使用 HtmlHelp API。我们可以使用此函数配合 HH_DISPLAY_TEXT_POPUP 并传递从 C# 填充了必要信息的 marshaled HH_POPUP 结构。唯一的问题是它需要 idString,即 HTMLHelp 文件中上下文帮助文本 string 的数字 ID。在 C++ 中,这些数字 ID 来自共享的 .h 文件,但这里我们没有......嗯,那就把它作为资源添加到项目中,并自己解析。我们可以“添加为链接”这个 .h 文件(它最初应该在 HTMLHelp 项目中),并将其构建操作设置为“嵌入资源”。.h 文件由如下的 defines 组成:

#define ButtonHelp          1001
#define ButtonHelp.TT       1002
#define MainForm.tbKW       1003
#define MainForm.tbKW.TT    1004
#define MainForm            1005
#define MainForm.TT         1006
#define MainForm.cbHC       1007
#define MainForm.tvHF       1008

因此,我们可以在运行时读取资源

Assembly.GetExecutingAssembly().GetManifestResourceStream("WinHelpTest.XPHelpMap.h"))

并创建一个将 string 映射到数字的字典。但仍然如何将这些数字映射到 C# 应用程序中的控件?嗯,我们可以想出一个自动递归命名约定,通过将控件及其父项在层次结构中的名称连接起来来构建 HTMLHelp 名称。

String GetControlName(Control ctrl)
{
    if (ctrl.Parent == null)
    {
        return ctrl.Name;
    }
    return GetControlName(ctrl.Parent) + "." + ctrl.Name;
}

例如,如果我们有一个名为 MainFormForm 并且上面有一个名为 tbKW 的控件,我们可以自动推断出与此控件匹配的 stringId 在字典中键为“MainForm.tbKW”。因此,如果我们考虑到这一点来构建 .h 文件 defines,那么在使用 C# 时一切都会连接起来。HtmlHelp API 在控件的 HelpRequested 事件上调用,自动用正确的 stringId 填充 HH_POPUP,该 stringId.h 字典中获取,其键为“递归命名约定控件名称” - GetControlName(control)

.chm 文件中获取工具提示是另一回事。即使是 C++ 应用程序也无法获得,所以我们必须使用一些技巧来实现。这个想法来自另一篇 CodeProject 文章 使用 C# 反编译 CHM (帮助) 文件,其中解释了如何浏览 .chm 文件的内容。基本上,包含在 HMLHelp 项目中的所有文件都编译为 .chm 文件中的 IStreams,这是一种复合文档文件,但不是你可以用 Ole32.dll StgOpenStorage 打开的文件,而是我们需要使用未公开的接口 ITStorage (itss.dll) 及其 StgOpenStorage。所以,基本上,我们可以创建一个具有与上下文相关帮助的 .txt 文件相同的语法的 .txt 文件(甚至可以使用同一个文件)并将工具提示文本添加进去。然后,使用与上下文相关帮助相同的命名约定命名 stringId,但末尾添加“.TT”以将其作为工具提示分隔开。因此,当应用程序运行时,我们可以从 .chm 存储中读取此 stream 并进行解析,然后创建 stringId 和工具提示文本的字典。然后,我们可以将 C# ToolTip 对象设置为与每个控件关联,并自动为控件名称分配文本。

Using the Code

本文附带的代码是一个 C# WinForms 项目和一个实现了 .chm 帮助交互的库文件。

还有一个 HTMLHelp Workshop 项目,可与 C# 应用一起使用

它演示了所述的思路,并可以为你提供一个好的起点。

WinForms 应用程序通过类库 WinHtmlLib 中的 WinHelpEx 类使用 .chm 帮助文件。所以,首先,创建一个这个类的对象

WinHelpEx s_help = new WinHelpEx();

这个对象可以是 static 的,并在整个应用程序中使用,或者根据 .chm 文件的需求和结构按窗体使用。如果为 .chm 文件中的每个窗体定义单独的上下文相关 .txt 文件和工具提示 .txt 文件以及 .h 映射文件,则每个窗体都必须有一个对象。如果所有上下文相关帮助都在一个文件中,工具提示文本在一个文件中,映射 .h 文件只有一个,那么你可以只为整个项目使用一个 WinHelpEx static 对象。

WinHelpEx 有两个主要函数

public void Load(
    string sChmPath,//path to the .chm file
    Stream sAliasIDH,//this is a stream object to the .h resource
     // used for the mapping
     // (Assembly.GetExecutingAssembly().GetManifestResourceStream(
     //"WinHelpTest.XPHelpMap.h"))
    string sCSStreamName,//name of the IStream in .chm
     // holding the context sensitive help
    string sTTStreamName//name of the IStream in .chm holding the tooltip text
);
//loads the two dictionaries from the .chm file

public void AttachForm(
    Control ctrlBase,//( can be null - the base control is frm )control
        //for the bottom of the hierarchy when constructing HTMLHelp string key
    Form frm,//the form for which we want to apply the .chm help 
    bool putHelpButton,//should we show the Help Button in the window Title bar
    IsControlUsedDlgt IsCtrlUsed, //( can be null - control is used
        //for help ) supplied by the form - callback that
        //can specify if control has help associated in .chm ( can exclude controls )
    GetControlNameDlgt GetCtrlName,//( can be null - control is assigned
        //the automatically generated HTMLHelp stringId ) supplied
        //by the form - callback that can specify if control
        //have HTMLHelp string name different than the automatically
        //generated for the context sensitive string name
    IsCSHelpDlgt IsCSHelp,//(can be null - if form default help action
        //is show topic , if control default help action is context
        //sensitive help ) supplied by the form - callback that can
        //specify if control have context sensitive help or show topic 
    IsControlUsedDlgt IsCtrlTTUsed,//( can be null - control
      //is used for tooltip ) supplied by the form - callback
      //that can specify if control has tooltip
      //associated in .chm ( can exclude controls )
    GetControlNameDlgt GetCtrlTTName //( can be null - control is assigned
       //the automatically generated HTMLHelp stringId for tooltip )
       //supplied by the form - callback that can specify
       //if control have HTMLHelp string name different
       //than the automatically generated for the tooltip string name
);
//goes through all the suncontrols of the form recursively and tries
//to associate context sensitive help and tooltip with them according
//to that if control is used and what is the HTMLHelp string name of the control

请查看附件示例了解更多详情,或者直接提问。

谢谢。

修订 1

代码已更新,根据消息部分 wvd_vegt 的评论,可在 Win7 64 位上运行。

修订 2

代码已更新,以输出一个包含 string ID 的 #defines 的 .h 文件。

要启用它,请取消注释 Program.cs 中的这些行

//FileStream fs = new FileStream(@"defines.h", FileMode.Append, FileAccess.Write);
//WinHelpEx.AttachOutput(fs); 
...
...
...
//WinHelpEx.ReleaseOutput();

在此情况下,您无需调用 load 函数 s_help.Load( ...... );

© . All rights reserved.