在Winforms RichTextBox控件中,如何使空格BELOW成为不可点击的最后一行链接?

时间:2013-06-21 22:03:03

标签: c# winforms hyperlink richtextbox

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

问题是,链接下面的所有空白区域都是可点击的链接。我知道文本下面的空白空间通常用作窗口中该行的“部分” - 例如,将光标放在此帖子下方,然后单击并拖动 - 您将选择最后一行。但通常这不包括可点击链接。尝试使用这篇文章的标题 - 你可以选择标题,但是你的光标不是可点击的“手”,直到你实际直接标题。

我可以通过将数据更改为始终包含尾随换行符来解决此问题,或者修改我设置框的文本以始终添加一个的点。但这两个看起来都很混乱。有没有办法让RichTextBox的链接更像Web浏览器中的链接?

我可以通过创建示例WinForms应用程序,放入RichTextBox并使用设计器将文本设置为“http://www.google.com”链接将显示手形光标的任何地方来重现此行为。

我正在使用Windows 7 / VS2010 / C#/ .net Framework 4.0

感谢您的建议。

2 个答案:

答案 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非常认同你应该采用更简单的方法来添加换行符,或者使用更合适的控件来显示超链接。