如果将鼠标悬停在超链接上时会触发 Cursor 更改事件,但它不会:(
Jeremy 的评论给了我一个想法:当用户将鼠标悬停在超链接上时,本机 RichTextBox 控件肯定会收到某种类型的通知,它显然没有被 WinForms 包装类公开。
一些研究证实了我的假设。设置为检测超链接的 RichTextBox 控件通过消息向其父级发送EN_LINK
通知。然后,通过处理这些通知,您可以在超链接悬停时覆盖其行为。WM_NOTIFY
EN_LINK
WinForms 包装器在私有代码中处理所有这些,并且不允许客户端对此行为进行任何直接控制。但是通过覆盖父窗口(即您的窗体)的窗口过程 ( WndProc
),您可以手动截取WM_NOTIFY
消息并观察EN_LINK
通知。
它需要一些代码,但它可以工作。例如,如果您禁止WM_SETCURSOR
所有EN_LINK
通知的消息,则根本看不到手形光标。
[StructLayout(LayoutKind.Sequential)]
struct CHARRANGE
{
public int cpMin;
public int cpMax;
};
[StructLayout(LayoutKind.Sequential)]
struct NMHDR
{
public IntPtr hwndFrom;
public IntPtr idFrom;
public int code;
};
[StructLayout(LayoutKind.Sequential)]
struct ENLINK
{
public NMHDR nmhdr;
public int msg;
public IntPtr wParam;
public IntPtr lParam;
public CHARRANGE chrg;
};
public class MyForm : Form
{
// ... other code ...
protected override void WndProc(ref Message m)
{
const int WM_NOTIFY = 0x004E;
const int EN_LINK = 0x070B;
const int WM_SETCURSOR = 0x0020;
if (m.Msg == WM_NOTIFY)
{
NMHDR nmhdr = (NMHDR)m.GetLParam(typeof(NMHDR));
if (nmhdr.code == EN_LINK)
{
ENLINK enlink = (ENLINK)m.GetLParam(typeof(ENLINK));
if (enlink.msg == WM_SETCURSOR)
{
// Set the result to indicate this message has been handled,
// and return without calling the default window procedure.
m.Result = (IntPtr)1;
return;
}
}
}
base.WndProc(ref m);
}
}
不幸的是,这很容易。现在出现了一个丑陋的黑客,我们在其中解决您描述的控件的默认行为,如果最后一行是超链接,它将控件高度的其余部分视为最后一行的一部分。
为此,我们需要获取鼠标指针的当前位置,并将其与控件检测到的超链接文本的位置进行比较。如果鼠标指针在超链接行内,我们允许默认行为并显示手形光标。否则,我们抑制手形光标。请参阅下面的注释代码以获得对该过程的更好解释(显然,rtb
是您的 RichTextBox 控件):
protected override void WndProc(ref Message m)
{
const int WM_NOTIFY = 0x004E;
const int EN_LINK = 0x070B;
const int WM_SETCURSOR = 0x0020;
if (m.Msg == WM_NOTIFY)
{
NMHDR nmhdr = (NMHDR)m.GetLParam(typeof(NMHDR));
if (nmhdr.code == EN_LINK)
{
ENLINK enlink = (ENLINK)m.GetLParam(typeof(ENLINK));
if (enlink.msg == WM_SETCURSOR)
{
// Get the position of the last line of text in the RichTextBox.
Point ptLastLine = rtb.GetPositionFromCharIndex(rtb.TextLength);
// That point was in client coordinates, so convert it to
// screen coordinates so that we can match it against the
// position of the mouse pointer.
ptLastLine = rtb.PointToScreen(ptLastLine);
// Determine the height of a line of text in the RichTextBox.
//
// For this simple demo, it doesn't matter which line we use for
// this since they all use the same size and style. However, you
// cannot generally rely on this being the case.
Size szTextLine = TextRenderer.MeasureText(rtb.Lines[0], rtb.Font);
// Then add that text height to the vertical position of the
// last line of text in the RichTextBox.
ptLastLine.Y += szTextLine.Height;
// Now that we know the maximum height of all lines of text in the
// RichTextBox, we can compare that to the pointer position.
if (Cursor.Position.Y > ptLastLine.Y)
{
// If the mouse pointer is beyond the last line of text,
// do not treat it as a hyperlink.
m.Result = (IntPtr)1;
return;
}
}
}
}
base.WndProc(ref m);
}
经过测试和工作......但我有没有提到这是一个丑陋的黑客?将其更像是概念证明。我当然不建议在生产代码中使用它。我非常同意 Hans 和 Jeremy 的观点,即您应该采用更简单的方法来添加换行符,或者使用更合适的控件来显示超链接。