在Winforms RichTextBox控件中,我如何使最后一行链接下面的空间不可点击

本文关键字:链接 一行 空间 控件 RichTextBox Winforms 最后 何使 | 更新日期: 2023-09-27 18:19:22

在一个Windows窗体c#应用程序中,我有一些RichTextBox控件,它们将链接显示为文本框的最后一行,之后没有换行符。

问题是,所有的空白,在物理上低于链接将是一个可点击的链接。据我所知,在窗口中,文本下方的空白空间通常是该行的"一部分"——例如,将光标放在这篇文章的正下方,点击并拖动——你将选择最后一行。但通常不包括可点击的链接。试试这篇文章的标题——你可以选择标题,但你的光标不是可点击的"手",直到你真正直接在标题。

我可以通过更改数据以始终包含尾随换行符来解决这个问题,或者修改我设置文本框的位置以始终添加一个换行符。但这两种方法似乎都很混乱。有没有办法使RichTextBox的链接行为更像网页浏览器中的链接?

我可以通过创建一个示例WinForms应用程序来重现这种行为,放入一个RichTextBox,并使用设计器将文本设置为"http://www.google.com"链接下面的任何地方都会显示手部光标。

我使用的是Windows 7/VS2010/c#/.net Framework 4.0

谢谢你的建议

在Winforms RichTextBox控件中,我如何使最后一行链接下面的空间不可点击

链接下方的任何地方都将显示手部光标。

您需要添加一个换行符以看到链接下方的文本光标,而不是手动光标。这是故意的。

我可以通过改变我的数据总是包含a来解决这个问题尾随换行,或修改我设置文本的点盒子里总是加一个。但这两种方法似乎都很混乱。没有吗?让RichTextBox的链接更像网页中的链接浏览器吗?

。在后面加一个换行符。或者将RichTexbox DetectUrls属性设置为false。或者像汉斯提到的,使用网络浏览器。或者使用第三方或开源的RichTextBox控件。

如果光标更改事件在悬停在超链接上时触发,但它没有:(

如果光标更改事件在悬停在超链接上时触发,但它没有:

Jeremy的评论给了我一个想法:当然本地RichTextBox控件确实接收某种类型的通知,当用户将鼠标停留在一个超链接,它显然只是不暴露的WinForms包装类。

一些研究证实了我的假设。设置为检测超链接的RichTextBox控件通过WM_NOTIFY消息向其父控件发送EN_LINK通知。通过处理这些EN_LINK通知,然后,当一个超链接悬停时,你可以覆盖它的行为。

WinForms包装器在私有代码中处理所有这些,不允许客户端对这些行为有任何直接控制。但是,通过重写父窗口(即您的表单)窗口过程(WndProc),您可以手动拦截WM_NOTIFY消息并监视EN_LINK通知。

这需要一些代码,但它可以工作。例如,如果您对所有EN_LINK通知抑制WM_SETCURSOR消息,您将根本看不到手部光标。

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

不幸的是,这是最简单的部分。现在是丑陋的hack,在这里我们围绕您所描述的控件的默认行为工作,如果最后一行是超链接,它将控件高度的其余部分作为最后一行的一部分。

要做到这一点,我们需要获取鼠标指针的当前位置,并将其与控件检测到的超链接文本的位置进行比较。如果鼠标指针位于超链接行内,则允许默认行为并显示手部光标。否则,我们将抑制手部光标。请参阅下面的注释代码,以获得对该过程的更好解释(显然,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);
}

已测试并正常工作& help;但我说过这是一个丑陋的黑客吗?把它看作是概念验证。我当然不建议在生产代码中使用它。我非常赞同Hans和Jeremy的观点,即要么采用添加换行符的简单方法,要么使用设计得更合适的控件来显示超链接。