Reputation: 179
what is the best way to create texts with several hyperlinks whereby the hyperlinks can appear at different positions within in the text.
I want to build something like this dynamically in the code-behind file:
<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>
But this doesnt work so far. How can I get the Hyperlinks aligned with the TextBlocks, though the fontsize, margin and paddings are the same.
And also, how do I get the line break withn in the stackpanel? At the end it should look like a normal TextBlock (with TextWrapping="Wrap"
).
EDIT: It's a Windows Phone 8.1 Project
EDIT#2: I couldn't get the WrapPanel to work with the WPToolkit, instead I've found something here.
Cheers,
Chris
Upvotes: 1
Views: 663
Reputation: 2782
Updates #2: Here is the possible WinRT version.
Section 1 - Xaml code(user control), here the WinRtApp is the project where the user control is defined. If you want to use some another ContentTemplate to present your data(like tweets), you should parse your text and add a new model(like a TweetPart) see how I did it with the HyperlinkButton, add new DataTemplate and extend the 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>
Section 2 - Xaml's code behind
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;
}
}
Updates #4 - Selector code (add as a new 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; }
}
How to use - MainPage xaml code
<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
}
}
Relay command code
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);
}
}
Updates #3
Upvotes: 2