Tuesday, 30 October 2018

Creating a Hyperlink in Xamarin.Forms II

Previously, I wrote about creating a hyperlink in a Xamarin.Forms app by adding a TapGestureRecognizer to the GestureRecognizers collection of a Span, and setting its TextDecorations property to Underline. This can be achieved in XAML as follows:

<Span Text="Xamarin documentation" TextColor="Blue" TextDecorations="Underline"> <Span.GestureRecognizers> <TapGestureRecognizer Command="{Binding TapCommand}" CommandParameter="https://docs.microsoft.com/xamarin/" /> </Span.GestureRecognizers> </Span>

The problem with this approach is that it requires repetitive code every time you need a hyperlink in your app. A better approach would be to sub-class the Span class into a HyperlinkSpan class, with the gesture recognizer and text decoration added there. Unfortunately, the Span class is sealed and so can’t be inherited from. However, there’s an enhancement proposal to unseal the class, so that it can be inherited from. Go and up vote it here!

Therefore, for the purposes of this blog post I’ll demonstrate sub-classing the Label class to create a HyperlinkLabel class. Once the Span class is unsealed, the same approach can be followed.

The sample this code comes from can be found on GitHub.

Creating a HyperlinkLabel Class

The following code shows the HyperlinkLabel class:

public class HyperlinkLabel : Label { public static readonly BindableProperty UrlProperty = BindableProperty.Create(nameof(Url), typeof(string), typeof(HyperlinkLabel), null); public string Url { get { return (string)GetValue(UrlProperty); } set { SetValue(UrlProperty, value); } } public HyperlinkLabel() { TextDecorations = TextDecorations.Underline; TextColor = Color.Blue; GestureRecognizers.Add(new TapGestureRecognizer { Command = new Command(() => Device.OpenUri(new Uri(Url))) }); } }

The class defines a Url property (and BindableProperty), and the class constructor sets the hyperlink look and the TapGestureRecognizer that will respond when the hyperlink is tapped. When a HyperlinkLabel is tapped, the TapGestureRecognizer will respond by executing the Device.OpenUri method to open the URL, specified by the Url property, in a web browser.

The HyperlinkLabel class can be consumed simply by adding an XML namespace declaration to your XAML, and then an instance of the class:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:HyperlinkLabel" x:Class="HyperlinkLabel.MainPage"> <StackLayout Margin="20"> <Label Text="Hyperlink Demo" HorizontalOptions="Center" FontAttributes="Bold" /> <Label Text="Click the text below to view Xamarin documentation." /> <local:HyperlinkLabel Text="Xamarin Documentation" Url="https://docs.microsoft.com/xamarin/" HorizontalOptions="Center" /> </StackLayout> </ContentPage>

The HyperlinkLabel instance is rendered as follows:

Simulator Screen Shot - iPhone 8 - 2018-10-29 at 11.37.55

It’s Url property is set to the URL to be opened when the hyperlink is tapped. When this occurs, a web browser appears and the URL is navigated to:

Simulator Screen Shot - iPhone 8 - 2018-10-29 at 11.38.04


Summary

The repetitive code for creating a hyperlink on a Span or Label should be sub-classed into a HyperlinkSpan or HyperlinkLabel class, with the gesture recognizer and text decoration added there. Unfortunately, the Span class is sealed and currently can’t be inherited from. However, there’s an enhancement proposal to unseal the Span class. Once this is achieved, the approach taken in this blog post for the HyperlinkLabel class can be applied to the Span class.

The sample this code comes from can be found on GitHub.

Thursday, 25 October 2018

Xamarin.Forms Compiled Bindings FAQ

We recently announced compiled bindings for Xamarin.Forms. Bindings aren’t cost efficient because they are resolved at runtime using reflection. In some scenarios this can introduce a performance hit. In addition, there isn’t any compile-time validation of binding expressions, and so invalid bindings aren’t detected until runtime.

Compiled bindings aim to improve data binding performance in Xamarin.Forms applications by resolving binding expressions at compile-time, rather than runtime. As well as providing compile-time validation of binding expressions, more importantly they eliminate the reflection used to resolve the bindings at runtime.

How compiled bindings are used is documented here. Rather than repeat all that information, I thought it might be useful to provide a high-level FAQ about them. So here goes.

How do compiled bindings work?

When you create a binding in Xamarin.Forms XAML, it’s accomplished with the Binding markup extension, which in turn creates a Binding, which in turn inherits from the BindingBase class. This is what we call a classic binding. When you create a compiled binding in Xamarin.Forms XAML, it’s accomplished with the Binding markup extension, which in turn creates a TypedBinding, which in turn inherits from the TypedBindingBase class, which in turn inherits from the BindingBase class. This inheritance hierarchy is shown in the following simplified class diagram:

bindings

Rather than accept a binding path (like the Binding constructor does), the TypedBinding constructor takes a Func that gets the value from the source, an Action that sets it, and a list of property changed handlers. XAMLC then takes the binding path from your XAML and uses it to create the TypedBinding for you. Also, the Binding markup extension knows whether to return a Binding or a TypedBinding.

Are compiled bindings more performant than classic bindings?

Yes. Classic bindings use reflection. Compiled bindings eliminate this reflection, replacing it with a Func to get data, an Action to set data, and a list of handlers for property change notification.

