在Windows窗体C#应用程序中,我有许多RichTextBox控件,它们显示一个链接作为文本框的最后一行,之后没有换行符。
问题是,链接下面的所有空白区域都是可点击的链接。我知道文本下面的空白空间通常用作窗口中该行的“部分” - 例如,将光标放在此帖子下方,然后单击并拖动 - 您将选择最后一行。但通常这不包括可点击链接。尝试使用这篇文章的标题 - 你可以选择标题,但是你的光标不是可点击的“手”,直到你实际直接标题。
我可以通过将数据更改为始终包含尾随换行符来解决此问题,或者修改我设置框的文本以始终添加一个的点。但这两个看起来都很混乱。有没有办法让RichTextBox的链接更像Web浏览器中的链接?
我可以通过创建示例WinForms应用程序,放入RichTextBox并使用设计器将文本设置为“http://www.google.com”链接将显示手形光标的任何地方来重现此行为。
我正在使用Windows 7 / VS2010 / C#/ .net Framework 4.0
感谢您的建议。
答案 0 :(得分:3)
此处链接将显示手形光标。
您需要设置换行符以查看链接下方的文本光标而不是手形光标。它的设计。
我可以通过将数据更改为始终包含a来解决此问题 尾随换行符,或修改我正在设置文本的点 总是添加一个框。但这两个看起来都很混乱。没有 使RichTextBox的链接更像是Web中的链接的方式 浏览器?
没有。之后换一个换行符。或者将RichTexbox DetectUrls属性设置为false。或者汉斯提到使用Web浏览器。或者使用第三方或开源RichTextBox控件。
如果将鼠标悬停在超链接上时触发Cursor更改事件会很好,但它不会:(
答案 1 :(得分:2)
如果将鼠标悬停在超链接上时触发Cursor更改事件会很好,但它不会:(
Jeremy的评论给了我一个想法:当用户将鼠标悬停在超链接上时,当然RichTextBox控件确实会收到某种类型的通知,显然WinForms包装类并没有公开它。
一些研究证实了我的假设。设置为检测超链接的RichTextBox控件通过EN_LINK
notification向其父级发送WM_NOTIFY
message。通过处理这些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非常认同你应该采用更简单的方法来添加换行符,或者使用更合适的控件来显示超链接。