0

I have seen several posts on various forms varying in times from 2006 to now about how to add hyperlinks to RichTextBox, but they all seem overly complex for what I want. I am creating a desktop chat client, and I receive input as strings, now among these strings may be some urls, I need those urls to be clickable. Which from what I gather means they need to be HyperLink objects.

Navigating through the RichTextBox and replacing the urls with HyperLinks seems to be no small feat. Does anyone have a relatively simple solution for this?

In my web client it's a simple one liner

value = value.replace(/(http:\/\/[^\s]+)/gi, '<a href="$1" target="_blank">$1</a>');

Never thought I'd see the day where C# actually makes it harder.

Nathan
  • 229
  • 5
  • 14
  • Just for clarity, is the RichTextBox you're concerned about the source of the user's input? Or are you concerned about the RichTextBox that will be the part that shows the full conversation? Or both? – Rowbear Jan 15 '16 at 19:32
  • @Rowbear I was mostly concerned about displaying the conversation with hyperlinks, having them clickable in the input richtextbox would be an added bonus but necessarily a requirement. – Nathan Jan 15 '16 at 20:37

1 Answers1

0

If you want to do an equivalent of value.replace(/(http:\/\/[^\s]+)/gi, '<a href="$1" target="_blank">$1</a>') in WPF:

<RichTextBox x:Name="MyRichTextBox" IsDocumentEnabled="True" IsReadOnly="True" />

And the code that converts the string is the following:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        var htmlText = "Google's website is http://www.google.com";
        MyRichTextBox.Document = ConvertToFlowDocument(htmlText);
    }

    private FlowDocument ConvertToFlowDocument(string text)
    {
        var flowDocument = new FlowDocument();

        var regex = new Regex(@"(http:\/\/[^\s]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
        var matches = regex.Matches(text).Cast<Match>().Select(m => m.Value).ToList();

        var paragraph = new Paragraph();
        flowDocument.Blocks.Add(paragraph);

        foreach (var segment in regex.Split(text))
        {
            if (matches.Contains(segment))
            {
                var hyperlink = new Hyperlink(new Run(segment))
                {
                    NavigateUri = new Uri(segment),
                };
                hyperlink.RequestNavigate += (sender, args) => Process.Start(segment);

                paragraph.Inlines.Add(hyperlink);
            }
            else
            {
                paragraph.Inlines.Add(new Run(segment));
            }
        }

        return flowDocument;
    }
}

It uses the same regular expression you provided, which is lacking if you properly want to recognize URLs with a regular expression. This one doesn't recognize https ones and the last dot in the following sentence would be a part of the URL: "This is a URL: http://www.google.com/."

What the code does is to split the text based on the regular expression, iterate it and adds the correct elements to the FlowDocument constructed on the fly.

Clicking the Hyperlink should open your default browser.

Result:

Result

That said, this is only good for read only usage of the RichTextBox (as indicated by the question in the comment).

Szabolcs Dézsi
  • 8,743
  • 21
  • 29
  • I have a separate regex for my web client that does https, basically the same exact thing but with https instead of http. Yes, this will be for a read only context as I just need it clickable once received not in sending necessarily. I will give this a try, thank you for your response though, I'll let you know how it goes :) – Nathan Jan 15 '16 at 20:28
  • Your solution was just shy of perfect! I added a param to accept a flow document so I can append to the existing flowdocument in the RichTextBox. Thank you very much, I'm new to WPF, and definitely never done anything with FlowDocuments. PS. I added a s? to my regex so it will recognize https, if someone leaves a dot at the end of a url thats their fault – Nathan Jan 15 '16 at 20:50