使用超链接动态创建 TextBlock
Create TextBlock dynamically with hyperlinks
创建具有多个超链接的文本的最佳方法是什么,超链接可以出现在文本中的不同位置。
我想在代码隐藏文件中动态构建这样的东西:
<StackPanel Orientation="Horizontal" Width="380">
<TextBlock Padding="0" Margin="0" Foreground="White" FontSize="20">Some random Text</TextBlock>
<HyperlinkButton VerticalAlignment="Top" Margin="0" Padding="0" Foreground="White" FontSize="20" Content="Link1" Tapped="RealLink_Tapped" />
<TextBlock Foreground="White" FontSize="20">Some more random Text</TextBlock>
<HyperlinkButton VerticalAlignment="Top" Margin="0" Padding="0" Foreground="White" FontSize="20" Content="Link2" Tapped="RealLink_Tapped" />
<TextBlock Foreground="White" FontSize="20">Some random Text</TextBlock>
<HyperlinkButton VerticalAlignment="Top" Margin="0" Padding="0" Foreground="White" FontSize="20" Content="Link3" Tapped="RealLink_Tapped" />
</StackPanel>
但这目前还行不通。我怎样才能让超链接与文本块对齐,尽管字体大小、边距和填充是相同的。
而且,如何在堆栈面板中获取换行符?最后它应该看起来像一个普通的 TextBlock(TextWrapping="Wrap"
)。
编辑:
这是一个 Windows Phone 8.1 项目
编辑#2:
我无法让 WrapPanel 与 WPToolkit 一起工作,相反我找到了一些东西 here。
干杯,
克里斯
更新 #2:
这是可能的 WinRT 版本。
第 1 节 - Xaml 代码(用户控件),这里的 WinRtApp 是定义用户控件的项目。如果您想使用其他一些 ContentTemplate 来呈现您的数据(如推文),您应该解析您的文本并添加一个新模型(如 TweetPart),看看我是如何使用 HyperlinkButton 实现的,添加新的 DataTemplate 并扩展 ContentTemplateSelector。
<UserControl
x:Class="WinRtApp.ComplexTextPresenter"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400" x:Name="This">
<Grid>
<Grid.Resources>
<DataTemplate x:Key="HyperlinkDataTemplateKey">
<HyperlinkButton Margin="0" FontSize="12" CommandParameter="{Binding }" Command="{Binding ElementName=This, Path=OnHyperlinkCommand}" Content="{Binding Path=Content}" Foreground="Blue"/>
</DataTemplate>
<DataTemplate x:Key="LiteralDataTemplateKey">
<TextBlock Margin="0" FontSize="12" Text="{Binding Path=Content}"></TextBlock>
</DataTemplate>
<MyDataTemplateSelector x:Key="DataTemplateSelectorKey"
LiteralDataTemplate ="{StaticResource LiteralDataTemplateKey}"
HyperlinkDataTemplate="{StaticResource HyperlinkDataTemplateKey}"/>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5,0,0,0"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<Rectangle Fill="Green" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<ListBox ItemsSource="{Binding ElementName=This, Path=InputCollection}" ItemTemplateSelector="{StaticResource DataTemplateSelectorKey}" Margin="5">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ListBox>
</Grid>
第 2 部分 - Xaml 背后的代码
public sealed partial class ComplexTextPresenter : UserControl
{
public static readonly DependencyProperty InputProperty = DependencyProperty.Register("Input", typeof(string), typeof(MainPage), new PropertyMetadata(default(string), InputPropertyChangedCallback));
private static void InputPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var ctrl = dependencyObject as ComplexTextPresenter;
if(ctrl == null)
return;
ctrl.Init();
}
public static readonly DependencyProperty InputCollectionProperty = DependencyProperty.Register("InputCollection", typeof(ObservableCollection<object>), typeof(MainPage), new PropertyMetadata(default(ObservableCollection<object>)));
public static readonly DependencyProperty OnHyperlinkCommandProperty = DependencyProperty.Register("OnHyperlinkCommand", typeof(ICommand), typeof(MainPage), new PropertyMetadata(default(ICommand)));
private IEnumerable<object> GetParsedInput()
{
List<BaseInputPart> inputParts = new List<BaseInputPart>();
var strings = Input.Split(new[] { " " }, StringSplitOptions.None).ToList();
strings.ForEach(s =>
{
if (s.IsHyperlink())
{
inputParts.Add(new HyperLinkPart { Content = s });
}
else
{
inputParts.Add(new LiteralPart { Content = s });
}
});
return inputParts.OfType<object>().ToList();
}
public string Input
{
get { return (string)GetValue(InputProperty); }
set { SetValue(InputProperty, value); }
}
public ObservableCollection<object> InputCollection
{
get { return (ObservableCollection<object>)GetValue(InputCollectionProperty); }
private set { SetValue(InputCollectionProperty, value); }
}
public ICommand OnHyperlinkCommand
{
get { return (ICommand)GetValue(OnHyperlinkCommandProperty); }
set { SetValue(OnHyperlinkCommandProperty, value); }
}
private void Init()
{
InputCollection = new ObservableCollection<object>(GetParsedInput());
}
public ComplexTextPresenter()
{
this.InitializeComponent();
}
}
public abstract class BaseInputPart
{
public abstract string Content { get; set; }
}
public class HyperLinkPart : BaseInputPart
{
public override string Content { get; set; }
}
public class LiteralPart : BaseInputPart
{
public override string Content { get; set; }
}
public static class StringExtension
{
#region hyperlink regex region
private static readonly Regex UrlRegex =
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})*)?");
#endregion
public static bool IsHyperlink(this string word)
{
var result = false;
try
{
// First check to make sure the word has at least one of the characters we need to make a hyperlink
if (word.IndexOfAny(@":.\/".ToCharArray()) != -1)
{
if (Uri.IsWellFormedUriString(word, UriKind.Absolute))
{
// The string is an Absolute URI
result = true;
}
else if (UrlRegex.IsMatch(word))
{
Uri uri = new Uri(word, UriKind.RelativeOrAbsolute);
if (!uri.IsAbsoluteUri)
{
// rebuild it it with http to turn it into an Absolute URI
uri = new Uri(@"http://" + word, UriKind.Absolute);
result = true;
}
if (uri.IsAbsoluteUri)
{
result = true;
}
}
else
{
Uri wordUri = new Uri(word);
// Check to see if URL is a network path
if (wordUri.IsUnc || wordUri.IsFile)
{
result = true;
}
}
}
}
catch (Exception e)
{
result = false;
}
return result;
}
}
更新 #4 - 选择器代码(添加为新 class)
public class MyDataTemplateSelector : DataTemplateSelector
{
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
if (item is HyperLinkPart)
return HyperlinkDataTemplate;
if (item is LiteralPart)
return LiteralDataTemplate;
return null;
}
public DataTemplate LiteralDataTemplate { get; set; }
public DataTemplate HyperlinkDataTemplate
{ get; set; }
}
如何使用-主页xaml代码
<Page
x:Class="PutHereTheNameOfYourProject.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ComplexTextPresenter x:Name="ComplexTextPresenter"/>
How to use - MainPage xaml code behind
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
Init();
}
private void Init()
{
ComplexTextPresenter.Input =
@"I Love https://www.google.com site. I Love https://www.google.com site. I Love https://www.google.com site. I Love https://www.google.com site. I Love https://www.google.com site. I Love https://www.google.com site. I Love https://www.google.com site.";
ComplexTextPresenter.OnHyperlinkCommand = new RelayCommand<object>(Execute);
}
private void Execute(object o)
{
//put here the code that can open browser
}
}
中继命令代码
public class RelayCommand<T> : ICommand
{
readonly Action<T> _execute;
readonly Func<T, bool> _canExecute;
public event EventHandler CanExecuteChanged;
public RelayCommand(Action<T> execute, Func<T, bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public void RefreshCommand()
{
var cec = CanExecuteChanged;
if (cec != null)
cec(this, EventArgs.Empty);
}
public bool CanExecute(object parameter)
{
if (_canExecute == null) return true;
return _canExecute((T)parameter);
}
public void Execute(object parameter)
{
_execute((T)parameter);
}
}
更新#3
- WinRt 是我的项目名称,您需要使用您的项目名称。
- 创建名为 ComplexTextPresenter 的用户控件。
- 用第 1 节中定义的代码替换 UserControl 的 xaml。
- 用第 2 节中定义的代码替换 UserControl 的代码隐藏。
- 将第 3 节中定义的 RelayCommand 代码作为 class 添加到您的项目中。
- 阅读this article,关于如何在XAML中添加引用。
- 为了在您的项目中获取 Wrappanel,运行 VS 中的下一个 NuGet 命令 Tools/NuGet 包 Manager/Package 管理器控制台:Install-Package WPtoolkit(taken from here).
- 下载ReSharper,它将帮助您管理所有程序集导入相关问题。
创建具有多个超链接的文本的最佳方法是什么,超链接可以出现在文本中的不同位置。
我想在代码隐藏文件中动态构建这样的东西:
<StackPanel Orientation="Horizontal" Width="380">
<TextBlock Padding="0" Margin="0" Foreground="White" FontSize="20">Some random Text</TextBlock>
<HyperlinkButton VerticalAlignment="Top" Margin="0" Padding="0" Foreground="White" FontSize="20" Content="Link1" Tapped="RealLink_Tapped" />
<TextBlock Foreground="White" FontSize="20">Some more random Text</TextBlock>
<HyperlinkButton VerticalAlignment="Top" Margin="0" Padding="0" Foreground="White" FontSize="20" Content="Link2" Tapped="RealLink_Tapped" />
<TextBlock Foreground="White" FontSize="20">Some random Text</TextBlock>
<HyperlinkButton VerticalAlignment="Top" Margin="0" Padding="0" Foreground="White" FontSize="20" Content="Link3" Tapped="RealLink_Tapped" />
</StackPanel>
但这目前还行不通。我怎样才能让超链接与文本块对齐,尽管字体大小、边距和填充是相同的。
而且,如何在堆栈面板中获取换行符?最后它应该看起来像一个普通的 TextBlock(TextWrapping="Wrap"
)。
编辑: 这是一个 Windows Phone 8.1 项目
编辑#2: 我无法让 WrapPanel 与 WPToolkit 一起工作,相反我找到了一些东西 here。
干杯,
克里斯
更新 #2: 这是可能的 WinRT 版本。
第 1 节 - Xaml 代码(用户控件),这里的 WinRtApp 是定义用户控件的项目。如果您想使用其他一些 ContentTemplate 来呈现您的数据(如推文),您应该解析您的文本并添加一个新模型(如 TweetPart),看看我是如何使用 HyperlinkButton 实现的,添加新的 DataTemplate 并扩展 ContentTemplateSelector。
<UserControl
x:Class="WinRtApp.ComplexTextPresenter"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400" x:Name="This">
<Grid>
<Grid.Resources>
<DataTemplate x:Key="HyperlinkDataTemplateKey">
<HyperlinkButton Margin="0" FontSize="12" CommandParameter="{Binding }" Command="{Binding ElementName=This, Path=OnHyperlinkCommand}" Content="{Binding Path=Content}" Foreground="Blue"/>
</DataTemplate>
<DataTemplate x:Key="LiteralDataTemplateKey">
<TextBlock Margin="0" FontSize="12" Text="{Binding Path=Content}"></TextBlock>
</DataTemplate>
<MyDataTemplateSelector x:Key="DataTemplateSelectorKey"
LiteralDataTemplate ="{StaticResource LiteralDataTemplateKey}"
HyperlinkDataTemplate="{StaticResource HyperlinkDataTemplateKey}"/>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5,0,0,0"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<Rectangle Fill="Green" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<ListBox ItemsSource="{Binding ElementName=This, Path=InputCollection}" ItemTemplateSelector="{StaticResource DataTemplateSelectorKey}" Margin="5">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ListBox>
</Grid>
第 2 部分 - Xaml 背后的代码
public sealed partial class ComplexTextPresenter : UserControl
{
public static readonly DependencyProperty InputProperty = DependencyProperty.Register("Input", typeof(string), typeof(MainPage), new PropertyMetadata(default(string), InputPropertyChangedCallback));
private static void InputPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var ctrl = dependencyObject as ComplexTextPresenter;
if(ctrl == null)
return;
ctrl.Init();
}
public static readonly DependencyProperty InputCollectionProperty = DependencyProperty.Register("InputCollection", typeof(ObservableCollection<object>), typeof(MainPage), new PropertyMetadata(default(ObservableCollection<object>)));
public static readonly DependencyProperty OnHyperlinkCommandProperty = DependencyProperty.Register("OnHyperlinkCommand", typeof(ICommand), typeof(MainPage), new PropertyMetadata(default(ICommand)));
private IEnumerable<object> GetParsedInput()
{
List<BaseInputPart> inputParts = new List<BaseInputPart>();
var strings = Input.Split(new[] { " " }, StringSplitOptions.None).ToList();
strings.ForEach(s =>
{
if (s.IsHyperlink())
{
inputParts.Add(new HyperLinkPart { Content = s });
}
else
{
inputParts.Add(new LiteralPart { Content = s });
}
});
return inputParts.OfType<object>().ToList();
}
public string Input
{
get { return (string)GetValue(InputProperty); }
set { SetValue(InputProperty, value); }
}
public ObservableCollection<object> InputCollection
{
get { return (ObservableCollection<object>)GetValue(InputCollectionProperty); }
private set { SetValue(InputCollectionProperty, value); }
}
public ICommand OnHyperlinkCommand
{
get { return (ICommand)GetValue(OnHyperlinkCommandProperty); }
set { SetValue(OnHyperlinkCommandProperty, value); }
}
private void Init()
{
InputCollection = new ObservableCollection<object>(GetParsedInput());
}
public ComplexTextPresenter()
{
this.InitializeComponent();
}
}
public abstract class BaseInputPart
{
public abstract string Content { get; set; }
}
public class HyperLinkPart : BaseInputPart
{
public override string Content { get; set; }
}
public class LiteralPart : BaseInputPart
{
public override string Content { get; set; }
}
public static class StringExtension
{
#region hyperlink regex region
private static readonly Regex UrlRegex =
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})*)?");
#endregion
public static bool IsHyperlink(this string word)
{
var result = false;
try
{
// First check to make sure the word has at least one of the characters we need to make a hyperlink
if (word.IndexOfAny(@":.\/".ToCharArray()) != -1)
{
if (Uri.IsWellFormedUriString(word, UriKind.Absolute))
{
// The string is an Absolute URI
result = true;
}
else if (UrlRegex.IsMatch(word))
{
Uri uri = new Uri(word, UriKind.RelativeOrAbsolute);
if (!uri.IsAbsoluteUri)
{
// rebuild it it with http to turn it into an Absolute URI
uri = new Uri(@"http://" + word, UriKind.Absolute);
result = true;
}
if (uri.IsAbsoluteUri)
{
result = true;
}
}
else
{
Uri wordUri = new Uri(word);
// Check to see if URL is a network path
if (wordUri.IsUnc || wordUri.IsFile)
{
result = true;
}
}
}
}
catch (Exception e)
{
result = false;
}
return result;
}
}
更新 #4 - 选择器代码(添加为新 class)
public class MyDataTemplateSelector : DataTemplateSelector
{
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
if (item is HyperLinkPart)
return HyperlinkDataTemplate;
if (item is LiteralPart)
return LiteralDataTemplate;
return null;
}
public DataTemplate LiteralDataTemplate { get; set; }
public DataTemplate HyperlinkDataTemplate
{ get; set; }
}
如何使用-主页xaml代码
<Page
x:Class="PutHereTheNameOfYourProject.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ComplexTextPresenter x:Name="ComplexTextPresenter"/>
How to use - MainPage xaml code behind
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
Init();
}
private void Init()
{
ComplexTextPresenter.Input =
@"I Love https://www.google.com site. I Love https://www.google.com site. I Love https://www.google.com site. I Love https://www.google.com site. I Love https://www.google.com site. I Love https://www.google.com site. I Love https://www.google.com site.";
ComplexTextPresenter.OnHyperlinkCommand = new RelayCommand<object>(Execute);
}
private void Execute(object o)
{
//put here the code that can open browser
}
}
中继命令代码
public class RelayCommand<T> : ICommand
{
readonly Action<T> _execute;
readonly Func<T, bool> _canExecute;
public event EventHandler CanExecuteChanged;
public RelayCommand(Action<T> execute, Func<T, bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public void RefreshCommand()
{
var cec = CanExecuteChanged;
if (cec != null)
cec(this, EventArgs.Empty);
}
public bool CanExecute(object parameter)
{
if (_canExecute == null) return true;
return _canExecute((T)parameter);
}
public void Execute(object parameter)
{
_execute((T)parameter);
}
}
更新#3
- WinRt 是我的项目名称,您需要使用您的项目名称。
- 创建名为 ComplexTextPresenter 的用户控件。
- 用第 1 节中定义的代码替换 UserControl 的 xaml。
- 用第 2 节中定义的代码替换 UserControl 的代码隐藏。
- 将第 3 节中定义的 RelayCommand 代码作为 class 添加到您的项目中。
- 阅读this article,关于如何在XAML中添加引用。
- 为了在您的项目中获取 Wrappanel,运行 VS 中的下一个 NuGet 命令 Tools/NuGet 包 Manager/Package 管理器控制台:Install-Package WPtoolkit(taken from here).
- 下载ReSharper,它将帮助您管理所有程序集导入相关问题。