An improved hyperlink control

The CHyperlink example project dialog  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.

What's new in this release

This release was compiled and tested with Visual C++ 5.00. Uwe Kleim reported me a bug in the ModifyLinkStyle() member function and now it has been fixed. Thanks Uwe!

Last updated: 11 November 1998