Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hyperlinks without underline in RichTextBox on Windows 10 1803

I'm displaying RTF document in RichTextBox ("upgraded" to RichEdit50W). Keywords in the document are linked to a webpage using a syntax:

{\field{\*\fldinst{HYPERLINK ""https://www.example.com/"" }}{\fldrslt{\cf1 keyword\cf0 }}}

I do not want to underline the keywords. Until Windows 10 version 1803 (and in all previous versions of Windows, including XP, Vista, 8), whenever a color was set on the anchor (note the \cf1), the anchor was not underlined.

But this no longer works in Windows 10 version 1803. I'm going to report this to Microsoft. But I'm not really sure, if I was not relying on an undocumented behavior. I can imagine that this change is actually not a bug, but rather a fix. So I wonder whether there is not a more correct way to prevent hyperlinks from being underlined.

Sample code:

public class ExRichText : RichTextBox
{
    [DllImport("kernel32.dll", EntryPoint = "LoadLibraryW", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern IntPtr LoadLibraryW(string path);

    protected override CreateParams CreateParams
    {
        get
        {
            var cp = base.CreateParams;
            LoadLibraryW("MsftEdit.dll");
            cp.ClassName = "RichEdit50W";
            return cp;
        }
    }
}

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        ExRichText rtb = new ExRichText();
        rtb.Parent = this;
        rtb.SetBounds(10, 10, 200, 100);
        rtb.Rtf = @"{\rtf1 {\colortbl ;\red255\green0\blue0;}bar {\field{\*\fldinst{HYPERLINK ""https://www.example.com/"" }}{\fldrslt{\cf1 link\cf0 }}} bar}";
    }
}

(unwanted) result on Windows 10 version 1803:

enter image description here

(desired) result on Windows 10 version 1706:

enter image description here

and the same result on Windows 7:

enter image description here

like image 509
Martin Prikryl Avatar asked May 17 '18 11:05

Martin Prikryl


1 Answers

For Windows 8 and above, you can use the SendMessage function to send the EM_SETEDITSTYLEEX message to richedit control to disable the underlining of friendly links by specifying SES_EX_HANDLEFRIENDLYURL for the lParam argument and zero for the wParam` argument.

SES_EX_HANDLEFRIENDLYURL

Display friendly name links with the same text color and underlining as automatic links, provided that temporary formatting isn’t used or uses text autocolor (default: 0).

Even though the underlining is supposedly disabled by default, the RichTextBox control has it enabled.

Add the following to your ExRichText class to provide a method (UnderlineFriendlyLink) to disable the underlining.

private const Int32 WM_User = 0x400;
private const Int32 EM_SETEDITSTYLEEX = WM_User + 275;
private const Int32 EM_GETEDITSTYLEEX = WM_User + 276;

/// <summary>Display friendly name links with the same text color and underlining as automatic links, provided that temporary formatting isn’t used or uses text autocolor (default: 0)</summary>
private const Int32 SES_EX_HANDLEFRIENDLYURL = 0x100;

[DllImport("user32.dll", CharSet = CharSet.Auto)]
private extern static Int32 SendMessage(HandleRef hWnd, Int32 msg, Int32 wParam, Int32 lParam);

public static void UnderlineFriendlyLink(RichTextBox rtb, bool enabled = false)
{
    if (rtb.IsHandleCreated)
    {
        Int32 wParam = enabled ? SES_EX_HANDLEFRIENDLYURL : 0;
        Int32 lParam = SES_EX_HANDLEFRIENDLYURL; // settings mask
        Int32 res = SendMessage(new HandleRef(null, rtb.Handle), EM_SETEDITSTYLEEX, wParam, lParam);
    }
}

Example usage:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        exRichText1.Rtf = @"{\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1033{\fonttbl{\f0\fnil\fcharset0 Microsoft Sans Serif;}}{\colortbl ;\red0\green0\blue255;}{\*\generator Riched20 10.0.16299}\viewkind4\uc1 \pard\f0\fs29 Hello {\b{\field{\*\fldinst{HYPERLINK ""http://www.fred.com""}}{\fldrslt{Link}}}}\f0\fs29\par\par https://www.google.com \par\par sd {{\field{\*\fldinst{HYPERLINK ""http://www.fred.com""}}{\fldrslt{klasl}}}}\f0\fs29  wed asdasd \par\par}";
    }

    private void exRichText1_LinkClicked(object sender, LinkClickedEventArgs e)
    {
        System.Diagnostics.Debug.Print(e.LinkText);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        ExRichText.UnderlineFriendlyLink(exRichText1, false);
    }
}

Your post did not indicate how you are detecting the clicking of the links, but be advised that if you are relying on the LinkClicked event as shown in the above example that event may not fire due to a logic bug in the RichTextBox CharRangeToString method. In particular this code fragment.

        //Windows bug: 64-bit windows returns a bad range for us.  VSWhidbey 504502.  
        //Putting in a hack to avoid an unhandled exception.
        if (c.cpMax > Text.Length || c.cpMax-c.cpMin <= 0) {
            return string.Empty;
        }

If you try the sample code, you will notice that the event fires only for the first link. If CharRangeToString returns String.Empty, the event is not raised. This limiting condition uses the Text.Length property (58 for the example) that returns the as displayed length. I believe that it should instead use the TextLength property (120 for the example).

By monitoring the control's parent for the EM_Notify message and processing the mouse click notification, it is possible to extract the link using the CharRange structure when compiling for x86 or AnyCPU(prefer 32-bit). When running as a 64-bit assembly, the CharRange structure does return invalid values as indicated in the source code.


Edit: All testing was done on Windows 10 version 1706 as I will not install 1803 at this time.

like image 181
TnTinMn Avatar answered Nov 18 '22 22:11

TnTinMn