WPF - 使超链接可点击
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/861409/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WPF - Making hyperlinks clickable
提问by Bojan Resnik
I've got a bit of text that I'm trying to display in a list. Some of those pieces of a text contain a hyperlink. I'd like to make the links clickable within the text. I can imagine solutions to this problem, but they sure don't seem pretty.
我有一些要在列表中显示的文本。其中一些文本片段包含超链接。我想让链接在文本中可点击。我可以想象这个问题的解决方案,但它们看起来肯定不漂亮。
For instance, I could tear apart the string, splitting it into hyperlinks and non-hyperlinks. Then I could dynamically build a Textblock, adding plain text elements and hyperlink objects as appropriate.
例如,我可以撕开字符串,将其拆分为超链接和非超链接。然后我可以动态构建一个 Textblock,根据需要添加纯文本元素和超链接对象。
I'm hoping there's a better, preferably something declarative.
我希望有一个更好的,最好是声明性的。
Example: "Hey, check out this link: http://mylink.comIt's really cool."
例如:“嘿,看看这个链接:http: //mylink.com真的很酷。”
回答by Bojan Resnik
You need something that will parse the Text of the TextBlock and create the all the inline objects at runtime. For this you can either create your own custom control derived from TextBlock or an attached property.
您需要一些可以解析 TextBlock 的文本并在运行时创建所有内联对象的东西。为此,您可以创建自己的自定义控件,派生自 TextBlock 或附加属性。
For the parsing, you can search for URLs in the text with a regular expression. I borrowed a regular expression from A good url regular expression?but there are others available on the web, so you can choose the one which works best for you.
对于解析,您可以使用正则表达式搜索文本中的 URL。我从一个好的 url 正则表达式中借用了一个正则表达式?但网络上还有其他可用的,因此您可以选择最适合您的一种。
In the sample below, I used an attached property. To use it, modify your TextBlock to use NavigateService.Text instead of Text property:
在下面的示例中,我使用了附加属性。要使用它,请修改您的 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>
The code for the attached property is given below:
附加属性的代码如下:
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());
}
}
}
回答by bitbonk
Here is the simplified version:
这是简化版:
<TextBlock>
Hey, check out this link:
<Hyperlink NavigateUri="CNN.COM" Click="cnn_Click">Test</Hyperlink>
</TextBlock>
回答by Sergey Aldoukhov
Something like this?
像这样的东西?
<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>
EDIT: If you need dynamic, bind it. In the example above, lvTopics(not shown) is bound to a list of objects with Title and Url properties. Also, it will not go to the url automatically, you need to handle it with a similar code:
编辑:如果您需要动态,请绑定它。在上面的示例中,lvTopics(未显示)绑定到具有 Title 和 Url 属性的对象列表。此外,它不会自动转到 url,您需要使用类似的代码来处理它:
private void Url_Click(object sender, RoutedEventArgs e)
{ browser.Navigate(((Hyperlink)sender).NavigateUri); }
I just wanted to show that you can embed anything into TextBlock, including Hyperlink, and anything into Hyperlink.
我只是想表明您可以将任何内容嵌入到 TextBlock 中,包括超链接,以及任何内容到超链接中。
回答by Dabblernl
The VB.Net version of Bojan's answer. I improved a little upon it: This code will parse an url like http://support.mycompany.com?username=x&password=yto an inline of http://support.mycompany.com, while still navigating to the full url with username and password
Bojan 答案的 VB.Net 版本。我在这改善了一点:此代码将解析像一个URL http://support.mycompany.com?username=x&password=y到内联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
回答by WhoIsNinja
If you're using something like MVVM light or similar architecture, you could have a interaction trigger on textblock mousedown property and do whatever in the viewmodel's code.
如果您使用的是 MVVM light 或类似架构之类的东西,您可以在 textblock mousedown 属性上有一个交互触发器,并在视图模型的代码中执行任何操作。