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






4.88/5 (24投票s)
另一篇描述超链接控件的文章。
引言
这是我对一个(超级)链接控件的实现。 我已经搁置了一段时间,最终决定发布它。 虽然市面上有许多其他的链接控件实现,但我找到的没有一个真正像 Internet Explorer® 中使用的那样。 例如,许多控件都基于 Static
控件,因此无法通过键盘获得焦点,这意味着它们在没有鼠标的情况下是毫无用处的(可访问性问题),并且具有不正确的单击行为。
我的链接控件实现提供了一些在其他实现中不常见的特性
- 它基于(子类化)按钮控件,因此可以像任何其他控件一样通过键盘获得焦点和导航
- 可以通过键盘(使用
<ENTER>
键)激活它 - 它在按钮-抬起时正确激活,而不是在按钮-按下时
- 它包含一个内置的工具提示
- 它在禁用时也能正确绘制和表现
- 它的颜色和字体完全可定制
- 它支持启动 URL 和
SHELLEXECUTEINFO
结构的内容(见下文) - 它可以像 Internet Explorer 中的链接一样进行拖放(尝试从演示应用程序中将其拖放到 IE 或桌面的实例上)
- 它使用
WM_NOTIFY
驱动的通知消息(Win32 方式)来通知父窗口鼠标活动,并赋予父窗口拒绝激活请求或取消拖放操作等能力。
背景
要完全理解此超链接控件实现所提供的优势,请在浏览器中使用此页面上的链接进行尝试。 您能同时使用鼠标和键盘导航它们吗? 您如何用鼠标激活控件;是在按下按钮时还是抬起按钮时? 当您按下按钮但将鼠标移出控件区域然后抬起按钮时会发生什么? 当您在别处按下按钮,然后在控件上方抬起按钮时又会发生什么? 等等。
使用控件
CJRTSLinkCtrl
类的代码封装在 JRTSLinkCtrl.cpp
和 JRTSLinkCtrl.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)