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.

Monday, 23 July 2018

Binding a Collection to a FlexLayout IV

Previously, I explained how to extend the ExtendedFlexLayout class so that it can bind to collections that change at runtime, with the changes being reflected on screen. While the ExtendedFlexLayout class only handles items being added to and removed from the bound collection at runtime, I mentioned that it could easily be extended to handle additional actions by examining the Action property.

In this blog post, I’ll examine how to extend the ExtendedFlexLayout class so that it can respond to the bound collection being cleared at runtime. The sample this code comes from can be found on GitHub.

Clearing the Collection

The ExtendedFlexLayout can respond to the bound collection being cleared at runtime in the handler for the CollectionChanged event:

void OnItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.Action == NotifyCollectionChangedAction.Reset) { // Items cleared Children.Clear(); } if (e.OldItems != null) { // Items removed Children.RemoveAt(e.OldStartingIndex); } if (e.NewItems != null) { // Item(s) added. for (int i = 0; i < e.NewItems.Count; i++) { var item = e.NewItems[i]; var view = CreateChildView(item); Children.Insert(e.NewStartingIndex + i, view); } } }

At the top of the event handler, the received NotifyCollectionChangedEventArgs are used to determine whether to clear the ExtendedFlexLayout control. The event argument data includes an Action property, which identifies whether an item has been added, moved, removed, replaced, or whether the collection has been cleared. If the bound collection has been cleared, the Action property will be set to NotifyCollectionChangedAction.Reset and the control responds by clearing the Children collection. When the Children collection has been cleared, both the OldItems (the list of items that have been replaced, removed, or moved within the collection) and NewItems (the list of items that have been added to the collection) properties will be null, so the code for adding and removing items won’t be executed on this invocation of the event handler.

Summary

This blog post has explained how to further extend the ExtendedFlexLayout class so that it can respond to the bound collection being cleared at runtime. This is achieved by clearing the Children collection of the control when the Action property of the handler that responds to the CollectionChanged event is equal to NotifyCollectionChangedAction.Reset.

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

Monday, 2 July 2018

Binding a Collection to a FlexLayout III

Previously, I explained how to extend the ExtendedFlexLayout class so that it allows a DataTemplateSelector to choose a DataTemplate at runtime. This enables scenarios such as the ExtendedFlexLayout class binding to a collection of objects where the appearance of each object can be chosen at runtime by the data template selector returning a particular DataTemplate.

Any items in the collection the ExtendedFlexLayout.ItemsSource property binds will be displayed on screen. However, if the collection changes at runtime, the changes aren’t displayed. It’s useful to think about how a collection can change at runtime - items could be added, moved, removed, or replaced. In addition, the collection could be cleared.

In this blog post I’ll examine how to extend the ExtendedFlexLayout class so that it can bind to a collection that changes at runtime, by responding to collection change notifications, with the changes being reflected on screen. In this implementation, collection changes will only be reflected on screen when items are added to the collection, or removed from the collection.

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

Adding CollectionChanged Support

The key to responding to a bound collection changing at runtime is subscribing to the CollectionChanged event of the collection, and then updating the Children property of the ExtendedFlexLayout class in response. An event handler for the collection’s CollectionChanged event can be registered in the OnItemsSourceChanged method, which is executed whenever the ItemsSource property changes:

1 static void OnItemsSourceChanged(BindableObject bindable, object oldVal, object newVal) 2 { 3 IEnumerable newValue = newVal as IEnumerable; 4 var layout = (ExtendedFlexLayout)bindable; 5 6 var observableCollection = newValue as INotifyCollectionChanged; 7 if (observableCollection != null) 8 { 9 observableCollection.CollectionChanged += layout.OnItemsSourceCollectionChanged; 10 } 11 12 layout.Children.Clear(); 13 if (newValue != null) 14 { 15 foreach (var item in newValue) 16 { 17 layout.Children.Add(layout.CreateChildView(item)); 18 } 19 } 20 }

Lines 6-10 contain the new code that’s been added to this method. This code casts the bound collection to INotifyCollectionChanged, and then registers an event handler for the CollectionChanged even. This event fires whenever an item is added, removed, changed, or moved in the bound collection, or when the collection is cleared. Other than that, the code in the method hasn’t changed.

The OnItemsSourceCollectionChanged method, which handles the CollectionChanged event, is shown in the following example:

void OnItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.OldItems != null) { // Item(s) replaced, moved, or removed - only remove processed here. Children.RemoveAt(e.OldStartingIndex); } if (e.NewItems != null) { // Item(s) added. for (int i = 0; i < e.NewItems.Count; i++) { var item = e.NewItems[i]; var view = CreateChildView(item); Children.Insert(e.NewStartingIndex + i, view); } } }

This event handler uses the received NotifyCollectionChangedEventArgs argument to determine the action to perform. The event argument data includes an Action property, which identifies whether an item has been added, moved, removed, replaced, or whether the collection has been cleared. However, it’s not always necessary to use this property to handle the collection changing. Instead, the OldItems and NewItems properties can be examined to partly determine the action required.

The OldItems property contains the list of items that have been replaced, removed, or moved within the collection, and the OldStartingIndex property returns the index at which the replace, remove, or move action took place. The NewItems property contains a list of items added to the collection, with the NewStartingIndex property returning the index at which the change occurred.

In the code above, if the OldItems property contains an item, it’s removed from the ExtendedFlexLayout.Children collection. If the NewItems property contains items, they are added to the ExtendedFlexLayout.Children collection. This is achieved by calling the CreateChildView method, which loads the DataTemplate and sets its binding context, with the resulting view being inserted into the Children collection.

Therefore, the ExtendedFlexLayout class only handles items being added to and removed from the bound collection at runtime (and not items being moved, replaced, or the collection being cleared), but it can easily be extended to handle additional actions by examining the Action property. In fact, I will return to this property in my next post.

Consuming the ExtendedFlexLayout

The ExtendedFlexLayout is consumed exactly as before:

<local:ExtendedFlexLayout ItemsSource="{Binding Monkeys}" ItemTemplate="{StaticResource monkeyDataTemplateSelector}" />

However, the ItemsSource property now binds to an ObservableCollection. When the MainPageViewModel is constructed, an instance of the ObservableCollection is created and a single Monkey is added to it. The ObservableCollection can then be manipulated by buttons on the UI (adding and removing Monkeys from the collection), with the ExtendedFlexLayout being updated in response:


Simulator Screen Shot - iPhone 8 - 2018-06-22 at 13.56.07

Summary

This blog post has explained how to further extend the ExtendedFlexLayout class so that it can bind to collections that change at runtime, with the changes being reflected on screen. While the ExtendedFlexLayout class only handles items being added to and removed from the bound collection at runtime (and not items being moved, replaced, or the collection being emptied), it can easily be extended to handle additional actions by examining the Action property.

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

Tuesday, 19 June 2018

Binding a FlexLayout to a Collection II

Previously, I explained how to extend the FlexLayout class with ItemsSource and ItemTemplate properties, so that it can bind to data stored in a collection. However, the resulting ExtendedFlexLayout class is only a minimally viable implementation as it omits a number of required features.

One of the missing features is that it doesn’t allow a DataTemplateSelector to choose a DataTemplate at runtime based on the value of a bound property. Instead it only permits a single defined DataTemplate to be used.

The purpose of this blog post is to further extend the ExtendedFlexLayout class so that it does allow a DataTemplateSelector to choose a DataTemplate at runtime. This enables scenarios such as the ExtendedFlexLayout binding to a collection of objects where the appearance of each object can be chosen at runtime by a data template selector returning a particular DataTemplate.

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

Adding DataTemplateSelector Support

As I explained previously, the ExtendedFlexLayout class has a CreateChildView method, which is called for each item in the collection that the ItemsSource property binds to, to load the DataTemplate referenced by the ItemTemplate property and set its binding context:

View CreateChildView(object item) { ItemTemplate.SetValue(BindableObject.BindingContextProperty, item); return (View)ItemTemplate.CreateContent(); }

This method can be extended to support a DataTemplateSelector choosing a DataTemplate at runtime:

View CreateChildView(object item) { if (ItemTemplate is DataTemplateSelector) { var dts = ItemTemplate as DataTemplateSelector; var itemTemplate = dts.SelectTemplate(item, null); itemTemplate.SetValue(BindableObject.BindingContextProperty, item); return (View)itemTemplate.CreateContent(); } else { ItemTemplate.SetValue(BindableObject.BindingContextProperty, item); return (View)ItemTemplate.CreateContent(); } }

This additional code checks if the ItemTemplate property references a class that inherits from DataTemplateSelector. If it is, it calls the DataTemplateSelector.SelectTemplate method to return the particular DataTemplate. Then, as before, the DataTemplate is loaded and its binding context is set.

Consuming the ExtendedFlexLayout

A data template selector is implemented by creating a class that inherits from DataTemplateSelector. The OnSelectTemplate method is then overridden to return a particular DataTemplate, as shown in the following code example:

public class MonkeyDataTemplateSelector : DataTemplateSelector { public DataTemplate NormalMonkeyTemplate { get; set; } public DataTemplate CynicalMonkeyTemplate { get; set; } protected override DataTemplate OnSelectTemplate(object item, BindableObject container) { return ((Monkey)item).Name.Contains("Face-Palm") ? CynicalMonkeyTemplate : NormalMonkeyTemplate; } }

Here, the OnSelectTemplate method returns the appropriate template based on the value of the Name property. The template to return is the template referenced by the NormalMonkeyTemplate property or the CynicalMonkeyTemplate, which are set when consuming the MonkeyDataTemplateSelector.

The MonkeyDataTemplateSelector is instantiated by declaring it as a resource:

<ContentPage.Resources> <Style x:Key="normalMonkeyFrame" TargetType="Frame"> <Setter Property="BackgroundColor" Value="LightYellow" /> <Setter Property="BorderColor" Value="Blue" /> <Setter Property="Margin" Value="10" /> <Setter Property="CornerRadius" Value="15" /> </Style> <Style x:Key="cynicalMonkeyFrame" TargetType="Frame"> <Setter Property="BackgroundColor" Value="Red" /> <Setter Property="BorderColor" Value="Black" /> <Setter Property="Margin" Value="10" /> <Setter Property="CornerRadius" Value="15" /> </Style> ... <DataTemplate x:Key="normalMonkeyTemplate"> <Frame WidthRequest="300" HeightRequest="480" Style="{StaticResource normalMonkeyFrame}"> ... </Frame> </DataTemplate> <DataTemplate x:Key="cynicalMonkeyTemplate"> <Frame WidthRequest="300" HeightRequest="480" Style="{StaticResource cynicalMonkeyFrame}"> ... </Frame> </DataTemplate> <local:MonkeyDataTemplateSelector x:Key="monkeyDataTemplateSelector" NormalMonkeyTemplate="{StaticResource normalMonkeyTemplate}" CynicalMonkeyTemplate="{StaticResource cynicalMonkeyTemplate}" /> </ContentPage.Resources>

The ResourceDictionary above defines two DataTemplate instances, and a MonkeyDataTemplateSelector instance. The MonkeyDataTemplateSelector instance sets its NormalMonkeyTemplate and CynicalMonkeyTemplate properties to the appropriate DataTemplate instances through the StaticResource markup extension. The two DataTemplates are largely identical, aside from setting different colours on the Frame instances.

The MonkeyDataTemplateSelector instance is consumed by assigning it to the ExtendedFlexLayout.ItemTemplate property:

<local:ExtendedFlexLayout ItemsSource="{Binding Monkeys}" ItemTemplate="{StaticResource monkeyDataTemplateSelector}" />

At runtime, the MonkeyDataTemplateSelector.OnSelectTemplate method is called (by the ExtendedFlexLayout class via the DataTemplateSelector.SelectTemplate method call) for each of the items in the underlying collection, with the call passing the data object as the item parameter. The DataTemplate that is returned by the method is then applied to that object.

The following screenshots show the result of the ExtendedFlexLayout applying the MonkeyDataTemplateSelector to each object in the underlying collection:


Simulator Screen Shot - iPhone 8 - 2018-06-18 at 16.09.05Simulator Screen Shot - iPhone 8 - 2018-06-18 at 16.09.10Simulator Screen Shot - iPhone 8 - 2018-06-18 at 16.09.15

Any Monkey object that has a Name property that contains “Face-Palm” is displayed with a red background, as a warning to be wary of cynical monkeys, with the remaining objects being displayed with a a light yellow background.

Summary

This blog post has explained how to further extend the ExtendedFlexLayout class so that it allows a DataTemplateSelector to choose a DataTemplate at runtime. This enables scenarios such as the ExtendedFlexLayout binding to a collection of objects where the appearance of each object can be chosen at runtime by the data template selector returning a particular DataTemplate. My next blog post will look at further extending the ExtendedFlexLayout class with additional functionality.

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

Thursday, 14 June 2018

Binding a FlexLayout to a Collection

In May we published a doc on the new FlexLayout control that’s present in Xamarin.Forms 3.0. FlexLayout is a versatile layout control that can arrange its children horizontally and vertically in a stack, and is also capable of wrapping its children if there are too many to fit in a single row or column. It also has options for orientation, alignment, and adapting to various screen sizes.

In the FlexLayout guide we outlined some common usage scenarios, with one being a catalog of items that are displayed horizontally, which are navigated through by swiping in the appropriate direction. Each item in the catalog is defined inline in XAML as children of the FlexLayout element. This is all very well for static catalog items that are defined by the developer, but what if the data is stored in a collection that’s populated from an external source such as a web service, or a database? This would require the FlexLayout to bind to a collection containing the data. However, this isn’t currently possible as there’s no ItemsSource property (or similar) on the FlexLayout class.

The purpose of this blog post is to demonstrate extending the FlexLayout with ItemsSource and ItemTemplate properties, so that it can bind to data stored in a collection. Note that the implementation will be a minimally viable implementation, rather than a production ready implementation. It demonstrates the simplest approach for binding a FlexLayout to a collection of items.

The sample the code in this blog post comes from can be found on GitHub.

Extending FlexLayout

The first step is to create a new class that inherits from FlexLayout, and add the required bindable properties and properties:

public class ExtendedFlexLayout : FlexLayout { public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create( nameof(ItemsSource), typeof(IEnumerable), typeof(ExtendedFlexLayout), propertyChanged: OnItemsSourceChanged); public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create( nameof(ItemTemplate), typeof(DataTemplate), typeof(ExtendedFlexLayout)); public IEnumerable ItemsSource { get { return (IEnumerable)GetValue(ItemsSourceProperty); } set { SetValue(ItemsSourceProperty, value); } } public DataTemplate ItemTemplate { get { return (DataTemplate)GetValue(ItemTemplateProperty); } set { SetValue(ItemTemplateProperty, value); } } ... }

The code above simply defines ItemsSource and ItemTemplate properties, along with matching BindableProperty versions of them. The ItemTemplate property will reference the DataTemplate to apply to each item in the collection referenced by the ItemsSource property. Note that the ItemsSourceProperty has a property changed handler defined, named OnItemsSourceChanged. This is where the templated items will be added to the ExtendedFlexLayout:

public class ExtendedFlexLayout : FlexLayout { ... static void OnItemsSourceChanged(BindableObject bindable, object oldVal, object newVal) { IEnumerable newValue = newVal as IEnumerable; var layout = (ExtendedFlexLayout)bindable; layout.Children.Clear(); if (newValue != null) { foreach (var item in newValue) { layout.Children.Add(layout.CreateChildView(item)); } } } View CreateChildView(object item) { ItemTemplate.SetValue(BindableObject.BindingContextProperty, item); return (View)ItemTemplate.CreateContent(); } }

The OnItemsSourceChanged method iterates through the collection referenced by the ItemsSource property, and calls the CreateChildView method for each item. This method sets the binding context of the ItemTemplate to the item, loads the DataTemplate referenced by the ItemTemplate property, and returns it to the OnItemsSourceChanged method, where the templated item is added as a child of the ExtendedFlexLayout.

Consuming the ExtendedFlexLayout

The ExtendedFlexLayout can be consumed in XAML as follows:

<ScrollView Orientation="Horizontal" Margin="0,20"> <local:ExtendedFlexLayout ItemsSource="{Binding Monkeys}"> <local:ExtendedFlexLayout.ItemTemplate> <DataTemplate> <Frame WidthRequest="300" HeightRequest="480"> <FlexLayout Direction="Column"> <Label Text="{Binding Name}" Style="{StaticResource headerLabel}" /> <Label Text="{Binding Description}" /> <Label Text="{Binding Trait1}" Margin="10,0,0,0" /> <Label Text="{Binding Trait2}" Margin="10,0,0,0" /> <Label Text="{Binding Trait3}" Margin="10,0,0,0" /> <Image Source="{Binding Image, Converter={StaticResource _stringToImageConverter}}" WidthRequest="180" HeightRequest="180" /> <Label FlexLayout.Grow="1" /> <Button /> </FlexLayout> </Frame> </DataTemplate> </local:ExtendedFlexLayout.ItemTemplate> </local:ExtendedFlexLayout> </ScrollView>

This ExtendedFlexLayout instance sets its ItemsSource property to a collection named Monkeys, which exists on the view model the page binds to. It also sets its ItemTemplate property to an inline DataTemplate that binds different views to different properties of each Monkey in the Monkeys collection. The result is, as per the FlexLayout guide, there are three items displayed that can be navigated through by swiping in the appropriate direction:

Simulator Screen Shot - iPhone 8 - 2018-06-14 at 11.57.59

The difference between the FlexLayout guide and this approach is that in the guide each item is declared inline in XAML. Here, each item comes from a collection that the ExtendedFlexLayout binds to. The advantage of this approach is that it allows the displayed data to be populated from an external source, such as a web service or a database.

Issues

As I previously mentioned, the ExtendedFlexLayout is a minimally viable implementation. While it works for the scenario outlined here, it’s quite limited:

  • It doesn’t respond to the bound collection changing at runtime (think ObservableCollection). Instead, the entire collection must be available when binding occurs.
  • It doesn’t respond to binding context changes. The binding context must be set when binding occurs, and can’t change.
  • It only permits a single defined DataTemplate to be used. It doesn’t allow a DataTemplateSelector to choose a DataTemplate at runtime based on the value of a bound property.
  • There’s no UI virtualisation. For large collections, the ExtendedFlexLayout could consume a lot of memory.

I’ll explore some of these issues in future blog posts.

Summary

This blog post has explained how to extend the FlexLayout with ItemsSource and ItemTemplate properties, so that it can bind to data stored in a collection. However, the ExtendedFlexLayout is currently a minimally viable implementation. My next blog post will look at extending the implementation with additional functionality.

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

Tuesday, 12 June 2018

Xamarin.Forms Behaviors: InvokeCommandAction and ConverterParameter

I previously mentioned that I’d published v1.4 of my Behaviors library, and that it included some new functionality. In my last blog post I looked at the SourceObject property that’s now present on the EventHandlerBehavior class. The final new item is the ConverterParameter property that now exists on the InvokeCommandAction class.

The InvokeCommandAction class executes a specified ICommand when invoked. It requires you to set a Command property to an ICommand instance, and CommandParameter and Converter properties can be optionally set. The CommandParameter property should be set to an object instance, with the Converter property being set to an instance of a class that implements IValueConverter. The ICommand specified in the Command property will then be executed when the “something” the parent behavior listens for occurs, with the CommandParameter and Converter values being used if specified.

Introducing the ConverterParameter Property

In v1.4 of the Behaviors library, the InvokeCommandAction class also has an optional ConverterParameter property, of type object. When this property is set, its value is passed to the Convert and ConvertBack methods of the IValueConverter implementation, as the parameter argument.

The following XAML shows an example of the InvokeCommandAction passing a parameter to the converter:

<ListView x:Name="listView" ItemsSource="{Binding People}"> <ListView.Behaviors> <behaviors:EventHandlerBehavior EventName="ItemSelected"> <behaviors:InvokeCommandAction Command="{Binding OutputAgeCommand}" Converter="{StaticResource SelectedItemConverter}" ConverterParameter="35" /> </behaviors:EventHandlerBehavior> </ListView.Behaviors> </ListView>

When the ListView.ItemSelected event fires, the OutputAgeCommand is executed. The InvokeCommandAction class expects to find the ICommand on the BindingContext of the attached object (and the BindingContext may have been set by a parent element). The Converter property of the InvokeCommandAction instance is set to the SelectedItemConverter instance, and the value of the ConverterParameter property is passed to the SelectedItemConverter instance as the parameter argument:

public class SelectedItemEventArgsToSelectedItemConverter : IValueConverter { public object Convert (object value, Type targetType, object parameter, CultureInfo culture) { var eventArgs = value as SelectedItemChangedEventArgs; var person = eventArgs.SelectedItem as Person; if (parameter != null) { int ageParam = int.Parse(parameter.ToString()); person = new Person(person.Name, person.Age, ageParam); } return person; } ... }

The converter returns the SelectedItem of the ListView from the SelectedItemChangedEventArgs. If the parameter argument contains data, it’s converted to an int and a new Person object is constructed that contains the int value of parameter. The InvokeCommandDemoPageViewModel then uses the Person object to update the AgeText property (which the UI binds to) to a message to be output:

void OutputAge (Person person) { if (person.AgeParameter > person.Age) AgeText = string.Format("{0} is {1}. That's younger than {2}.", person.Name, person.Age, person.AgeParameter); else AgeText = string.Format("{0} is {1}. That's older than {2}.", person.Name, person.Age, person.AgeParameter); OnPropertyChanged ("AgeText"); }

While this is a contrived example, it does illustrate how the ConverterParameter property can be used to pass a parameter to the value converter, that can be used in the conversion process.

Note that even if the instance of the value converter is shared among several data bindings, the ConverterParameter property can be different to perform different conversions.

Summary

In v1.4 of the Behaviors library, the InvokeCommandAction class also has an optional ConverterParameter property, of type object. When this property is set, its value is passed to the Convert and ConvertBack methods of the IValueConverter implementation, as the parameter argument.

The sample application that this code comes from can be downloaded from GitHub, and the Behaviors library can be found on NuGet.