Create Hyperlink in TextBlock via Binding
Asked Answered
C

3

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?

Cremate answered 1/1, 2015 at 19:49 Comment(0)
I
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

Integrator answered 2/1, 2015 at 13:18 Comment(14)
i get an exceptions, DependencyProperty TextBlockExtension. FormattedText cannot be set on an object of type System.Windows.Controls.TextBlock, any ideas?Savannasavannah
@Savannasavannah Have you made it static? Also have you added the namespace in your xaml?Integrator
@Integrator yes, i non-static as well.Savannasavannah
@Savannasavannah Are you targetting Runtime or Silverlight?Integrator
silverlight. thats the probleme?Savannasavannah
@Savannasavannah Yes, in Silverlight it doesnt work at least with the above code. I don't have solution now, so unfortunately can't help you.Integrator
You saved me hours... Thank you. Solved my problem for windows phone universal app.Asteria
Thank you sir!, I spend 2 hours trying to do this, then i gave up and started searching.Falsework
@Integrator : How do I add namespace in xaml file? can u elaborate please...Broadax
@Broadax 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.Integrator
@Integrator : 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.Broadax
@Broadax It depends which platform you are targeting, if you target UWP then take a look at this answer.Integrator
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.Runstadler
@AzazulHaq It has been a year, but I changed DependencyProperty.Register to DependencyProperty.RegisterAttached to make it work as Attached Property.Alroy
G
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

Garget answered 27/2, 2020 at 17:15 Comment(0)
R
-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;
    }
Rundle answered 1/1, 2015 at 22:19 Comment(1)
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?Cremate

© 2022 - 2024 — McMap. All rights reserved.