web-dev-qa-db-fra.com

Ajouter un hyperlien au bloc de texte wpf

Salutations, j'ai du texte dans une base de données et c'est comme suit:

Lorem ipsum dolor assis amet, élite adipiscing consectetur. Duis tellus nisl, venenatis et pharetra ac, tempor sed sapien. Integer pellentesque blandit velit, en tempus urna sempre sit amet. Duis mollis, libero ut consectetur interdum, massa tellus posuere nisi, eu aliquet elit lacus nec erat. Présentez un commodo quam. ** [a href = ' http://somesite.com '] certains site [/ a] ** Suspendisse à nisi sit amet massa molestie gravida feuida feugiat ac sem. Phasellus ac mauris ipsum, vel auctor odio

Ma question est la suivante: comment afficher un Hyperlink dans un TextBlock? Je ne veux pas utiliser un contrôle webBrowser à cette fin. Je ne veux pas non plus utiliser ce contrôle: http://www.codeproject.com/KB/WPF/htmltextblock.aspx également

34
niao

Vous pouvez utiliser Regex avec un convertisseur de valeur dans une telle situation.

Utilisez ceci pour vos besoins (idée originale de ici ):

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

Cela correspondra à tous les liens de votre chaîne contenant des liens et créera 2 groupes nommés pour chaque correspondance: link et text

Vous pouvez maintenant parcourir tous les matchs. Chaque match vous donnera un

    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
    }

Remarque: utilisez cette logique dans votre convertisseur de valeur ConvertToHyperlinkedText personnalisé.

16
mg007

L'affichage est assez simple, la navigation est une autre question. XAML va comme ceci:

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

Et le gestionnaire d'événements qui lance le navigateur par défaut pour naviguer vers votre lien hypertexte serait:

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

Modifier: Pour le faire avec le texte que vous avez de la base de données, vous devrez en quelque sorte analyser le texte. Une fois que vous connaissez les parties textuelles et les parties liées par un lien hypertexte, vous pouvez créer dynamiquement le contenu des blocs de texte dans le 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");
95
Stanislav Kniazev

Une autre version de ceci et pas totalement identique à la reconnaissance du format ici, mais voici une classe pour reconnaître automatiquement les liens dans un morceau de texte et les rendre hyperliens en direct:

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

En utilisant cela, vous pouvez simplement faire <TextBlock ns:TextBlockExt.FormattedText="{Binding Content}" /> au lieu de <TextBlock Text="{Binding Content}" /> et il reconnaîtra et activera automatiquement les liens, ainsi que les balises de mise en forme normales comme <Bold>, etc.

Notez que cela est basé sur la réponse de @gwiazdorrr ici ainsi que d'autres réponses sur cette question; Je les ai essentiellement combinés en un seul et j'ai fait quelques manipulations de récursivité et ça marche! :). Les modèles et les systèmes pourraient également être adaptés pour reconnaître d'autres types de liens ou de balisage si vous le souhaitez.

0
sfaust