WPF - 使超链接可点击

标签 wpf url hyperlink

我有一些文本想要在列表中显示。其中一些文本片段包含超链接。我想让文本中的链接可单击。我可以想象这个问题的解决方案,但它们看起来确实不太漂亮。

例如,我可以拆分字符串,将其分成超链接和非超链接。然后我可以动态构建一个文本 block ,根据需要添加纯文本元素和超链接对象。

我希望有一个更好的,最好是声明性的东西。

示例:“嘿,看看这个链接:http://mylink.com 这真的很酷。”

最佳答案

您需要能够解析 TextBlock 的文本并在运行时创建所有内联对象的东西。为此,您可以创建自己的从 TextBlock 派生的自定义控件或附加属性。

对于解析,您可以使用正则表达式搜索文本中的 URL。我借用了A good url regular expression?的正则表达式但网络上还有其他可用的,因此您可以选择最适合您的一个。

在下面的示例中,我使用了附加属性。要使用它,请修改您的 TextBlock 以使用 NavigateService.Text 而不是 Text 属性:

<Window x:Class="DynamicNavigation.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:DynamicNavigation"
    Title="Window1" Height="300" Width="300">
    <StackPanel>
        <!-- Type something here to see it displayed in the TextBlock below -->
        <TextBox x:Name="url"/>
        
        <!-- Dynamically updates to display the text typed in the TextBox -->
        <TextBlock local:NavigationService.Text="{Binding Text, ElementName=url}" />
    </StackPanel>
</Window>

附加属性的代码如下:

using System;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;

namespace DynamicNavigation
{
    public static class NavigationService
    {
        // Copied from http://geekswithblogs.net/casualjim/archive/2005/12/01/61722.aspx
        private static readonly Regex RE_URL = new Regex(@"(?#Protocol)(?:(?:ht|f)tp(?:s?)\:\/\/|~/|/)?(?#Username:Password)(?:\w+:\w+@)?(?#Subdomains)(?:(?:[-\w]+\.)+(?#TopLevel Domains)(?:com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum|travel|[a-z]{2}))(?#Port)(?::[\d]{1,5})?(?#Directories)(?:(?:(?:/(?:[-\w~!$+|.,=]|%[a-f\d]{2})+)+|/)+|\?|#)?(?#Query)(?:(?:\?(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)(?:&(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)*)*(?#Anchor)(?:#(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)?");
        
        public static readonly DependencyProperty TextProperty = DependencyProperty.RegisterAttached(
            "Text",
            typeof(string),
            typeof(NavigationService),
            new PropertyMetadata(null, OnTextChanged)
        );

        public static string GetText(DependencyObject d)
        { return d.GetValue(TextProperty) as string; }
        
        public static void SetText(DependencyObject d, string value)
        { d.SetValue(TextProperty, value); }
        
        private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var text_block = d as TextBlock;
            if (text_block == null)
                return;
                
            text_block.Inlines.Clear();

            var new_text = (string)e.NewValue;
            if ( string.IsNullOrEmpty(new_text) )
                return;
                
            // Find all URLs using a regular expression
            int last_pos = 0;
            foreach (Match match in RE_URL.Matches(new_text))
            {
                // Copy raw string from the last position up to the match
                if (match.Index != last_pos)
                {
                    var raw_text = new_text.Substring(last_pos, match.Index - last_pos);
                    text_block.Inlines.Add(new Run(raw_text));
                }
                
                // Create a hyperlink for the match
                var link = new Hyperlink(new Run(match.Value))
                {
                    NavigateUri = new Uri(match.Value)
                };
                link.Click += OnUrlClick;
                
                text_block.Inlines.Add(link);
                
                // Update the last matched position
                last_pos = match.Index + match.Length;
            }
            
            // Finally, copy the remainder of the string
            if (last_pos < new_text.Length)
                text_block.Inlines.Add(new Run(new_text.Substring(last_pos)));
        }

        private static void OnUrlClick(object sender, RoutedEventArgs e)
        {
            var link = (Hyperlink)sender;
            // Do something with link.NavigateUri like:
            Process.Start(link.NavigateUri.ToString());
        }
    }
}

关于WPF - 使超链接可点击,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/861409/

相关文章:

javascript - URI路径导航问题

c# - DataGridTemplateColumn 和绑定(bind)错误? MVVM

c# - WPF控制大量数据的性能问题

android - 如何从 Android 中的电子邮件 URL 打开已安装的应用程序?

macos - AppleScript 中的正确处理程序以响应自定义 URL 处理小程序的 "open"Unix 命令

python - Tango with Django - 第 8 章 - 练习

css - 您可以更新::selection 彩色文本中链接的颜色吗?

linux - 创建/删除许多硬链接(hard link)的缺点?

c# - 键盘按键捕获

WPF 多行 TextBlock 中心对齐问题