How much more performant are compiled bindings than classic bindings?

How long is a piece of string? Ultimately it depends on the platform, OS, and the device but internal testing has shown that compiled bindings can be resolved 8-20 times quicker than classic bindings. See Performance for more information.

Which version of Xamarin.Forms supports compiled bindings?

Compiled bindings have been present in Xamarin.Forms for a while, but at the time of writing I’d recommend using Xamarin.Forms 3.3 as more compiled binding scenarios are supported in this release.

Can you create TypedBinding objects in code?

Technically yes, but it's not recommended for the following reasons:

  1. While the TypedBinding type is public, it's not intended to be used by app developers. It's public purely because it's consumed by the IL generated by XAMLC. It should be thought of as internal.
  2. Consequently, the TypedBinding type deliberately won't appear in your IDE intelligence.
  3. Why would you want to? You end writing code like this:

var binding = new TypedBinding<ComplexMockViewModel, string>( cmvm => cmvm.Model.Model.Text, (cmvm, s) => cmvm.Model.Model.Text = s, new [] { new Tuple<Func<ComplexMockViewModel, object>, string>(cmvm=>cmvm, "Model"), new Tuple<Func<ComplexMockViewModel, object>, string>(cmvm=>cmvm.Model, "Model"), new Tuple<Func<ComplexMockViewModel, object>, string>(cmvm=>cmvm.Model.Model, "Text") }) { Mode = BindingMode.OneWay };


Why do compiled bindings require XAMLC?

XAMLC will take the binding path from your XAML binding expression and use it to create the TypedBinding for you. Thus avoiding writing code like above.

Do compiled bindings work when the BindingContext is set in code, rather than XAML?

Yes.

Should I replace my classic bindings with compiled bindings?

It’s up to you. If you don’t have a performance problem with your classic bindings, why bother replacing them? On the other hand, there’s no harm in replacing classic bindings with compiled bindings.

Really, the key scenario for replacing classic bindings with compiled bindings is where you’ve identified a performance problem. Maybe you have a performance problem in a ListView on Android. Try switching to compiled bindings there to see if it helps.

Can every classic binding be replaced with a compiled binding?

No. Compiled bindings are currently disabled for any binding expressions that define the Source property. This is because the Source property is always set using the x:Reference markup extension, which can’t currently be resolved at compile time.

Monday, 22 October 2018

Creating a Hyperlink in Xamarin.Forms

Creating a hyperlink in a Xamarin.Forms app has traditionally involved writing an effect or custom renderer, and having to provide platforms implementations as required.

Xamarin.Forms 3.2 introduced the ability to add a gesture recognizer to a Span, and Xamarin.Forms 3.3 introduced the ability to add a text decoration (such as underline) to a Span. Therefore, it’s now possible to easily add a tappable hyperlink to a Xamarin.Forms page. While this blog post focuses on achieving this with a Span, it’s equally directly applicable to a Label.

The sample this code comes from can be found on GitHub.

Creating a Hyperlink

The following XAML shows a Label that comprises three Span objects:

<Label> <Label.FormattedText> <FormattedString> <Span Text="Deliver native Android, iOS, and Windows apps with a single shared .NET code base. For more information, see " /> <Span Text="Xamarin documentation" TextColor="Blue" TextDecorations="Underline"> <Span.GestureRecognizers> <TapGestureRecognizer Command="{Binding TapCommand}" CommandParameter="https://docs.microsoft.com/xamarin/" /> </Span.GestureRecognizers> </Span> <Span Text="." /> </FormattedString> </Label.FormattedText> </Label>

The first and last Span objects simply comprise text, with the second Span object representing a tappable hyperlink. It has its colour set to blue, and has an underline text decoration. This creates the appearance of a hyperlink, as shown in the following screenshot:

Simulator Screen Shot - iPhone 8 - 2018-10-22 at 12.25.51

More importantly, the second Span has a TapGestureRecognizer in its GestureRecognizer collection. Therefore, when this Span is tapped the TapGestureRecognizer will respond by executing the ICommand defined by the Command property. In addition, the URI specified by the CommandParameter property will be passed to the ICommand as a parameter.

The code-behind for the page contains the TapCommand implementation:

public partial class MainPage : ContentPage { public ICommand TapCommand => new Command<string>(OpenBrowser); public MainPage() { InitializeComponent(); BindingContext = this; } void OpenBrowser(string url) { Device.OpenUri(new Uri(url)); } }

The TapCommand simply executes the OpenBrowser method, passing the CommandParameter property value as a parameter. In turn, this method calls the Device.OpenUri method, that’s included in Xamarin.Forms, to open the URI in a web browser. Therefore, the overall effect is that when the hyperlink is tapped on the page, a web browser appears and the URI associated with the hyperlink is navigated to:

Simulator Screen Shot - iPhone 8 - 2018-10-22 at 12.27.34

Summary

Xamarin.Forms 3.2 introduced the ability to add a gesture recognizer to a Span, and Xamarin.Forms 3.3 introduced the ability to add a text decoration (such as underline) to a Span. Therefore, it’s now possible to easily add a tappable hyperlink to a Xamarin.Forms page.

The sample this code comes from can be found on GitHub.