Download Source
Code and Demo
Introduction
After having tried some hyperlink controls, I felt the
necessity to write my own control that was more flexible and had
a more consistent programming interface. My intention was to
create an hyperlink that could be used not only with text but
also with icons and bitmaps, and that supported dynamic
modification of its style and font. Moreover, I wanted that my
control had the look and feel of an usual web page hyperlink. Why
cannot hyperlinks have the input focus? Why cannot I move the
focus from an hyperlink to another using the "tab" key
as if they were normal dialog buttons and activate them pressing
the spacebar?
Well, here is an hyperlink control that makes all these
beautiful things possible. It was realized with the help of some
articles written by Paul DiLascia on MSJ and Stuart Patterson on
Windows Developer’s Journal. It’s rightful to tell that I think
at my control as an improvement of the homonymous control
developed by Chris Maunder, whose work was excellent.
Comparison with existing hyperlinkcontrols
In this section, I summarize the main features that
distinguish my control from other similar controls you can find
in this site. I hope this will help you to decide which code
suits your needs better.
- You can set the hyperlink on any type of static control
(bitmaps and icons above all!). - The hyperlink can get the input focus (a special color is
used to show the focus state) and can be activated with
the spacebar like normal dialog buttons. - The hyperlink maintains the correct aspect when you
change its style, text or font. - Control resizing works correctly even if it has a
nonclient border. - Simplified programming interface.
Using the control
To use the hyperlink control, first create a static control
with your resource editor (text, icon, or bitmap), then give it a
control ID (e.g. IDC_HYPERLINK). If you want your control to be
able to get the focus, you have to set the "tabstop"
style, since for static controls it is not the default. I advice
to set it only with text controls, which can change color when
focused. Then, attach it to a member variable of type CHyperLink
(ClassWizard makes this very simple). Don’t forget to include the
"HyperLink.h" file in your dialog class implementation
file. The member function SetURL() allows to specify the link
URL. However, setting an explicit URL is not obligatory for text
controls: if you specify a caption text, this is taken as URL.
Vice versa, if you don’t specify a caption text, but ‘manually’
set the URL with SetURL(), then the text is set equal to the URL.
For all other types of static controls instead, you have the
responsibility to set the URL using SetURL() (usually in the
OnInitDialog() member function of your dialog class), or
otherwise a debug assertion will occur. The default link colors
are blue, dark cyan, purple and red, respectively for normal,
focused, visited and "cursor over" states.
Customizing the hyperlink
The hyperlink styles are static class constants. They are
shown in the following table
Style | Meaning | Default |
---|---|---|
StyleUnderline | The link is underlined | YES |
StyleAutoSize | The control resizes itself automatically when text or font changes |
YES |
StyleUseHover | The link use the "hand over" color |
NO |
StyleDownClick | The link is activated when mouse button "goes down" |
NO |
StyleGetFocusOnClick | The link gets the input focus when clicked |
NO |
StyleNoHandCursor | The link doesn’t use the default hand cursor |
NO |
StyleNoActiveColor | The link doesn’t change color when activated (i.e. when it get the focus) |
NO |
- To modify link style call the member function
ModifyLinkStyle(dwRemove, dwAdd) where dwRemove
is a bitwise OR of the styles you want to remove and dwAdd
is a bitwise OR of the styles you want to add (strange
but true!).
Setting the StyleAutoSize, the control will
automatically resize itself to fit the size of the caption (even
while it is visible!). The resizing occurs also if you set a new
window caption text or font. Remember that this style applies to
text controls only. For icons and bitmaps, auto-resizing is a
default characteristic, and StyleAutoSize becomes
meaningless. The resizing will honour the SS_CENTERIMAGE,
SS_LEFT, SS_RIGHT and SS_CENTER static control flags. If you
don’t want the style change to be dinamically applied to the
control, ModifyLinkStyle() has a third optional boolean
parameter: just set it to FALSE (the default is TRUE).
- To mark/unmark a link as visited call the SetVisited()
member function.
- To modify link colors, call the SetColors() member
function, in the following way:
SetColors(crLinkColor, crActiveColor, crVisitedColor, crHoverColor);
The last parameter is optional: if not specified the system
default highlight color will be used. Alternatively, fill a
HYPERLINKCOLORS structure (a typedef defined in
"HyperLink.h") and pass it to the overloaded version of
SetColors(). This structure is also used by GetColors() to
retrieve the current link colors.
SetColors() is a static member function: the color change has
effect on all instances of CHyperLink and you need to redraw your
control in order that the new colors to be applied.
- To set a mouse cursor different from the default hand,
call SetLinkCursor(). This function requires a unique
argument: the handle of a new cursor. It is a static
function and has effect on all hyperlinks, just like
SetColors().
All the "set" functions above have their natural
"get" counterparts. See the following section.
Operations for CHyperLink:
Here is an excerpt from the class declaration
// Operations public: static void GetColors(HYPERLINKCOLORS& linkColors); static HCURSOR GetLinkCursor(); static void SetLinkCursor(HCURSOR hCursor); static void SetColors(COLORREF crLinkColor, COLORREF crActiveColor, COLORREF crVisitedColor, COLORREF crHoverColor = -1); static void SetColors(HYPERLINKCOLORS& colors); void SetURL(CString strURL); CString GetURL() const; DWORD GetLinkStyle() const; BOOL ModifyLinkStyle(DWORD dwRemove, DWORD dwAdd, BOOL bApply=TRUE); void SetWindowText(LPCTSTR lpszText); void SetFont(CFont *pFont); BOOL IsVisited() const; void SetVisited(BOOL bVisited = TRUE); // Use this if you want to subclass and also set different URL BOOL SubclassDlgItem(UINT nID, CWnd* pParent, LPCTSTR lpszURL=NULL) { m_strURL = lpszURL; return CStatic::SubclassDlgItem(nID, pParent); }
Implementation details
Some notes about implementation. I used the MFC message
reflection to set the text color for the control (see the
ON_WM_CTLCOLOR_REFLECT() macro in the message map). Message
reflection can be a very complicated argument. Briefly, many
controls notifies some event messages to their parent window, so
that the latter has a chance to handle them. When the parent
window sends back the message to the control we have the
so-called message reflection. This mechanism is well implemented
in MFC. Reflected messages appear preceded by an equal sign in
ClassWizard.
Reading the code, you may be surprised that the control works
without the SS_NOTIFY style. Normally, a static control does not
get mouse events unless it has the SS_NOTIFY style. However, as
Paul DiLascia asserts, handling the WM_NCHITTEST message and
making the OnNcHitTest() functions returns the hit-test value
HTCLIENT achieves the same effect as SS_NOTIFY, but requires
fewer lines of code and is more reliable than turning on
SS_NOTIFY in OnCtlColor because Windows doesn’t send WM_CTLCOLOR
to bitmap static controls.
For dynamic text and font modification I supplied special
versions of SetFont() and SetWindowText() functions. These
functions perform their operations while the window is kept
hidden to maintain the correct link aspect.
Author’s note
I’m continuously working to improve this control. I’ll be
grateful to you if you mail me your comments, advices, or bug
apparition reports!.
Latest additions
- Link styles are now static class constants.
- Colors and cursor and their relative get/set functions,
are now static class members. - Many bug fixes in resizing and color management.