【问题标题】:Add hyperlink to textblock wpf将超链接添加到文本块 wpf
【发布时间】:2011-01-06 18:22:27
【问题描述】:

您好, 我在数据库中有一些文本,如下所示:

Lorem ipsum dolor sit amet,consectetur adipiscing elit。杜伊斯 Tellus nisl、venenatis et pharetra ac、tempor sed sapien。整数 pellentesque blandit velit,在 tempus urna semper 中。杜伊斯 mollis,libero ut consectetur interdum,massa tellus posuere nisi,欧盟 aliquet elit lacus nec erat。 Praesent 一个commodo quam。 **[一种 href='http://somesite.com']一些网站[/a]**暂停在nisi sat amet massa molestie gravida feugiat ac sem。 Phasellus ac mauris ipsum, vel 拍卖师奥迪

我的问题是:如何在TextBlock 中显示Hyperlink?我不想为此目的使用 webBrowser 控件。 我也不想使用这个控件:http://www.codeproject.com/KB/WPF/htmltextblock.aspxalso

【问题讨论】:

    标签: html wpf hyperlink textblock


    【解决方案1】:

    显示比较简单,导航是另一个问题。 XAML 是这样的:

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

    启动默认浏览器以导航到您的超链接的事件处理程序将是:

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

    编辑:要使用从数据库中获得的文本,您必须以某种方式解析文本。一旦知道了文本部分和超链接部分,就可以在代码中动态构建文本块内容:

    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");
    

    【讨论】:

    • 是的..但正如我所写的,我将此链接包含在存储在数据库中的一些文本中。然后我想阅读文本并在需要时添加适当的超链接
    • 如何在将数据库绑定到 TextBlock.Text 的转换器中完成此操作?
    • 请注意那些找到此答案但得到“FileNotFound”异常的人:我必须用 System.Diagnostics.Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri) { UseShellExecute = true });
    【解决方案2】:

    在这种情况下,您可以将正则表达式与值转换器一起使用。

    将此用于您的要求(来自here 的原始想法):

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

    这将匹配包含链接的字符串中的所有链接,并为每个匹配创建 2 个命名组:linktext

    现在您可以遍历所有匹配项。每场比赛都会给你一个

        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
        }
    

    注意:在您的自定义 ConvertToHyperlinkedText 值转换器中使用此逻辑。

    【讨论】:

      【解决方案3】:

      这是另一个版本,与此处识别格式不完全相同,但这里有一个用于自动识别一段文本中的链接并使它们成为实时超链接的类:

      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 { }
          }
      }
      

      使用它你可以只做&lt;TextBlock ns:TextBlockExt.FormattedText="{Binding Content}" /&gt;而不是&lt;TextBlock Text="{Binding Content}" /&gt;,它会自动识别和激活链接,以及识别像&lt;Bold&gt;这样的普通格式标签。

      请注意,这是基于@gwiazdorrr here 的回答以及有关此问题的其他一些回答;我基本上将它们全部合并为 1 并进行了一些递归处理,它可以工作! :)。如果需要,这些模式和系统也可以适应识别其他类型的链接或标记。

      【讨论】:

      • 你的意思是&lt;TextBlock ns:TextBlockExt.FormattedText="{Binding Content}" /&gt;
      • "http[s]?://[^\s-]+" 不太清楚我是否理解为什么- 不被视为 URI 的有效部分?
      • 我还必须将!replacements.Any() 替换为replacements.Count == 0;也许原始代码正在使用 Linq 字典。没有给定命名空间就很难分辨
      • 老实说,我对正则表达式没问题,但无论如何都不是正则表达式大师;已经有一段时间了,但我相信我从其他地方提取了这种模式,但不记得在哪里了。它当然有可能使用一些调整,但一直在为我迄今为止使用的它工作。至于 Any() 是的,这是它正在使用的 LINQ 表达式。
      • 为了它的价值,我正在使用@"http[s]?://[^\s]*[^\s\.]"。它不会在空格之前(或字符串结尾之前)吃掉一个尾随句点,假设所述句点终止一个句子而不是终止 URI。但这是否合适取决于用例。我取消了- 豁免; URI 中到处都有连字符(包括在这个页面的 URI 中:P)
      【解决方案4】:

      XAML:

      <TextBlock x:Name="txbLink" Height="30" Width="500" Margin="0,10"/>
      

      C#:

      Regex regex = new Regex(@"(?<text1>.*?)\<a\s+href='(?<link>\[^'\]+)'\>(?<textLink>.*?)\</a\>(?<text2>.*)", RegexOptions.Compiled);
      string stringContainingLinks = "Click <a href='http://somesite.com'>here</a> for download.";
      foreach (Match match in regex.Matches(stringContainingLinks))
      {
              string text1 = match.Groups["text1"].Value;
              string link = match.Groups["link"].Value;
              string textLink = match.Groups["textLink"].Value;
              string text2 = match.Groups["text2"].Value;
              
              var h = new Hyperlink();
              h.NavigateUri = new Uri(link);
              h.RequestNavigate += new RequestNavigateEventHandler(Hyperlink_RequestNavigate);
              h.Inlines.Add(textLink);
              txbLink.Inlines.Add(text1);
              txbLink.Inlines.Add(h);
              txbLink.Inlines.Add(text2);
      }
          
      private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
      {
          Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
          e.Handled = true;
      }
      

      【讨论】:

        猜你喜欢
        • 2023-01-11
        • 1970-01-01
        • 2018-07-22
        • 1970-01-01
        • 1970-01-01
        • 2013-08-22
        • 2011-08-22
        • 2016-01-17
        • 1970-01-01
        相关资源
        最近更新 更多