Html 将超链接添加到 textblock wpf

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2092890/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-29 01:54:05  来源:igfitidea点击:

Add hyperlink to textblock wpf

htmlwpfhyperlinktextblock

提问by niao

Greetings, I have some text in a db and it is as follows:

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

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. Praesent a commodo quam. **[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

Lorem ipsum dolor 坐 amet,consectetur adipiscing 精英。Duis Tellus nisl、venenatis et pharetra ac、tempor sed sapien。整数 pellentesque blandit velit,在 tempus urna semper 坐 amet。Duis mollis、libero ut consectetur interdum、massatellus posuere nisi、eu aliquet elit lacus necerat。Praesent commodo quam。**[a href=' http://somesite.com']some site[/a]**Suspendisse at nisi sat amet massa molestie gravida feugiat ac sem。Phasellus ac mauris ipsum, vel auctor odio

My question is: How can I display a Hyperlinkin a TextBlock? I don't want to use a webBrowser control for this purpose. I don't want to use this control either: http://www.codeproject.com/KB/WPF/htmltextblock.aspxalso

我的问题是:如何Hyperlink在 a 中显示a TextBlock?我不想为此使用 webBrowser 控件。我不希望用这种控制:http://www.codeproject.com/KB/WPF/htmltextblock.aspx

采纳答案by mg007

You can use Regex with a value converter in such situation.

在这种情况下,您可以将 Regex 与值转换器一起使用。

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 string containing links, and make 2 named groups for each match : linkand text

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

Now you can iterate through all the matches. Each match will give you a

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

    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 ConvertToHyperlinkedTextvalue converter.

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

回答by Stanislav Kniazev

Displaying is rather simple, the navigation is another question. XAML goes like this:

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

<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 launches default browser to navigate to your hyperlink would be:

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

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

Edit: To do it with the text you've got from database you'll have to parse the text somehow. Once you know the textual parts and hyperlinked parts, you can build textblock contents dynamically 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");

回答by sfaust

Another version of this and not totally the same as recognizing the format here, but here is a class for automatically recognizing links in a piece of text and making them 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 that you can just do <TextBlock ns:TextBlockExt.FormattedText="{Binding Content}" />instead of <TextBlock Text="{Binding Content}" />and it will recognize and activate links automatically, as well as recognizing normal formatting tags like <Bold>, etc.

使用它,你可以<TextBlock ns:TextBlockExt.FormattedText="{Binding Content}" />代替<TextBlock Text="{Binding Content}" />它,它会自动识别和激活链接,以及识别正常的格式标签,如<Bold>等。

Note that this is based off of the answer by @gwiazdorrr hereas well as some other answers on this question; I basically combined them all into 1 and did some recursion handling and it works! :). The patterns and systems could also be adapted to recognize other types of links or markup if desired.

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