Add hyperlink to wpf text block

Hi, I have text in db and it looks like this:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. DUIs tellus nisl, venenatis et pharetra ac, tempor sed sapien. integer pellentesque blandit velit, in tempus urna semper sit amet. DUIs mollis, libero ut consectetur interdum, massa tellus posuere nisi, eu aliquet elit lacus nec erat. Imagine a comm cam. ** [a href = ' http://somesite.com '] some site [/ a] ** Suspendisse at nisi sit amet massa molestie gravida feugiat ac sem. Phasellus ac mauris ipsum, vel auctor odio

My question is: how can I display Hyperlink in TextBlock ? For this purpose, I do not want to use the webBrowser control. I also do not want to use this control: http://www.codeproject.com/KB/WPF/htmltextblock.aspx also

+31
source share
3 answers

In this situation, you can use Regex with a value converter.

Use this for your requirements (original idea from here ):

  private Regex regex = new Regex(@"\[a\s+href='(?<link>[^']+)'\](?<text>.*?)\[/a\]", RegexOptions.Compiled); 

This will match all links in your line containing links, and make 2 named groups for each match: link and text

Now you can iterate over all matches. Each match will give you

  foreach (Match match in regex.Matches(stringContainingLinks)) { string link = match.Groups["link"].Value; int link_start = match.Groups["link"].Index; int link_end = match.Groups["link"].Index + link.Length; string text = match.Groups["text"].Value; int text_start = match.Groups["text"].Index; int text_end = match.Groups["text"].Index + text.Length; // do whatever you want with stringContainingLinks. // In particular, remove whole `match` ie [a href='...']...[/a] // and instead put HyperLink with `NavigateUri = link` and // `Inlines.Add(text)` // See the answer by Stanislav Kniazev for how to do this } 

Note. use this logic in your custom ConvertToHyperlinkedText value ConvertToHyperlinkedText .

+16
source

The display is pretty simple, navigation is another issue. XAML is as follows:

 <TextBlock Name="TextBlockWithHyperlink"> Some text <Hyperlink NavigateUri="http://somesite.com" RequestNavigate="Hyperlink_RequestNavigate"> some site </Hyperlink> some more text </TextBlock> 

And the event handler that starts the default browser to go to your hyperlink will be as follows:

 private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e) { System.Diagnostics.Process.Start(e.Uri.ToString()); } 

Edit: To do this with the text you received from the database, you will have to parse the text somehow. After you recognize the text parts and hyperlinks, you can dynamically create the contents of the text block in the code:

 TextBlockWithHyperlink.Inlines.Clear(); TextBlockWithHyperlink.Inlines.Add("Some text "); Hyperlink hyperLink = new Hyperlink() { NavigateUri = new Uri("http://somesite.com") }; hyperLink.Inlines.Add("some site"); hyperLink.RequestNavigate += Hyperlink_RequestNavigate; TextBlockWithHyperlink.Inlines.Add(hyperLink); TextBlockWithHyperlink.Inlines.Add(" Some more text"); 
+86
source

Another version of this is not quite the same as format recognition here, but here is a class for automatically recognizing links in a piece of text and turning them into live hyperlinks:

 internal class TextBlockExt { static Regex _regex = new Regex(@"http[s]?://[^\s-]+", RegexOptions.Compiled); public static readonly DependencyProperty FormattedTextProperty = DependencyProperty.RegisterAttached("FormattedText", typeof(string), typeof(TextBlockExt), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsMeasure, FormattedTextPropertyChanged)); public static void SetFormattedText(DependencyObject textBlock, string value) { textBlock.SetValue(FormattedTextProperty, value); } public static string GetFormattedText(DependencyObject textBlock) { return (string)textBlock.GetValue(FormattedTextProperty); } static void FormattedTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (!(d is TextBlock textBlock)) return; var formattedText = (string)e.NewValue ?? string.Empty; string fullText = $"<Span xml:space=\"preserve\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">{formattedText}</Span>"; textBlock.Inlines.Clear(); using (var xmlReader1 = XmlReader.Create(new StringReader(fullText))) { try { var result = (Span)XamlReader.Load(xmlReader1); RecognizeHyperlinks(result); textBlock.Inlines.Add(result); } catch { formattedText = System.Security.SecurityElement.Escape(formattedText); fullText = $"<Span xml:space=\"preserve\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">{formattedText}</Span>"; using (var xmlReader2 = XmlReader.Create(new StringReader(fullText))) { try { dynamic result = (Span) XamlReader.Load(xmlReader2); textBlock.Inlines.Add(result); } catch { //ignored } } } } } static void RecognizeHyperlinks(Inline originalInline) { if (!(originalInline is Span span)) return; var replacements = new Dictionary<Inline, List<Inline>>(); var startInlines = new List<Inline>(span.Inlines); foreach (Inline i in startInlines) { switch (i) { case Hyperlink _: continue; case Run run: { if (!_regex.IsMatch(run.Text)) continue; var newLines = GetHyperlinks(run); replacements.Add(run, newLines); break; } default: RecognizeHyperlinks(i); break; } } if (!replacements.Any()) return; var currentInlines = new List<Inline>(span.Inlines); span.Inlines.Clear(); foreach (Inline i in currentInlines) { if (replacements.ContainsKey(i)) span.Inlines.AddRange(replacements[i]); else span.Inlines.Add(i); } } static List<Inline> GetHyperlinks(Run run) { var result = new List<Inline>(); var currentText = run.Text; do { if (!_regex.IsMatch(currentText)) { if (!string.IsNullOrEmpty(currentText)) result.Add(new Run(currentText)); break; } var match = _regex.Match(currentText); if (match.Index > 0) { result.Add(new Run(currentText.Substring(0, match.Index))); } var hyperLink = new Hyperlink() { NavigateUri = new Uri(match.Value) }; hyperLink.Inlines.Add(match.Value); hyperLink.RequestNavigate += HyperLink_RequestNavigate; result.Add(hyperLink); currentText = currentText.Substring(match.Index + match.Length); } while (true); return result; } static void HyperLink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e) { try { Process.Start(e.Uri.ToString()); } catch { } } } 

Using this, you can simply do <TextBlock ns:Attached.FormattedText="{Binding Content}"/> instead of <TextBlock Text="{Binding Content}"/> and it will automatically recognize and activate links, as well as recognize regular tags formatting such as <Bold> etc.

Please note that this is based on @gwiazdorrr's answer here, as well as some other answers to this question; I basically combined them all in 1 and did some recursion processing, and it works! :). Templates and systems can also be adapted to recognize other types of links or markup, if necessary.

0
source

Source: https://habr.com/ru/post/899741/


All Articles