5,427,813 members and growing! (16,141 online)
Email Password   helpLost your password?
Desktop Development » Miscellaneous » General     Intermediate

A Better(?) (Hyper)Link Control

By James R. Twine

An article that describes yet another hyperlink control
VC6, VC7, C++Windows, Win2K, WinXP, MFC, VS6, Visual Studio, Dev

Posted: 11 Jan 2003
Updated: 21 Jan 2003
Views: 75,377
Bookmarked: 32 times
Announcements
Want a new Job?



Search    
Advanced Search
Sitemap
24 votes for this Article.
Popularity: 6.52 Rating: 4.73 out of 5
1 vote, 4.2%
1
0 votes, 0.0%
2
0 votes, 0.0%
3
3 votes, 12.5%
4
20 votes, 83.3%
5

Sample Image - JRTSLinkCtrl.jpg

Introduction

This is my implementation of a (hyper)link control.  I have been sitting on it for a while, and finally decided to release it.  While there are may other implementations of link controls out there, none of the ones that I have found really act like the ones used in Internet Explorer®.  For example, many of them are based on the Static control, and as such are unable to be focused using the keyboard, which means that they are useless without a mouse (accessibility issue), and have incorrect click behavior.

My implementation of the link control offers some features not commonly found in other implementations:

  • It is based on (subclasses) the Button control, so it can be focused and navigated just like any other control
  • It can be activated by the keyboard (using the <ENTER> key)
  • It correctly activates on the Button-Up, not the Button-Down
  • It contains a built-in ToolTip
  • It draws and behaves correctly when disabled
  • Its colors and fonts are completely customizable
  • It supports the ability to launch both URLs and the contents of a SHELLEXECUTEINFO structure (see below)
  • It can be dragged and dropped just like a link in Internet Explorer (try dragging it from the demo application to an instance of IE or the Desktop)
  • It uses WM_NOTIFY-based notification messages (the Win32 way) to notify the parent of mouse activity, and gives the parent the ability to deny an activation request, or cancel a Drag-n-Drop operation, etc.

Background

To fully understand the benefits this implementation of the hyperlink control has to offer, experiment with the links on this page using your browser.  Can you navigate them using the mouse and keyboard?  How do you activate the control with the mouse; on the Button-Down or the Button-Up?  What happens when you Button-Down over the control, but move the mouse off of it and then Button-Up?  What happens when you Button-Down somewhere else, but then Button-Up over the control?  Etc.

Using The Control

The code for the CJRTSLinkCtrl class is wrapped up in the JRTSLinkCtrl.cpp and JRTSLinkCtrl.h files.  The CJRTSLinkCtrl class is used to subclass an existing Button control, just as you would normally use the CButton class.  For best ease of use, rebuild your ClassWizard database and include the code for this class when you do.  That way, you will be able to bind a CJRTSLinkCtrl class from within ClassWizard.

Now, let us explore the class by going through its header file from top to bottom.  We will start with the WM_NOTIFY-based Notification Messages and the NMHDR-Based Notification Structure.

const  UINT  NMLC_DBLCLICK    =  NM_DBLCLK;         // Double-Click Notification Message

const  UINT  NMLC_SETFOCUS    =  NM_SETFOCUS;       // Control Has Gained The Input Focus

const  UINT  NMLC_KILLFOCUS   =  NM_KILLFOCUS;      // Control Has Lost The Input Focus

const  UINT  NMLC_RCLICK      =  NM_RCLICK;         // Right-Click In Control

const  UINT  NMLC_RDBLCLICK   =  NM_RDBLCLICK;      // Double-Right-Click In Control

const  UINT  NMLC_ACTIVATE    =  ( NM_FIRST - 90 ); // Link Activate Notification Message

const  UINT  NMLC_MOUSEENTER  =  ( NM_FIRST - 89 ); // Mouse Enter Link Notification

const  UINT  NMLC_MOUSEHOVER  =  ( NM_FIRST - 88 ); // Mouse Hover Notification

const  UINT  NMLC_MOUSELEAVE  =  ( NM_FIRST - 87 ); // Mouse Leave Notification

const  UINT  NMLC_BEGINDRAG   =  ( NM_FIRST - 86 ); // Begin Drag Operation Notification

