65.9K
CodeProject 正在变化。 阅读更多。
Home

一个更好的 (?) (超级)链接控件

2003 年 1 月 12 日

CPOL

8分钟阅读

viewsIcon

156450

downloadIcon

2110

另一篇描述超链接控件的文章。

Sample Image - JRTSLinkCtrl.jpg

引言

这是我对一个(超级)链接控件的实现。  我已经搁置了一段时间,最终决定发布它。  虽然市面上有许多其他的链接控件实现,但我找到的没有一个真正像 Internet Explorer® 中使用的那样。  例如,许多控件都基于 Static 控件,因此无法通过键盘获得焦点,这意味着它们在没有鼠标的情况下是毫无用处的(可访问性问题),并且具有不正确的单击行为。

我的链接控件实现提供了一些在其他实现中不常见的特性

  • 它基于(子类化)按钮控件,因此可以像任何其他控件一样通过键盘获得焦点和导航
  • 可以通过键盘(使用 <ENTER> 键)激活它
  • 它在按钮-抬起时正确激活,而不是在按钮-按下
  • 它包含一个内置的工具提示
  • 它在禁用时也能正确绘制和表现
  • 它的颜色和字体完全可定制
  • 它支持启动 URL 和 SHELLEXECUTEINFO 结构的内容(见下文)
  • 它可以像 Internet Explorer 中的链接一样进行拖放(尝试从演示应用程序中将其拖放到 IE 或桌面的实例上)
  • 它使用 WM_NOTIFY 驱动的通知消息(Win32 方式)来通知父窗口鼠标活动,并赋予父窗口拒绝激活请求或取消拖放操作等能力。

背景

要完全理解此超链接控件实现所提供的优势,请在浏览器中使用此页面上的链接进行尝试。  您能同时使用鼠标键盘导航它们吗?  您如何用鼠标激活控件;是在按下按钮时还是抬起按钮时?  当您按下按钮但将鼠标移出控件区域然后抬起按钮时会发生什么?  当您在别处按下按钮,然后在控件上方抬起按钮时又会发生什么?  等等。

使用控件

CJRTSLinkCtrl 类的代码封装在 JRTSLinkCtrl.cppJRTSLinkCtrl.h 文件中。  CJRTSLinkCtrl 类用于子类化现有的按钮控件,就像您通常使用 CButton 类一样。  为了方便使用,请重建您的ClassWizard 数据库,并在其中包含此类代码。  这样,您就可以在ClassWizard 中绑定 CJRTSLinkCtrl 类。

现在,让我们从头到尾遍历其头文件来探索该类。  我们将从 WM_NOTIFY 驱动的通知消息和基于 NMHDR的通知结构开始。

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)
};

所有这些通知消息都通过 CWnd::OnNotify(...) 或使用 ON_NOTIFY(...) 消息映射宏进行处理。  NMLINKCTRL 结构是传递给处理函数 NMHDR 指针参数的基于 NMHDR 的结构。

 

通知消息 描述
NMLC_DBLCLICK 此通知消息用于通知父窗口在链接控件中发生了双击事件。
 
NMLC_SETFOCUS 此通知消息用于通知父窗口链接控件已获得输入焦点。
 
NMLC_KILLFOCUS 此通知消息用于通知父窗口链接控件已失去输入焦点。
 
NMLC_RCLICK 此通知消息用于通知父窗口在链接控件中发生了右键单击事件。
 
NMLC_RDBLCLICK 此通知消息用于通知父窗口在链接控件中发生了双击右键单击事件。
 
NMLC_ACTIVATE 此通知消息用于通知父窗口链接控件即将被激活。  这也为父控件提供了最后一次机会来设置/更改链接的目标,或者拒绝激活:在通知调用返回时,如果 m_dwRetVal 设置为 FALSE,则链接将不会被激活。
 
NMLC_MOUSEENTER 此通知消息用于通知父窗口鼠标已进入链接控件的客户端区域。
 
NMLC_MOUSEHOVER 此通知消息用于通知父窗口鼠标已悬停在链接控件的客户端区域。  (请注意,这不使用标准的 NM_HOVER 消息。  这是因为标准的 NM_HOVER 通知支持阻止“悬停操作”的发生,而链接控件不支持该功能。)
 
