40

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

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 sat amet。Duis mollis、libero ut consectetur interdum、massa tellus posuere nisi、eu aliquet elit lacus necerat。Praesent 一个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

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

4

4 回答 4

109

显示相当简单,导航是另一个问题。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");
于 2010-01-19T11:19:14.697 回答
16

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

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

    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在您的自定义值转换器中使用此逻辑。

于 2010-01-22T16:16:38.440 回答
2

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

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

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

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

于 2019-04-19T21:39:53.380 回答
0

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

在此处输入图像描述

于 2020-08-01T20:19:40.177 回答