const  UINT  NMLC_ENDDRAG     =  ( NM_FIRST - 85 ); // End Drag Operation Notification

const  UINT  NMLC_BEGINRDRAG  =  ( NM_FIRST - 84 ); // Begin RDrag Operation Notification

const  UINT  NMLC_ENDRDRAG    =  ( NM_FIRST - 83 ); // End RDrag Operation Notification

const  UINT  NMLC_MCLICK      =  ( NM_FIRST - 82 ); // MButton Click

const  UINT  NMLC_MDBLCLICK   =  ( NM_FIRST - 81 ); // MButton Double-Click



struct NMLINKCTRL                                   // Link Control's Notification Object

{
    NMHDR m_hdr;                                    // Base NMHDR Object

    DWORD m_dwRetVal;                               // Return Value (Used by NMLC_ACTIVATE)

    DWORD m_dwFlags;                                // Message Flags

    POINT m_ptWhere;                                // Cursor Position (Screen)

};

All of these notification messages are handled using CWnd::OnNotify(...) or by using the ON_NOTIFY(...) message map macros.  The NMLINKCTRL structure is the NMHDR-based structure that is passed in the NMHDR pointer argument to the handler functions.

 
Notification Message Description
NMLC_DBLCLICK This notification message is used to notify the parent that a Double-Click has occurred in the Link Control.
 
NMLC_SETFOCUS This notification message is used to notify the parent that the Link Control has gained the input focus.
 
NMLC_KILLFOCUS This notification message is used to notify the parent that the Link Control has lost the input focus.
 
NMLC_RCLICK This notification message is used to notify the parent that a Right-Click has occurred in the Link Control.
 
NMLC_RDBLCLICK This notification message is used to notify the parent that a Double-Right-Click has occurred in the Link Control
 
NMLC_ACTIVATE This notification message is used to notify the parent that the Link Control is about to be activated.  This also gives the Parent control a last chance to set/change the Target of the link, or to deny activation: upon return of the notification call, if m_dwRetVal is set to FALSE, the link will not be activated.
 
NMLC_MOUSEENTER This notification message is used to notify the parent that the mouse has entered the Link Control's client area.
 
NMLC_MOUSEHOVER This notification message is used to notify the parent that the mouse has hovered over the Link Control's client area.  (Note that this does not use the standard NM_HOVER message.  This is because the standard NM_HOVER notification supports preventing a "hover-action" from taking place, and the Link Control does not support that functionality.)
 
NMLC_MOUSELEAVE This notification message is used to notify the parent that the mouse has left the Link Control's client area
 
NMLC_SETFOCUS This notification message is used to notify the parent that the Link Control has gained the input focus.
 
NMLC_KILLFOCUS This notification message is used to notify the parent that the Link Control has lost the input focus.
 
NMLC_BEGINDRAG This notification message is used to notify the parent that a Drag-n-Drop operation is about to begin.  This also gives the Parent control a last chance to prevent the initiation of the Drag-n-Drop operation: upon return of the notification call, if m_dwRetVal is set to FALSE, the Drag-n-Drop operation will not be initiated.
 
NMLC_ENDDRAG This notification message is used to notify the parent that a Drag-n-Drop Operation has completed.  The  m_dwRetVal member of the NMLINKCTRL structure contains the result of the Drag-n-Drop operation, which will be one of the DROPEFFECT_* values.
 
NMLC_BEGINRDRAG This notification message is used to notify the parent that a Right-Button Drag-n-Drop operation is about to begin.  This also gives the Parent control a last chance to prevent the initiation of the Drag-n-Drop operation: upon return of the notification call, if m_dwRetVal is set to FALSE, the Drag-n-Drop operation will not be initiated.
 
NMLC_ENDRDRAG This notification message is used to notify the parent that a Right-Button Drag-n-Drop Operation has completed.  The  m_dwRetVal member of the NMLINKCTRL structure contains the result of the Drag-n-Drop operation, which will be one of the DROPEFFECT_* values.
 
NMLC_MCLICK This notification message is used to notify the parent that a Middle-Click has occurred in the Link Control.
 
