问题描述
我有一些文字,我试图在列表中显示。这些文本中的一些包含超链接。我想在文本中点击链接。我可以想象这个问题的解决方案,但它们肯定看起来不太漂亮。
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.
例如,我可以撕掉字符串,将其拆分为超链接和非超链接。然后我可以动态地构建一个Textblock,根据需要添加纯文本元素和超链接对象。
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.
我希望有一个更好的,最好是声明性的东西。
I'm hoping there's a better, preferably something declarative.
示例:嘿,请查看此链接:这真的很酷。
Example: "Hey, check out this link: http://mylink.com It's really cool."
推荐答案
你需要能解析TextBlock文本并在运行时创建所有内联对象的东西。为此,您可以创建自己的自定义控件派生自TextBlock或附加属性。
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.
对于解析,您可以使用正则表达式搜索文本中的URL。我借用了的正则表达式,但是网上有其他可用的,所以你可以选择最适合你的那个。
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.
在下面的示例中,我使用了附加属性。要使用它,请修改TextBlock以使用NavigateService.Text而不是Text属性:
In the sample below, I used an attached property. To use it, modify your TextBlock to use NavigateService.Text instead of Text property:
<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 - 使超链接可单击的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!