Click here to Skip to main content
15,867,330 members
Articles / Desktop Programming / MFC
Article

XHTMLStatic - An Extra-Lean Custom Control to Display HTML

Rate me:
Please Sign up or sign in to vote.
4.92/5 (42 votes)
25 Oct 2007CPOL9 min read 267.3K   4.2K   158   78
A custom control based on CStatic that interprets and displays common HTML text formatting elements such as bold, italic, color, font, and size, but adds only 16 Kb to your app.

Introduction

There are many places in an application where nice text formatting will give your software a polished look — like in about-boxes, splash screens, and even registration dialogs. When I look into how to do this, I find these alternatives:

  1. Rich edit control — This is by far the toughest to use, since it involves a very complicated API and custom code — unless you are willing to learn RTF. (I don't know about you, but all those backslashes make me dizzy.) CodeProject has many rich edit control articles here.
  2. CHtmlCtrl — This is a nice CHtmlView-based control written by Paul DiLascia. You can find it in the January 2000 MSJ. OK, so obviously I didn't go this way. Why not? Here's DiLascia's last sentence from his article, which explains how to use CHtmlCtrl in an About box:
    "...a word of caution: it does take a little while to load all those Internet Explorer DLLs. If it takes 10 seconds and an hourglass to run the About dialog, users may think your app was written by a bozo."
  3. QHTM — This is a shareware product, and there is also a "lite" freeware version available here. This initially looked good, but then I discovered the size of the "lite" DLL was 248 Kb. This is way too much code to drag in, when all you want is simple text formatting. And besides, you don't get the source code, even with the shareware version — you have to pay extra for the source.

So with these alternatives, you must choose between complexity and/or code bloat. This was disappointing to me, but also made me think how to display simple text formatting.

First, what to base the new control on? To keep it simple as possible, I picked CStatic. Second, what formatting to use? This was an easy decision, because it's obvious that HTML is becoming formatting standard of choice. The next decision had to do with what HTML elements to support. Here there is a not-so-clear line between doing something simple and compact, and doing what CHtmlCtrl does. For example, right now I do not need to display tables, so this eliminated a lot of complexity. If I ever did need tables, I would probably just use CHtmlCtrl — after all, if you really need the functionality, why not get it straight from Microsoft? The final decision was also easy: the new control would support no interactive elements, except one: hyperlinks.

CXHTMLStatic Features

The following HTML tags are supported by CXHTMLStatic:

TagSyntaxAttributes
A - Anchor<A>...</A>HREF="http://www.codeproject.com"
HREF="mailto:hdietrich@gmail.com"
HREF="app:MY_COMMAND_MESSAGE"
BIG - Big Text<BIG>...</BIG>Screenshot - blank2.png
B - Bold Text<B>...</B>Screenshot - blank1.png
BR - Line Break<BR>Screenshot - blank2.png
CENTER - Center Text<CENTER>...</CENTER>Screenshot - blank1.png
CODE - Software Code Text<CODE>...</CODE>Screenshot - blank2.png
FONT - Font Change<FONT>...</FONT>COLOR="Color string"
BGCOLOR="Color string"
SIZE="Size adjustment"
FACE="Font face name"
HR - Horizontal Rule<HR>SIZE=Line thickness
I - Italic Text<I>...</I>Screenshot - blank1.png
SMALL - Small Text<SMALL>...</SMALL>Screenshot - blank2.png
STRIKE - Strike-through Text<STRIKE>...</STRIKE>Screenshot - blank1.png
SUB - Subscript Text<SUB>...</SUB>Screenshot - blank2.png
SUP - Superscript Text<SUP>...</SUP>Screenshot - blank1.png
U - Underlined Text<U>...</U>Screenshot - blank2.png

All of these tags are standard HTML, except the BGCOLOR attribute for FONT and the app: specifier for A.

Using the FONT Tag

The FONT COLOR and BGCOLOR attributes both take a color string value, which is a string in one of three forms:

  • "color-name" - Example: "red".
  • "hex-value" - Example: "#FF0000".
  • "rgb-value" - Example: "255,0,0".

The SIZE attribute for FONT currently takes only relative size adjustments - plus or minus. For example, SIZE="+4" or SIZE="-2".

Using the APP: Hyperlink

Using an APP: hyperlink in a XHTMLStatic control involves three steps:

  1. Define the APP: struct - here is a sample table:
    C++
    ///////////////////////////////////////////////////////////////////
    //
    // 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") }
    };

    This table only has one entry, but you can add as many entries as you need. Each entry has four elements: the first is the HWND of the window that is to receive the message; the second is the numeric message number that will be sent to the window via SendMessage(); the third is user-defined data that is returned in the wParam member; and the fourth is a string that ties the table entry to the HTML code. When the user clicks on the link, XHTMLStatic will scan the table trying to find a match for the string that follows the "app:". Then, it will extract the message number from the table and send the message.

  2. Pass the table address to the XHTMLStatic control:
    m_static1.SetAppCommands(m_AppCommands, 1);
    

    This function passes the table address and the number of entries in the table. The XHTMLStatic control makes its own copy of the table, so the parent need not ensure its persistence.

  3. Insert the hyperlink in the HTML — in XHTMLStaticTestDlg.cpp, the HTML for the APP: hyperlink (shown here without the font tags) is written as:
    C++
    _T("<a href=\"app:WM_APP_COMMAND1\">Moby Dick</a>")
    

    The string "WM_APP_COMMAND" that follows the app: is what ties the hyperlink to the app command table of Step 1. Note that this string may be anything you wish; to improve readability, you can use the "string form" of the actual message command constant.

