Create a label that can display underlined (or use Xamarin Forms Labs ExtendedLabel):
using Xamarin.Forms;
public class ExtendedLabel : Label
{
public static readonly BindableProperty IsUnderlinedProperty = BindableProperty.Create<ExtendedLabel, bool>(p => p.IsUnderlined, false);
public bool IsUnderlined
{
get { return (bool)GetValue(IsUnderlinedProperty); }
set { SetValue(IsUnderlinedProperty, value); }
}
// ...other custom properties
}
Handle the IsUnderlined property in your custom renderers:
public class ExtendedLabelRenderer : LabelRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
// ...
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == ExtendedLabel.IsUnderlinedProperty.PropertyName)
{
RefreshControl(Control);
}
}
private void RefreshControl(UILabel control)
{
if (control == null) return;
// Apply based on IsUnderlined property
if (!string.IsNullOrEmpty(control.Text)) control.AttributedText = new NSMutableAttributedString(control.Text, underlineStyle: view.IsUnderlined ? NSUnderlineStyle.Single : NSUnderlineStyle.None);
// ...apply based on other custom properties
}
}
Add your label control to YourPage.xaml and add a gesture recognizer to handle tap:
<controls:ExtendedLabel
Text="View in browser"
TextColor="Blue"
IsUnderlined="True">
<controls:ExtendedLabel.GestureRecognizers>
<!--TODO:<TapGestureRecognizer Command="TapCommand" /> Couldn't do this for some reason so use Tapped handler-->
<TapGestureRecognizer
Tapped="OnOpenInBrowserTapGestureRecognizerTapped"
NumberOfTapsRequired="1" />
</controls:ExtendedLabel.GestureRecognizers>
</controls:ExtendedLabel>
Handle the tap in code behind YourPage.xaml.cs:
private void OnOpenInBrowserTapGestureRecognizerTapped(object sender, EventArgs args)
{
var labelViewSender = (ExtendedLabel)sender;
labelViewSender.Opacity = 0.6;
labelViewSender.FadeTo(1);
var viewModel = BindingContext as YourPageViewModel;
if (viewModel == null) return;
if (viewModel.NavigateToUrlCommand.CanExecute(null)) viewModel.NavigateToUrlCommand.Execute(null);
}
The view model:
public class YourPageViewModel : ViewModelBase
{
public const string NavigateToUrlMessage = "NavigateToUrl";
private string _url;
public string Url
{
get { return _url; }
set { SetProperty(ref _url, value); }
}
//...
private Command _navigateToUrlCommand;
public ICommand NavigateToUrlCommand
{
get { return _navigateToUrlCommand ?? (_navigateToUrlCommand = new Command(param => NavigateToUrl(), CanNavigateToUrl)); }
}
public bool CanNavigateToUrl(object parameter) => true;
private void NavigateToUrl()
{
MessagingCenter.Send(this, NavigateToUrlMessage, Url);
}
//...
}
Subscribe to and handle the NavigateToUrlMessage message in the YourPage.xaml.cs code behind:
protected override void OnAppearing()
{
MessagingCenter.Subscribe<YourPageViewModel, string>(this, YourPageViewModel.NavigateToUrlMessage, (sender, args) =>
{
var context = (BindingContext as YourPageViewModel);
if (context == null) return;
if (string.IsNullOrEmpty(args)) return;
Device.BeginInvokeOnMainThread(() =>
{
Device.OpenUri(new Uri(args));
});
});
//...
base.OnAppearing();
}
protected override void OnDisappearing()
{
MessagingCenter.Unsubscribe<YourPageViewModel, string>(this, YourPageViewModel.NavigateToUrlMessage);
//...
base.OnDisappearing();
}