NMLC_MDBLCLICK This notification message is used to notify the parent that a Double-Middle-Click has occurred in the Link Control
 


All of the Notification Messages support the following Flags.  The Flags are used to indicate which usual keyboard modifiers are down at the time that the message was sent.  The Right and Left-specific flags are only supported on platforms that can distinguish between them.  The last three Flags are Left/Right neutral, so when it doubt, use them:

const DWORD LCF_RCTRL        = 0x00000001;  // Right-CTRL Key Flag

const DWORD LCF_LCTRL        = 0x00000002;  // Left-CTRL Key Flag

const DWORD LCF_RSHIFT       = 0x00000004;  // Right-SHIFT Key Flag

const DWORD LCF_LSHIFT       = 0x00000008;  // Left-SHIFT Key Flag

const DWORD LCF_RALT         = 0x00000010;  // Right-ALT Key Flag

const DWORD LCF_LALT         = 0x00000020;  // Left-ALT Key Flag

const DWORD LCF_CTRL         = 0x00000040;  // The/A CTRL Key Flag

const DWORD LCF_SHIFT        = 0x00000080;  // The/A SHIFT Key Flag

const DWORD LCF_ALT          = 0x00000100;  // The/A ALT Key Flag


Next, let us look at the styles supported by the control:

const  DWORD  LCS_HOTTRACK   = 0x00000001;  // HotTracking Style

const  DWORD  LCS_DRAGDROP   = 0x00000002;  // Drag-N-Drop Style

const  DWORD  LCS_VISITED    = 0x00000004;  // Visited Style

const  DWORD  LCS_TOOLTIPS   = 0x00000010;  // ToolTips Style	


 
Style Description
LCS_HOTTRACK This style enables Hot-Tracking for the control.
 
LCS_DRAGDROP This style enables the Drag-n-Drop feature of the control.  Drag-n-Drop operations can only be initiated from the Link Area of the control, not just the Client Area.  That is the tradeoff that I decided on between the behavior of a (checkbox-style) button and an actual Link control.
 
LCS_VISITED This style sets the "Visited" flag for the Link.  "Visited" links are shown in a different color.  Normally, links become "Visited" the first time they are Activated.
 
LCS_TOOLTIPS This style enables the control's built-in ToolTip. 
 

All of the style bits can be turned on or off at any time and will have a more-or-less immediate effect.

There are also other style bits in the header file that are prefixed with LCS_I_.  These are internally used style bits, and cannot be set/reset externally.

Now we come to the fun stuff... The public class members:

// Attributes

public:
void  SetTarget( LPCTSTR cpTarget );                // Set Target As A URL

void  SetTarget( const SHELLEXECUTEINFO &seiSEI );  // Set Target As SEI Information

void  SetDisplay( LPCTSTR cpDisplay );              // Set Display Text

void  SetToolTip( LPCTSTR cpToolTip );              // Set ToolTip Text, If Any

void  SetNormalFont( const LOGFONT &lfNormalFont ); // Set "Normal" Font

void  SetULFont( const LOGFONT &lfULFont );         // Set "Underline"/"HotTracked" Font

DWORD GetLastActivateError( void );                 // Return/Error Code From Last Activate



// Operations

public:
void  Activate( void );                             // Externally Activate The Link

DWORD ModifyLCStyle( DWORD dwRemove, DWORD dwAdd ); // Modify Control's Styles

void SetTarget( LPCTSTR cpTarget )
void SetTarget( const SHELLEXECUTEINFO &seiSEI )

These functions set the Target of the link or the "action to be taken place when the link is Activated".  The first version of the function takes a string in a URL format.  No validation is done on the string (this allows you to have your own internal format/actions).  The second version of the function takes a SHELLEXECUTEINFO object.  When the second version is used, the contents of the passed SHELLEXECUTEINFO object are passed the to ShellExecuteEx(...) function.  This allows you to have the link do just about anything you want when it is Activated.

void SetDisplay( LPCTSTR cpDisplay )

This function sets the Display string of the link.  This is the string that is shown to the user.

void SetToolTip( LPCTSTR cpDisplay )

This function sets the ToolTip for the link.

void SetNormalFont( const LOGFONT &lfNormalFont )

