WPF – 使超链接可点击
我有一些文字,我试图显示在列表中。 其中一些文本片段包含超链接。 我想让链接在文本中点击。 我可以想象解决这个问题的方法,但是他们肯定不太好看。
例如,我可以拆开string,将其拆分成超链接和非超链接。 然后,我可以dynamic构build一个文本块,根据需要添加纯文本元素和超链接对象。
我希望有一个更好的,最好是声明的东西。
例如:“嘿,看看这个链接: http : //mylink.com这真的很酷。
您需要一些能够parsingTextBlock的Text并在运行时创build所有内联对象的东西。 为此,您可以创build自己的自定义控件派生自TextBlock或附加属性。
对于parsing,您可以使用正则expression式在文本中searchURL。 我从一个好的url正则expression式中借用了一个正则expression式? 但networking上还有其他可用的,所以你可以select一个最适合你的。
在下面的示例中,我使用了一个附加属性。 要使用它,请修改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|[az]{2}))(?#Port)(?::[\d]{1,5})?(?#Directories)(?:(?:(?:/(?:[-\w~!$+|.,=]|%[af\d]{2})+)+|/)+|\?|#)?(?#Query)(?:(?:\?(?:[-\w~!$+|.,*:]|%[af\d{2}])+=(?:[-\w~!$+|.,*:=]|%[af\d]{2})*)(?:&(?:[-\w~!$+|.,*:]|%[af\d{2}])+=(?:[-\w~!$+|.,*:=]|%[af\d]{2})*)*)*(?#Anchor)(?:#(?:[-\w~!$+|.,*:=]|%[af\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()); } } }
这里是简化的版本:
<TextBlock> Hey, check out this link: <Hyperlink NavigateUri="CNN.COM" Click="cnn_Click">Test</Hyperlink> </TextBlock>
像这样的东西?
<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>
编辑:如果你需要dynamic,绑定它。 在上面的示例中,lvTopics(未显示)绑定到具有Title和Url属性的对象列表。 此外,它不会自动去url,你需要用一个类似的代码来处理它:
private void Url_Click(object sender, RoutedEventArgs e) { browser.Navigate(((Hyperlink)sender).NavigateUri); }
我只是想表明,你可以embedded任何东西到TextBlock,包括超链接,和任何东西到超链接。
Bojan的答案的VB.Net版本。 我稍微改进了一下:这段代码将像http://support.mycompany.com?username=x&password=y这样的urlparsing为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|[az]{2})))(?#Port)(?::[\d]{1,5})?(?#Directories)(?:(?:(?:/(?:[-\w~!$+|.,=]|%[af\d]{2})+)+|/)+|\?|#)?(?#Query)(?:(?:\?(?:[-\w~!$+|.,*:]|%[af\d{2}])+=(?:[-\w~!$+|.,*:=]|%[af\d]{2})*)(?:&(?:[-\w~!$+|.,*:]|%[af\d{2}])+=(?:[-\w~!$+|.,*:=]|%[af\d]{2})*)*)*(?#Anchor)(?:#(?:[-\w~!$+|.,*:=]|%[af\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
如果您使用的是MVVM light或类似的体系结构,您可以在textblock mousedown属性上使用交互触发器,并在视图模型的代码中执行任何操作。