Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I've got a bit of text that I'm trying to display in a list. Some of those pieces of a text contain a hyperlink. I'd like to make the links clickable within the text. I can imagine solutions to this problem, but they sure don't seem pretty.

For instance, I could tear apart the string, splitting it into hyperlinks and non-hyperlinks. Then I could dynamically build a Textblock, adding plain text elements and hyperlink objects as appropriate.

I'm hoping there's a better, preferably something declarative.

Example: "Hey, check out this link: http://mylink.com It's really cool."

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
628 views
Welcome To Ask or Share your Answers For Others

1 Answer

You need something that will parse the Text of the TextBlock and create the all the inline objects at runtime. For this you can either create your own custom control derived from TextBlock or an attached property.

For the parsing, you can search for URLs in the text with a regular expression. I borrowed a regular expression from A good url regular expression? but there are others available on the web, so you can choose the one which works best for you.

In the sample below, I used an attached property. To use it, modify your TextBlock to use NavigateService.Text instead of Text property:

<Window x:Class="DynamicNavigation.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:DynamicNavigation"
    Title="Window1" Height="300" Width="300">
    <StackPanel>
        <!-- Type something here to see it displayed in the TextBlock below -->
        <TextBox x:Name="url"/>

        <!-- Dynamically updates to display the text typed in the TextBox -->
        <TextBlock local:NavigationService.Text="{Binding Text, ElementName=url}" />
    </StackPanel>
</Window>

The code for the attached property is given below:

using System;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;

namespace DynamicNavigation
{
    public static class NavigationService
    {
        // Copied from http://geekswithblogs.net/casualjim/archive/2005/12/01/61722.aspx
        private static readonly Regex RE_URL = new Regex(@"(?#Protocol)(?:(?:ht|f)tp(?:s?)://|~/|/)?(?#Username:Password)(?:w+:w+@)?(?#Subdomains)(?:(?:[-w]+.)+(?#TopLevel Domains)(?:com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum|travel|[a-z]{2}))(?#Port)(?::[d]{1,5})?(?#Directories)(?:(?:(?:/(?:[-w~!$+|.,=]|%[a-fd]{2})+)+|/)+|?|#)?(?#Query)(?:(?:?(?:[-w~!$+|.,*:]|%[a-fd{2}])+=(?:[-w~!$+|.,*:=]|%[a-fd]{2})*)(?:&(?:[-w~!$+|.,*:]|%[a-fd{2}])+=(?:[-w~!$+|.,*:=]|%[a-fd]{2})*)*)*(?#Anchor)(?:#(?:[-w~!$+|.,*:=]|%[a-fd]{2})*)?");

        public static readonly DependencyProperty TextProperty = DependencyProperty.RegisterAttached(
            "Text",
            typeof(string),
            typeof(NavigationService),
            new PropertyMetadata(null, OnTextChanged)
        );

        public static string GetText(DependencyObject d)
        { return d.GetValue(TextProperty) as string; }

        public static void SetText(DependencyObject d, string value)
        { d.SetValue(TextProperty, value); }

        private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var text_block = d as TextBlock;
            if (text_block == null)
                return;

            text_block.Inlines.Clear();

            var new_text = (string)e.NewValue;
            if ( string.IsNullOrEmpty(new_text) )
                return;

            // Find all URLs using a regular expression
            int last_pos = 0;
            foreach (Match match in RE_URL.Matches(new_text))
            {
                // Copy raw string from the last position up to the match
                if (match.Index != last_pos)
                {
                    var raw_text = new_text.Substring(last_pos, match.Index - last_pos);
                    text_block.Inlines.Add(new Run(raw_text));
                }

                // Create a hyperlink for the match
                var link = new Hyperlink(new Run(match.Value))
                {
                    NavigateUri = new Uri(match.Value)
                };
                link.Click += OnUrlClick;

                text_block.Inlines.Add(link);

                // Update the last matched position
                last_pos = match.Index + match.Length;
            }

            // Finally, copy the remainder of the string
            if (last_pos < new_text.Length)
                text_block.Inlines.Add(new Run(new_text.Substring(last_pos)));
        }

        private static void OnUrlClick(object sender, RoutedEventArgs e)
        {
            var link = (Hyperlink)sender;
            // Do something with link.NavigateUri like:
            Process.Start(link.NavigateUri.ToString());
        }
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...