NMLC_MOUSELEAVE 此通知消息用于通知父窗口鼠标已离开链接控件的客户端区域。
 
NMLC_SETFOCUS 此通知消息用于通知父窗口链接控件已获得输入焦点。
 
NMLC_KILLFOCUS 此通知消息用于通知父窗口链接控件已失去输入焦点。
 
NMLC_BEGINDRAG 此通知消息用于通知父窗口即将开始拖放操作。  这也为父控件提供了最后一次机会来阻止拖放操作的启动:在通知调用返回时,如果 m_dwRetVal 设置为 FALSE,则拖放操作将不会被启动。
 
NMLC_ENDDRAG 此通知消息用于通知父窗口拖放操作已完成。  NMLINKCTRL 结构中的 m_dwRetVal 成员包含拖放操作的结果,这将是 DROPEFFECT_* 值之一。
 
NMLC_BEGINRDRAG 此通知消息用于通知父窗口即将开始右键拖放操作。  这也为父控件提供了最后一次机会来阻止拖放操作的启动:在通知调用返回时,如果 m_dwRetVal 设置为 FALSE,则拖放操作将不会被启动。
 
NMLC_ENDRDRAG 此通知消息用于通知父窗口右键拖放操作已完成。  NMLINKCTRL 结构中的 m_dwRetVal 成员包含拖放操作的结果,这将是 DROPEFFECT_* 值之一。
 
NMLC_MCLICK 此通知消息用于通知父窗口在链接控件中发生了中间键单击事件。
 
NMLC_MDBLCLICK 此通知消息用于通知父窗口在链接控件中发生了双击中间键单击事件。
 


所有通知消息都支持以下标志。  这些标志用于指示发送消息时按下了哪些常规键盘修饰键。  左右专用标志仅在能够区分它们的平台上受支持。  最后三个标志是左右中性的,因此如有疑问,请使用它们。

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


接下来,让我们看看控件支持的样式

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	


 

样式 描述
LCS_HOTTRACK 此样式为控件启用热跟踪功能。
 
LCS_DRAGDROP 此样式启用控件的拖放功能。  拖放操作只能从控件的链接区域启动,而不能仅从客户端区域启动。  这是我在(复选框样式)按钮的行为与实际链接控件的行为之间做出的权衡。
 
LCS_VISITED 此样式为链接设置“已访问”标志。  “已访问”链接显示为不同的颜色。  通常,链接在首次激活时变为“已访问”。
 
LCS_TOOLTIPS 此样式启用控件的内置工具提示。
 

所有样式位都可以随时打开或关闭,并且会产生或多或少即时的效果。

头文件中还有其他以 LCS_I_ 为前缀的样式位。  这些是内部使用的样式位,不能在外部设置/重置。

现在我们来看有趣的部分……公共类成员

// 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 )

这些函数设置链接的目标或“链接被激活时要执行的操作”。  第一个版本的函数接受 URL 格式的字符串。  对字符串不进行任何验证(这允许您拥有自己的内部格式/操作)。  第二个版本的函数接受一个 SHELLEXECUTEINFO 对象。  当使用第二个版本时,传入的 SHELLEXECUTEINFO 对象的内容将被传递给 ShellExecuteEx(...) 函数。  这允许您在激活链接时执行任何您想要的操作。

void SetDisplay( LPCTSTR cpDisplay )

此函数设置链接的显示字符串。  这是显示给用户的字符串。

void SetToolTip( LPCTSTR cpDisplay )

此函数设置链接的工具提示。

void SetNormalFont( const LOGFONT &lfNormalFont )

此函数设置控件的“常规”字体。  字体可以随时更改。

void SetULFont( const LOGFONT &lfULFont )

此函数设置控件的“下划线”字体。  字体可以随时更改。

DWORD GetLastActivateError( void )

此函数返回控件上次激活时(如果有)的错误代码。

void Activate( void )

此函数为开发人员提供了一种外部激活链接的方式。

DWORD ModifyLCStyle( DWORD dwRemove, DWORD dwAdd )

此函数允许开发人员修改控件的样式位。  它显然是模仿了具有相似名称的 CWnd 函数。

历史

    版本 1.0    首次公开发布给 The Code Project。 (~12/30/02)
    版本 1.1    双击 bug 修复和附加功能。 (~1/13/03)

© . All rights reserved.