7

My problem is to find the urls from the text content and convert it into the clickable hyperlinks via data binding.

This is what I've tried

 <TextBlock Tag="{Binding message}" x:Name="postDescription" TextWrapping="Wrap" 
  Grid.Row="3" Grid.ColumnSpan="3" Margin="10,10,10,12" FontSize="16" 
  TextAlignment="Justify" Foreground="{StaticResource foreGroundWhite}" >
    <Run Text="{Binding description, Converter={StaticResource statusFormatter}}" />
  </TextBlock>

In code,

public class StatusFormatter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, string language)
        {
            return returnTextWithUrl((String)value);
        }

        public static String returnTextWithUrl(String text)
        {
            if(text == null) { return null;  }
            MatchCollection mactches = uriFindRegex.Matches(text);

            foreach (Match match in mactches)
            {
                //Need Help here
                HyperlinkButton hyperlink = new HyperlinkButton();
                hyperlink.Content = match.Value;
                hyperlink.NavigateUri = new Uri(match.Value);
                text = text.Replace(match.Value, ??);
            }
            return text;
        }
}
}

The output should be something like this

<TextBlock Tag="{Binding message}" x:Name="postDescription" TextWrapping="Wrap" 
      Grid.Row="3" Grid.ColumnSpan="3" Margin="10,10,10,12" FontSize="16" 
      TextAlignment="Justify" Foreground="{StaticResource foreGroundWhite}" >
        Click this link -
        <Hyperlink NavigateUri="http://www.bing.com">bing</Hyperlink>
        - for more info.
      </TextBlock>

Any Help?

MohanRajNK
  • 895
  • 2
  • 13
  • 26

3 Answers3

14

To do what you want you will have to use Inlines property of your TextBlock, but as it's not a DependencyProperty, it cannot be a target of binding. We will have to extend your TextBlock class, but as it's sealed we will have to use other class.

Lets define static class, which will add apropriate Inline - Hyperlink or Run, depending on Regex match. It can look for example like this:

public static class TextBlockExtension
{
    public static string GetFormattedText(DependencyObject obj)
    { return (string)obj.GetValue(FormattedTextProperty); }

    public static void SetFormattedText(DependencyObject obj, string value)
    { obj.SetValue(FormattedTextProperty, value); }

    public static readonly DependencyProperty FormattedTextProperty =
        DependencyProperty.Register("FormattedText", typeof(string), typeof(TextBlockExtension),
        new PropertyMetadata(string.Empty, (sender, e) =>
        {
            string text = e.NewValue as string;
            var textBl = sender as TextBlock;
            if (textBl != null)
            {
                textBl.Inlines.Clear();
                Regex regx = new Regex(@"(http://[^\s]+)", RegexOptions.IgnoreCase);
                var str = regx.Split(text);
                for (int i = 0; i < str.Length; i++)
                    if (i % 2 == 0)
                        textBl.Inlines.Add(new Run { Text = str[i] });
                    else
                    {
                        Hyperlink link = new Hyperlink { NavigateUri = new Uri(str[i]), Foreground = Application.Current.Resources["PhoneAccentBrush"] as SolidColorBrush };
                        link.Inlines.Add(new Run { Text = str[i] });
                        textBl.Inlines.Add(link);
                    }                        
            }
        }));
}

Then in XAML we use it just like this:

<TextBlock local:TextBlockExtension.FormattedText="{Binding MyText}" FontSize="15"/>

And after putting some text to my property:

private void firstBtn_Click(object sender, RoutedEventArgs e)
{
    MyText = @"Simple text with http://mywebsite.com link";
}

I can see such a result:

SampleLink

Romasz
  • 29,662
  • 13
  • 79
  • 154
  • i get an exceptions, DependencyProperty TextBlockExtension. FormattedText cannot be set on an object of type System.Windows.Controls.TextBlock, any ideas? – esskar Mar 30 '15 at 20:26
  • @esskar Have you made it static? Also have you added the namespace in your xaml? – Romasz Mar 30 '15 at 20:29
  • @Romasz yes, i non-static as well. – esskar Mar 30 '15 at 20:32
  • @esskar Are you targetting *Runtime* or *Silverlight*? – Romasz Mar 30 '15 at 20:33
  • silverlight. thats the probleme? – esskar Mar 30 '15 at 20:45
  • @esskar Yes, in Silverlight it doesnt work at least with the above code. I don't have solution now, so unfortunately can't help you. – Romasz Mar 30 '15 at 21:18
  • 1
    You saved me hours... Thank you. Solved my problem for windows phone universal app. – jamdownian Jun 14 '15 at 21:36
  • Thank you sir!, I spend 2 hours trying to do this, then i gave up and started searching. – Stamos Mar 05 '16 at 15:04
  • @Romasz : How do I add namespace in xaml file? can u elaborate please... – Mak13 Dec 13 '16 at 14:41
  • @Mak13 You add it like this: `xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"` - you have a sample in each page at the very top. – Romasz Dec 13 '16 at 15:10
  • @Romasz : I could manage to run the code on simulator but only when i remove this code "Foreground = Application.Current.Resources["PhoneAccentBrush"] as SolidColorBrush". and plz suggest a change in code if I want to support http and https as well. – Mak13 Dec 14 '16 at 14:28
  • @Mak13 It depends which platform you are targeting, if you target UWP then take a look at [this answer](http://stackoverflow.com/a/40804076/2681948). – Romasz Dec 14 '16 at 15:57
  • Anyone tried this solution with WPF (not Silverlight or UWP) project? The snippet doesn't throw any exception but it also doesn't set the value of TextBlock, and remain it set to empty. – Azaz ul Haq Jul 12 '17 at 12:23
  • 2
    @AzazulHaq It has been a year, but I changed `DependencyProperty.Register` to `DependencyProperty.RegisterAttached` to make it work as Attached Property. – kurakura88 Jun 08 '18 at 09:55
1

I stumbled on this post while looking for the same for UWP. In case you are here too for the same, I'd recommend you use a HyperlinkButton instead of a Hyperlink wrapped in a Textblock. Below is the code on how to use it.

<HyperlinkButton Content="{x:Bind Text}" NavigateUri="{x:Bind Hyperlink}"/>

You can also use Binding instead of x:Bind and yes you can set the Mode=OneWay too.

Read More on Microsoft Docs

iam.Carrot
  • 4,976
  • 2
  • 24
  • 71
-1

You can't put Hyperlink objects inside a String. Instead you need to return a Span containing inlines from your converter. The plain text will be Run objects and the links will be Hyperlink objects.

    public static Span returnTextWithUrl(String text)
    {
        if(text == null) { return null;  }
        var span = new Span();
        MatchCollection mactches = uriFindRegex.Matches(text);
        int lastIndex = 0;
        foreach (Match match in mactches)
        {
            var run = new Run(text.Substring(lastIndex, match.Index - lastIndex));
            span.Inlines.Add(run);
            lastIndex = match.Index + match.Length;
            var hyperlink = new Hyperlink();
            hyperlink.Content = match.Value;
            hyperlink.NavigateUri = new Uri(match.Value);
            span.Inlines.Add(hyperlink);
        }
        span.Inlines.Add(new Run(text.Substring(lastIndex)));
        return span;
    }
Dave M
  • 2,863
  • 1
  • 22
  • 17
  • It seems that I can't bind span to Run text property in WinRT. It displays the content as Windows.UI.Xaml.Documents.Span. Any otherway to achieve this? – MohanRajNK Jan 02 '15 at 04:46