Вам нужно что-то, что будет анализировать текст TextBlock и создавать все встроенные объекты во время выполнения. Для этого вы можете либо создать свой собственный пользовательский элемент управления, производный от TextBlock, либо присоединенное свойство.
Для синтаксического анализа вы можете искать URL-адреса в тексте с помощью регулярного выражения. Я позаимствовал регулярное выражение из Хорошее регулярное выражение URL? но в Интернете есть и другие, поэтому вы можете выбрать тот, который лучше всего подходит для вас.
В приведенном ниже примере я использовал присоединенное свойство. Чтобы использовать его, измените свой 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());
}
}
}
person
Bojan Resnik
schedule
15.05.2009