Using Character Entities

Support for character entities was one of the features most requested for the last version. I wanted to support all the commonly used ones, but I did not want a huge table with characters that most people would never use. The compromise: a table-driven entity lookup, with a table that can be easily added to.

Here's how it works:

  1. The entity table is defined in XHTMLStatic.h as
    C++
    static XHTMLSTATIC_CHAR_ENTITIES    m_aCharEntities[];
    Each of the table entries is defined as:
    C++
    struct XHTMLSTATIC_CHAR_ENTITIES
    {
        TCHAR * pszName;    // string entered in HTML - e.g., " "
        TCHAR   cCode;      // code generated by XHTMLStatic
        TCHAR   cSymbol;    // character symbol displayed
    };
  2. The table is specified in XHTMLStatic.cpp:
    C++
    XHTMLSTATIC_CHAR_ENTITIES CXHTMLStatic::m_aCharEntities[] = 
    {
        { _T("&amp;"),     0,  _T('&') },     // ampersand
        { _T("&bull;"),    0,  _T('\x95') },  // bullet      NOT IN MS SANS
                                              //             SERIF
        { _T("&cent;"),    0,  _T('\xA2') },  // cent sign
        { _T("&copy;"),    0,  _T('\xA9') },  // copyright
        { _T("&deg;"),     0,  _T('\xB0') },  // degree sign
        { _T("&euro;"),    0,  _T('\x80') },  // euro sign
        { _T("&frac12;"),  0,  _T('\xBD') },  // fraction one half
        { _T("&frac14;"),  0,  _T('\xBC') },  // fraction one quarter
        { _T("&gt;"),      0,  _T('>') },     // greater than
        { _T("&iquest;"),  0,  _T('\xBF') },  // inverted question mark
        { _T("&lt;"),      0,  _T('<') },     // less than
        { _T("&micro;"),   0,  _T('\xB5') },  // micro sign
        { _T("&middot;"),  0,  _T('\xB7') },  // middle dot = Georgian comma
        { _T(" "),    0,  _T(' ') },     // nonbreaking space
        { _T("&para;"),    0,  _T('\xB6') },  // pilcrow sign = paragraph sign
        { _T("&plusmn;"),  0,  _T('\xB1') },  // plus-minus sign
        { _T("&pound;"),   0,  _T('\xA3') },  // pound sign
        { _T("&quot;"),    0,  _T('"') },     // quotation mark
        { _T("&reg;"),     0,  _T('\xAE') },  // registered trademark
        { _T("&sect;"),    0,  _T('\xA7') },  // section sign
        { _T("&sup1;"),    0,  _T('\xB9') },  // superscript one
        { _T("&sup2;"),    0,  _T('\xB2') },  // superscript two
        { _T("&times;"),   0,  _T('\xD7') },  // multiplication sign
        { _T("&trade;"),   0,  _T('\x99') },  // trademark   NOT IN MS SANS
                                              //             SERIF
        { NULL,            0,  0 }            // MUST BE LAST

    To add an entry, just follow the same format as for the other entries. You can the Microsoft Character Map utility, charmap.exe, to obtain the hex display code.

  3. That's it! The XHTMLStatic control will now substitute the display code whenever it sees the entity name.
One caution when using character entities: some fonts, like MS Sans Serif, are very limited in the available glyphs. So you should verify that they display properly, or select another font before displaying them.

The following table shows how the character entities will be displayed:

EntityDisplay SymbolDescription
&amp;&ampersand
&bull;bullet (not in MS SANS SERIF)
&cent;¢cent sign
&copy;©copyright
&deg;°degree sign
&euro;euro sign
&frac12;½fraction one half
&frac14;¼fraction one quarter
&gt;>greater than
&iquest;¿inverted question mark
&lt;<less than
&micro;µmicro sign
&middot;·middle dot = Georgian comma
&nbsp;Screenshot - blank2.pngnonbreaking space
&para;pilcrow sign = paragraph sign
&plusmn;±plus-minus sign
&pound;£pound sign
&quot;"quotation mark = double quote
&reg;®registered trademark
&sect;§section sign
&sup1;¹superscript one
&sup2;²superscript two
&times;×multiplication sign
&trade;trademark (not in MS SANS SERIF)

CXHTMLStatic Demo

The demo project provides a sample app that shows what the XHTMLStatic control looks like:

screenshot

The demo displays most of the XHTMLStatic features, including sub- and super-script, font, and http: and app: hyperlinks.

Press the "Click here to see the HTML" button and the HTML is shown in a separate dialog:

screenshot

When you click on the title, you will see the parent dialog catch the APP: message:

screenshot

Current Limitations

  1. The supported HTML tags are limited to those listed above.
  2. Currently changing the size of fonts on the same line does not produce the desired result. For anyone who wishes to do so, this can be fixed by keeping track of baseline and adjusting rect for drawing text.
  3. There are some simplifying assumptions made when parsing the HTML - for example, it is assumed there are no spaces between the < and the beginning of the tag. Another assumption is that the sequence "<a href=" will only have one space between the "a" and the "href", and that there will be no spaces between the "href" and the "=".

How To Use

To integrate this CXHTMLStatic into your own app, you first need to add the following files to your project:

  • XHTMLStatic.cpp
  • XHTMLStatic.h
  • XNamedColors.cpp
  • XNamedColors.h

Next, create some static controls on your dialog, where you want to put the new XHTMLStatic control. Also assign variable name to these controls using ClassWizard. Next, include the header file XHTMLStatic.h in dialog's header file, and replace the CStatic variables with CXHTMLStatic. This shows header file XHTMLStaticTestDlg.h from demo:

C++
#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
    .
    .
    .

Final step is to put some text into XHTMLStatic control. Using the variable you added with ClassWizard, you can say

C++
m_static1.SetWindowText(_T("<b><font size=\"+8\">Moby Dick</font></b>"));

For an example of how to use CXHTMLStatic, see XHTMLStaticTestDlg.cpp.

Revision History

Version 1.4 - 2007 October 19

  • Fixed bug where text could be written outside of control's client rect, reported (with fix) by David Pritchard.
  • Expanded table of character entities.
  • Added VS2005 project.
  • Implemented memory DC to improve performance, suggested by conan.ks.
  • Switched from WM_TIMER to WM_MOUSEMOVE for display of hand cursor, suggested by rm2 and RichardC. This prevents hand cursor from appearing on overlapping windows.

Version 1.3 - 2006 August 15

  • Added transparency support, suggested by Anna.
  • Added support for WM_PRINT, suggested by beaus07.
  • Added support for <center>, requested by several readers.
  • Added support for tooltips for hyperlinks.
  • Load hand cursor from IDC_HAND, suggested by kamnas.
  • Fixed font object leak, reported by furbo.
  • Fixed problem with SetWindowText() reported by Andro67; the background and text colors are no longer reset when SetWindowText() is called.
  • Fixed bug when control is hidden, reported by RichardC.

Version 1.2 - 2004 June 12

  • Changed APP: hyperlink to use HWND instead of GetParent.
  • Added wParam to XHTMLSTATIC_APP_COMMAND struct.
  • Added function SetTextColor(LPCTSTR lpszColor) .
  • Added function SetLogFont(const LOGFONT * pLogFont).
  • Added function SetWindowText() to call Init and RedrawWindow .
  • Fixed bug with XNamedColors in handling of "255,0,0" style.
  • in SetColorFromString().
  • Fixed bug with descenders of large serif fonts.

Version 1.1 - 2004 May 20

  • Implemented SUB tag.
  • Implemented SUP tag.
  • Implemented BIG tag.
  • Implemented SMALL tag.
  • Implemented CODE tag.
  • Implemented HR tag.
  • Implemented APP: hyperlink.
  • Implemented common character entities.
  • Improved parsing performance.
  • Bug fixes.

Version 1.0 - 2002 September 16

  • Initial public release.

Usage

This software is released into the public domain. You are free to use it in any way you like, except that you may not sell this source code. If you modify it or extend it, please to consider posting new code here for everyone to share. This software is provided "as is" with no expressed or implied warranty. I accept no liability for any damage or loss of business that this software may cause.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Hans Dietrich Software
United States United States
I attended St. Michael's College of the University of Toronto, with the intention of becoming a priest. A friend in the University's Computer Science Department got me interested in programming, and I have been hooked ever since.

Recently, I have moved to Los Angeles where I am doing consulting and development work.

For consulting and custom software development, please see www.hdsoft.org.






Comments and Discussions

 
QuestionWonderful code but I am having difficulty trying to add a sigma symbol Pin
georgeo13-Nov-15 3:46
georgeo13-Nov-15 3:46 
QuestionTry adding a "&lt;", the character '<', to a <td> and see what fun happens. Pin
Member 832959818-Sep-12 7:23
Member 832959818-Sep-12 7:23 
GeneralMinor resource leak? (plus fix if necessary) Pin
prcarp31-Mar-10 8:46
prcarp31-Mar-10 8:46 
General<sub>, <small>, <font> sometimes ignored?</font></small></sub> Pin
Rob Bryce24-Sep-09 12:35
Rob Bryce24-Sep-09 12:35 
GeneralRe: , , sometimes ignored? Pin
Hans Dietrich24-Sep-09 14:19
mentorHans Dietrich24-Sep-09 14:19 
GeneralRe: , , sometimes ignored? Pin
Alexandre GRANVAUD28-Sep-09 21:53
Alexandre GRANVAUD28-Sep-09 21:53 
hdsoft.org doesnt respond !(
GeneralRe: , , sometimes ignored? Pin
Hans Dietrich28-Sep-09 22:03
mentorHans Dietrich28-Sep-09 22:03 
Questionsomebody help me? Pin
Martial Spirit4-Jun-09 5:06
Martial Spirit4-Jun-09 5:06 
AnswerRe: somebody help me? Pin
Martial Spirit4-Jun-09 23:50
Martial Spirit4-Jun-09 23:50 
GeneralTooltip window not destroyed Pin
prcarp5-Feb-09 8:20
prcarp5-Feb-09 8:20 
GeneralDoes not handle WM_SIZE message (with fix) Pin
Don M2-Sep-08 9:57
Don M2-Sep-08 9:57 
GeneralRe: Does not handle WM_SIZE message (with fix) Pin
Hans Dietrich2-Sep-08 10:08
mentorHans Dietrich2-Sep-08 10:08 
QuestionPorted to non-MFC? Pin
cstup27-Aug-08 12:12
cstup27-Aug-08 12:12 
GeneralBug when using named system colors (and fix) Pin
Don M19-Mar-08 3:02
Don M19-Mar-08 3:02 
General&lt;B&gt; inside &lt;CENTER&gt; doesn't work Pin
Don M6-Feb-08 4:59
Don M6-Feb-08 4:59 
QuestionIs there a way to use this in a C# VS2005 app? Pin
jpe1-Nov-07 20:32
jpe1-Nov-07 20:32 
GeneralRedraw performance is low Pin
gdier18-Oct-07 15:24
gdier18-Oct-07 15:24 
GeneralFix to stop text being written outside control Pin
David Pritchard19-Jul-06 8:46
David Pritchard19-Jul-06 8:46 
GeneralRe: Fix to stop text being written outside control Pin
theguynewman28-Nov-06 22:27
theguynewman28-Nov-06 22:27 
GeneralRe: Fix to stop text being written outside control Pin
David Pritchard29-Nov-06 10:15
David Pritchard29-Nov-06 10:15 
GeneralRe: Fix to stop text being written outside control Pin
capricose_12-Apr-07 0:43
capricose_12-Apr-07 0:43 
GeneralRe: Fix to stop text being written outside control Pin
David Pritchard13-Apr-07 1:05
David Pritchard13-Apr-07 1:05 
GeneralRe: Fix to stop text being written outside control Pin
capricose_13-Apr-07 1:44
capricose_13-Apr-07 1:44 
GeneralRe: Fix to stop text being written outside control Pin
zhuyali21-Aug-07 18:08
zhuyali21-Aug-07 18:08 
GeneralRe: Fix to stop text being written outside control Pin
David Pritchard9-Jan-08 10:38
David Pritchard9-Jan-08 10:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.