XHTMLStatic - 一个极其精简的自定义控件,用于显示 HTML






4.92/5 (39投票s)
一个基于 CStatic 的自定义控件,它可以解释并显示常见的 HTML 文本格式化元素,如粗体、斜体、颜色、字体和大小,但只会为您的应用程序增加 16 KB 的体积。
引言
在应用程序中有许多地方,漂亮的文本格式都能让您的软件看起来更精致——例如在关于框、启动屏幕,甚至注册对话框中。当我想办法实现这一点时,我发现了以下几种选择:
- 富文本控件 - 这是最难使用的一种,因为它涉及非常复杂的 API 和自定义代码——除非您愿意学习 RTF。 (我不知道您是否和我一样,但那些反斜杠让我头晕。) CodeProject 在这里有许多关于富文本控件的文章:这里。
- CHtmlCtrl - 这是 Paul DiLascia 编写的一个不错的基于
CHtmlView
的控件。您可以在 2000 年 1 月的 MSJ 中找到它。好吧,显然我没有走这条路。为什么没有?这里是 DiLascia 在他的文章中的最后一句话,它解释了如何在 About 对话框中使用 CHtmlCtrl:"...一句忠告:加载所有这些 Internet Explorer DLL 需要一些时间。如果运行 About 对话框需要 10 秒钟和沙漏光标,用户可能会认为您的应用程序是由一个笨蛋编写的。" - QHTM - 这是一个共享软件产品,还有一个“精简版”的免费软件版本,可以在这里找到。最初看起来不错,但后来我发现“精简版”DLL 的大小是 248 KB。当您只需要简单的文本格式化时,引入这么多代码太浪费了。而且,即使是共享软件版本,您也无法获得源代码——您必须额外付费才能获得源代码。
因此,在这些选择之间,您必须在复杂性和/或代码臃肿之间做出选择。这让我很失望,但也促使我思考如何显示简单的文本格式。
首先,基于什么来创建新的控件?为了保持尽可能简单,我选择了 CStatic
。其次,使用什么格式?这是一个容易的决定,因为显然 HTML 正在成为首选的格式标准。下一个决定是支持哪些 HTML 元素。这里在实现简单紧凑的功能和实现 CHtmlCtrl
所做的事情之间没有明确的界限。例如,目前我不需要显示表格,这就消除了很多复杂性。如果我将来需要表格,我可能会直接使用 CHtmlCtrl
——毕竟,如果您真的需要这个功能,为什么不直接从微软那里获取呢?最后的决定也很容易:新控件将不支持任何交互元素,除了一个:超链接。
CXHTMLStatic 功能
CXHTMLStatic
支持以下 HTML 标签:
Tag | 语法 | 属性 |
---|---|---|
A - 锚点 | <A>...</A> | HREF="https://codeproject.org.cn" HREF="mailto:hdietrich@gmail.com" HREF="app:MY_COMMAND_MESSAGE" |
BIG - 大号文本 | <BIG>...</BIG> | ![]() |
B - 粗体文本 | <B>...</B> | ![]() |
BR - 换行 | <BR> | ![]() |
CENTER - 文本居中 | <CENTER>...</CENTER> | ![]() |
CODE - 软件代码文本 | <CODE>...</CODE> | ![]() |
FONT - 字体更改 | <FONT>...</FONT> | COLOR="颜色字符串" BGCOLOR="颜色字符串" SIZE="大小调整" FACE="字体名称" |
HR - 水平线 | <HR> | SIZE=线条粗细 |
I - 斜体文本 | <I>...</I> | ![]() |
SMALL - 小号文本 | <SMALL>...</SMALL> | ![]() |
STRIKE - 删除线文本 | <STRIKE>...</STRIKE> | ![]() |
SUB - 下标文本 | <SUB>...</SUB> | ![]() |
SUP - 上标文本 | <SUP>...</SUP> | ![]() |
U - 下划线文本 | <U>...</U> | ![]() |
除了 FONT
的 BGCOLOR
属性和 A
的 app: 说明符之外,所有这些标签都是标准的 HTML。
使用 FONT 标签
FONT 的 COLOR
和 BGCOLOR
属性都接受一个 color string value
,它是一个有三种形式的字符串:
- "颜色名称" - 示例:"red"。
- "十六进制值" - 示例:"#FF0000"。
- "RGB 值" - 示例:"255,0,0"。
FONT
的 SIZE
属性目前只接受相对大小的调整——加或减。例如,SIZE="+4"
或 SIZE="-2"
。
使用 APP: 超链接
在 XHTMLStatic 控件中使用 APP: 超链接需要三个步骤:
- 定义 APP: 结构 - 这是一个示例表:
/////////////////////////////////////////////////////////////////// // // define app command message used by <a href=\"app:WM_APP_COMMAND\"> // #define WM_APP_COMMAND_1 (WM_APP+100) XHTMLSTATIC_APP_COMMAND CXHTMLStaticTestDlg::m_AppCommands[] = { { m_hWnd, WM_APP_COMMAND_1, 1, _T("WM_APP_COMMAND") } };
这个表只有一个条目,但您可以添加任意多的条目。每个条目有四个元素:第一个是要接收消息的窗口的
HWND
;第二个是通过SendMessage()
发送到窗口的数字消息编号;第三个是用户定义的数据,将在 wParam 成员中返回;第四个是用于将表条目与 HTML 代码关联的字符串。当用户单击链接时,XHTMLStatic 将扫描该表,尝试找到与 "app:" 后面的字符串匹配的条目。然后,它将从表中提取消息编号并发送消息。 - 将表地址传递给 XHTMLStatic 控件:
m_static1.SetAppCommands(m_AppCommands, 1);
此函数传递表地址和表中的条目数。XHTMLStatic 控件会自己复制一份表,因此父窗口无需确保其持久性。
- 在 HTML 中插入超链接 — 在 XHTMLStaticTestDlg.cpp 中,APP: 超链接的 HTML(此处未显示字体标签)的写法如下:
_T("<a href=\"app:WM_APP_COMMAND1\">Moby Dick</a>")
app: 后面的字符串 "
WM_APP_COMMAND
" 是将超链接与步骤 1 中的应用程序命令表关联起来的。请注意,此字符串可以是您想要的任何内容;为了提高可读性,您可以使用实际消息命令常量的“字符串形式”。
使用字符实体
对字符实体的支持是上一个版本中最受欢迎的功能之一。我希望支持所有常用的字符实体,但又不想有一个巨大的表,包含大多数人永远不会使用的字符。折衷方案是:一个驱动的实体查找表,该表可以轻松添加新条目。
它是如何工作的:
- 实体表在 XHTMLStatic.h 中定义为:
static XHTMLSTATIC_CHAR_ENTITIES m_aCharEntities[];
表中的每个条目定义为:struct XHTMLSTATIC_CHAR_ENTITIES { TCHAR * pszName; // string entered in HTML - e.g., " " TCHAR cCode; // code generated by XHTMLStatic TCHAR cSymbol; // character symbol displayed };
- 表在 XHTMLStatic.cpp 中指定:
XHTMLSTATIC_CHAR_ENTITIES CXHTMLStatic::m_aCharEntities[] = { { _T("&"), 0, _T('&') }, // ampersand { _T("•"), 0, _T('\x95') }, // bullet NOT IN MS SANS // SERIF { _T("¢"), 0, _T('\xA2') }, // cent sign { _T("©"), 0, _T('\xA9') }, // copyright { _T("°"), 0, _T('\xB0') }, // degree sign { _T("€"), 0, _T('\x80') }, // euro sign { _T("½"), 0, _T('\xBD') }, // fraction one half { _T("¼"), 0, _T('\xBC') }, // fraction one quarter { _T(">"), 0, _T('>') }, // greater than { _T("¿"), 0, _T('\xBF') }, // inverted question mark { _T("<"), 0, _T('<') }, // less than { _T("µ"), 0, _T('\xB5') }, // micro sign { _T("·"), 0, _T('\xB7') }, // middle dot = Georgian comma { _T(" "), 0, _T(' ') }, // nonbreaking space { _T("¶"), 0, _T('\xB6') }, // pilcrow sign = paragraph sign { _T("±"), 0, _T('\xB1') }, // plus-minus sign { _T("£"), 0, _T('\xA3') }, // pound sign { _T("""), 0, _T('"') }, // quotation mark { _T("®"), 0, _T('\xAE') }, // registered trademark { _T("§"), 0, _T('\xA7') }, // section sign { _T("¹"), 0, _T('\xB9') }, // superscript one { _T("²"), 0, _T('\xB2') }, // superscript two { _T("×"), 0, _T('\xD7') }, // multiplication sign { _T("™"), 0, _T('\x99') }, // trademark NOT IN MS SANS // SERIF { NULL, 0, 0 } // MUST BE LAST
要添加一个条目,只需按照与其他条目相同的格式操作即可。您可以使用 Microsoft 的字符映射表实用程序 charmap.exe 来获取十六进制显示代码。
- 就是这样!当 XHTMLStatic 控件看到实体名称时,它将用显示代码进行替换。
下表显示了字符实体将如何显示:
实体 | 显示符号 | 描述 |
---|---|---|
& | & | ampersand |
• | • | 圆点 (MS SANS SERIF 中没有) |
¢ | ¢ | 美分符号 |
© | © | 版权 |
° | ° | 度符号 |
€ | € | 欧元符号 |
½ | ½ | 分数 一半 |
¼ | ¼ | 分数 四分之一 |
> | > | greater than |
¿ | ¿ | 反问号 |
< | < | less than |
µ | µ | 微符号 |
· | · | 中间点 = 格鲁吉亚逗号 |
| ![]() |
不间断空格 |
¶ | ¶ | 段落符号 = 段落符号 |
± | ± | 加减号 |
£ | £ | 英镑符号 |
" | " | 引号 = 双引号 |
® | ® | 注册商标 |
§ | § | 节符号 |
¹ | ¹ | 上标一 |
² | ² | 上标二 |
× | × | 乘号 |
™ | ™ | 商标 (MS SANS SERIF 中没有) |
CXHTMLStatic 演示
演示项目提供了一个示例应用程序,展示了 XHTMLStatic 控件的外观。

演示展示了大多数 XHTMLStatic 功能,包括下标和上标、字体以及 http: 和 app: 超链接。
按“点击此处查看 HTML”按钮,HTML 将在单独的对话框中显示。

当您单击标题时,您将看到父对话框捕获到 APP:
消息。

当前限制
- 支持的 HTML 标签仅限于上面列出的那些。
- 目前,更改同一行上字体的尺寸不会产生预期的结果。对于任何希望这样做的人来说,这可以通过跟踪基线并调整绘图文本的矩形来修复。
- 在解析 HTML 时做了一些简化假设——例如,假设在 < 和标签的开始之间没有空格。另一个假设是,序列 "<a href=" 在 "a" 和 "href" 之间只有一个空格,并且 "href" 和 "=" 之间没有空格。
如何使用
要将此 CXHTMLStatic
集成到您自己的应用程序中,您首先需要将以下文件添加到您的项目中:
- XHTMLStatic.cpp
- XHTMLStatic.h
- XNamedColors.cpp
- XNamedColors.h
接下来,在您的对话框中创建一些静态控件,您想要放置新的 XHTMLStatic 控件。还请使用 ClassWizard 为这些控件指定变量名。然后,在对话框的头文件中包含 XHTMLStatic.h 头文件,并将 CStatic
变量替换为 CXHTMLStatic
。这显示了演示中的 XHTMLStaticTestDlg.h 头文件。
#include "XHTMLStatic.h"
//////////////////////////////////////////////////////////////////////
// CXHTMLStaticTestDlg dialog
class CXHTMLStaticTestDlg : public CDialog
{
// Construction
public:
CXHTMLStaticTestDlg(CWnd* pParent = NULL); // standard constructor
// Dialog Data
//{{AFX_DATA(CXHTMLStaticTestDlg)
enum { IDD = IDD_XHTMLSTATICTEST_DIALOG };
CXHTMLStatic m_static1;
CXHTMLStatic m_static2;
//}}AFX_DATA
.
.
.
最后一步是在 XHTMLStatic 控件中放入一些文本。使用您通过 ClassWizard 添加的变量,您可以说:
m_static1.SetWindowText(_T("<b><font size=\"+8\">Moby Dick</font></b>"));
有关如何使用 CXHTMLStatic
的示例,请参阅 XHTMLStaticTestDlg.cpp。
修订历史
版本 1.4 - 2007 年 10 月 19 日
- 修复了文本可能写在控件客户区外面的错误,该错误由 David Pritchard 报告(并附带修复)。
- 扩展了字符实体的表格。
- 添加了 VS2005 项目。
- 实现了内存 DC 以提高性能,由 conan.ks 建议。
- 从 WM_TIMER 切换到 WM_MOUSEMOVE 来显示手形光标,由 rm2 和 RichardC 建议。这可以防止手形光标出现在重叠的窗口上。
版本 1.3 - 2006 年 8 月 15 日
- 添加了透明度支持,由 Anna 建议。
- 添加了对
WM_PRINT
的支持,由 beaus07 建议。 - 添加了对 <center> 的支持,这是由几位读者请求的。
- 为超链接添加了工具提示支持。
- 从
IDC_HAND
加载手形光标,由 kamnas 建议。 - 修复了字体对象泄露问题,由 furbo 报告。
- 修复了
SetWindowText()
的问题,由 Andro67 报告;调用SetWindowText()
时背景和文本颜色不再被重置。 - 修复了控件隐藏时的错误,由 RichardC 报告。
版本 1.2 - 2004 年 6 月 12 日
- 将 APP: 超链接从使用
GetParent
改为使用 HWND。 - 向
XHTMLSTATIC_APP_COMMAND
结构添加了 wParam。 - 添加了函数
SetTextColor(LPCTSTR lpszColor)
。 - 添加了函数
SetLogFont(const LOGFONT * pLogFont)
。 - 添加了函数
SetWindowText()
以调用 Init 和RedrawWindow
。 - 修复了
XNamedColors
在处理“255,0,0”样式时的错误。 - 在
SetColorFromString()
中。 - 修复了大型衬线字体的下伸部问题。
版本 1.1 - 2004 年 5 月 20 日
- 实现了 SUB 标签。
- 实现了 SUP 标签。
- 实现了 BIG 标签。
- 实现了 SMALL 标签。
- 实现了 CODE 标签。
- 实现了 HR 标签。
- 实现了 APP: 超链接。
- 实现了常见的字符实体。
- 提高了解析性能。
- 错误修复。
版本 1.0 - 2002 年 9 月 16 日
- 首次公开发布。
用法
本软件已进入公共领域。您可以自由地以任何方式使用它,但不得出售此源代码。如果您修改或扩展它,请考虑将新代码发布到此处供大家分享。本软件按“原样”提供,不附带任何明示或暗示的保证。对于本软件可能造成的任何损害或业务损失,本人概不负责。