WPF -使超链接可单击

u5i3ibmn  于 2022-11-18  发布在  其他
关注(0)|答案(5)|浏览(197)

我有一段文字要显示在清单中。其中有些文字包含超连结。我想让文字中的链接可以按一下。我可以想像这个问题的解决方案,但看起来并不太好。
例如,我可以将字符串拆分成超链接和非超链接,然后动态构建一个Textblock,适当地添加纯文本元素和超链接对象。
我希望有更好的,最好是声明性的。
例如:“嘿,看看这个链接:http://mylink.com这真的很酷。”

waxmsbnn

waxmsbnn1#

您需要能够在运行时解析TextBlock的Text并创建所有内联对象的程序。为此,您可以创建自己的自定义控件,该控件派生自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());
        }
    }
}
tvz2xvvm

tvz2xvvm2#

以下是简化版本:

<TextBlock>
    Hey, check out this link:        
    <Hyperlink NavigateUri="CNN.COM" Click="cnn_Click">Test</Hyperlink>
</TextBlock>
siotufzp

siotufzp3#

像这样的东西?

<TextBlock>
    <TextBlock Text="Hey, check out this link:"/>
    <Hyperlink NavigateUri={Binding ElementName=lvTopics, Path=SelectedValue.Title}
                           Click="Url_Click">
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Feed: " FontWeight="Bold"/>
            <TextBlock Text={Binding ElementName=lvTopics, Path=SelectedValue.Url}/>
        </StackPanel>
    </Hyperlink>
</TextBlock>

编辑:如果你需要动态的,就绑定它。在上面的例子中,lvTopics(未显示)被绑定到一个带有Title和Url属性的对象列表。另外,它不会自动转到url,你需要用类似的代码来处理它:

private void Url_Click(object sender, RoutedEventArgs e)
{    browser.Navigate(((Hyperlink)sender).NavigateUri); }

我只是想告诉你,你可以在TextBlock中嵌入任何东西,包括Hyperlink,也可以在Hyperlink中嵌入任何东西。

rt4zxlrg

rt4zxlrg4#

Bojan答案的VB.Net版本。我对它做了一些改进:此代码将把http://support.mycompany.com?username=x&password=y之类的url解析为内联的http://support.mycompany.com,同时仍然使用用户名和密码导航到完整的url

Imports System.Text.RegularExpressions
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Documents

Public Class NavigationService
    ' Copied from http://geekswithblogs.net/casualjim/archive/2005/12/01/61722.aspx '
    Private Shared ReadOnly RE_URL = New Regex("(?#Protocol)(?<domainURL>(?:(?: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 Shared ReadOnly TextProperty = DependencyProperty.RegisterAttached( _
        "Text",
        GetType(String),
        GetType(NavigationService),
        New PropertyMetadata(Nothing, AddressOf OnTextChanged)
    )

    Public Shared Function GetText(d As DependencyObject) As String
        Return TryCast(d.GetValue(TextProperty), String)
    End Function

    Public Shared Sub SetText(d As DependencyObject, value As String)
        d.SetValue(TextProperty, value)
    End Sub

    Private Shared Sub OnTextChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
        Dim text_block = TryCast(d, TextBlock)
        If text_block Is Nothing Then Return

        text_block.Inlines.Clear()

        Dim new_text = CStr(e.NewValue)
        If String.IsNullOrEmpty(new_text) Then Return

        ' Find all URLs using a regular expression '
        Dim last_pos As Integer = 0
        For Each match As Match In RE_URL.Matches(new_text)
            'Copy raw string from the last position up to the match '
            If match.Index <> last_pos Then
                Dim raw_text = new_text.Substring(last_pos, match.Index - last_pos)
                text_block.Inlines.Add(New Run(raw_text))
            End If

            ' Create a hyperlink for the match '
            Dim link = New Hyperlink(New Run(match.Groups("domainURL").Value)) With
                {
                    .NavigateUri = New Uri(match.Value)
                }
            AddHandler link.Click, AddressOf OnUrlClick

            text_block.Inlines.Add(link)

            'Update the last matched position '
            last_pos = match.Index + match.Length
        Next

        ' Finally, copy the remainder of the string '
        If last_pos < new_text.Length Then
            text_block.Inlines.Add(New Run(new_text.Substring(last_pos)))
        End If
    End Sub

    Private Shared Sub OnUrlClick(sender As Object, e As RoutedEventArgs)
        Try
            Dim link = CType(sender, Hyperlink)
            Process.Start(link.NavigateUri.ToString)
        Catch
        End Try
    End Sub
End Class
oyjwcjzk

oyjwcjzk5#

如果您使用的是MVVM light或类似的架构,则可以在textblock mousedown属性上设置一个交互触发器,并在视图模型的代码中执行任何操作。

相关问题