This function sets the "normal" font for the control.  The font can be changed at any time.

void SetULFont( const LOGFONT &lfULFont )

This function sets the "underline" font for the control.  The font can be changed at any time.

DWORD GetLastActivateError( void )

This function returns the error code (if any) from the last time the control was Activated.

void Activate( void )

This function gives the developer a way to externally Activate the link.

DWORD ModifyLCStyle( DWORD dwRemove, DWORD dwAdd )

This function allows the developer to modify the style bits of the control.  It is obviously modeled after a CWnd function of a similar name.

History

    Version 1.0    Initial public release to The Code Project. (~12/30/02)
    Version 1.1    Double-click bug fix and additional features. (~1/13/03)

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

James R. Twine


Mvp
Programming since the age of 10, started professionally at the age of 17. Currently involved in both Client and Server side development on Win32 platforms for MC/HA/FT financial applications. Development experience with Win32, Win16, Linux and other flavors of Unix.

Extensive multithreaded development experience on Windows platforms using the Win32 SDK, and MFC.

Experience with HA/FT n-tiered Client/Server systems as well as GUI apps of varying complexity. Some experience with Game developement.

Having learned that the stuff you can barely get away with doing Client-side apps just does not cut it in the real "Server World", I am amazed how many 'professionals' cannot tell the difference between "works" and "correct" or try to (mis)use VB and/or MFC on server-side development projects, never considering that just because it RUNS, does not mean it runs WELL.

Lastly, I am also a collector of arcade games, and can perform repairs, conversions, etc. Search for my name, you will find me on lots of arcade-related documents and sites.

Sites of interest(?):
http://www.jrtwine.com
http://www.jrtwine.com/jtwine
http://www.signingtime.com
http://www.deletefxpfiles.com
http://www.checkfavorites.com
http://www.coinop.org
Occupation: President
Company: JRTwine Software, LLC
Location: United States United States

Other popular Miscellaneous articles:

Article Top
Sign Up to vote for this article
You must Sign In to use this message board.
FAQ FAQ Noise ToleranceSearch Search Messages 
 Layout  Per page   
 Msgs 1 to 25 of 44 (Total in Forum: 44) (Refresh)FirstPrevNext
Subject  Author Date 
GeneralVery nice!memberradialronnie14:45 17 May '08  
QuestionHeap CorruptionmemberHalloko8:24 28 Jul '07  
AnswerRe: Heap CorruptionmemberJames R. Twine8:59 28 Jul '07  
GeneralRe: Heap CorruptionmemberHalloko10:48 28 Jul '07  
GeneralRe: Heap CorruptionmemberHalloko10:58 28 Jul '07  
GeneralRe: Heap CorruptionmemberHalloko11:09 28 Jul '07  
AnswerRe: Heap Corruption - ** Found and Fixed! **memberJames R. Twine5:37 3 Aug '07  
GeneralSizeToContent missing!memberMichael Mogensen7:31 20 Jan '07  
GeneralPrint and Print PreviewmemberNitin Dangare23:47 3 May '04  
GeneralRe: Print and Print PreviewmemberJames R. Twine16:51 4 May '04  
GeneralRe: Print and Print PreviewmemberNitin Dangare20:03 4 May '04  
GeneralLink Control in CWndsussedward herskovits7:24 9 Jul '03  
GeneralRe: Link Control in CWndmemberJames R. Twine8:08 9 Jul '03  
GeneralBUG Windows XPmemberGreg Ennis6:27 11 May '03  
GeneralRe: BUG Windows XPmemberJames R. Twine13:51 11 May '03  
GeneralRe: BUG Windows XPmemberpermutations3:48 30 Jan '04  
GeneralRe: BUG Windows XPmemberJames R. Twine16:59 30 Jan '04  
Generalwhy reinvent that wheel?memberJaime Olivares4:44 15 May '05  
GeneralRe: why reinvent that wheel?memberJames R. Twine14:59 15 May '05  
GeneralRe: why reinvent that wheel?memberJaime Olivares15:13 15 May '05  
GeneralSome Compile ProblemssussPredrag Manojlovic16:26 13 Mar '03  
GeneralRe: Some Compile Problems