January 11th, 2019

Xamarin.Forms 3.5: A Little Bindable Love

This is a guest post contributed by Andrei Nitescu, a mobile developer since 2013 and frequent contributor to Xamarin.Forms. He shares about his experiences with other developers on his blog, and delivering talks whenever he has the chance.

Xamarin.Forms 3.5

Xamarin.Forms 3.5-pre2 is now available in pre-release on NuGet and via your Visual Studio NuGet package manager. For a complete list of improvements including Andrei’s BindableLayout contribution, review the full release notes.

I’d like to share with you some information about one of my contributions to Xamarin.Forms, the bindable layout. As a developer, I love the potential of the layout views to create visually rich views that delight our app users. Xamarin.Forms, the cross-platform mobile application framework, it’s more straightforward to create such custom views that work across all mobile platforms. Xamarin.Forms allows us to write the layout logic to set the size and position of the views only once.

While the “binding layout” name might suggest that it’s an addition to the base Layout view class, it actually works as an adaptor. It takes a source of data items and calls the Layout<T> view to create and update its child views based on the changes in the data item source.

Layouts, a Quick Refresh

(Skip this section and the next one if you already know how layouts and data-binding to layouts work. There is no new information on that.)

Xamarin.Forms has a special View called Layout. The Layout class is a container for other Views and has a specific mission: to position and size its child Views. Here’s the class diagram with all the available layouts in the framework today:

The Layout<T> class is a specialized Layout which exposes a Children View collection. Therefore, we can add, remove, or replace child Views to fit our design requirements:

StackLayout stackLayout =…;

stackLayout.Children.Add(new Label() { Text = … });

Layout<T> is the base class for most common layouts some of which you’ve already been using, like the StackLayout or Grid for example. The Layout<T> makes it possible to create your own layout to size and position child views any way you want; it’s really up to your imagination. There’s excellent documentation and samples on how to build your own custom layouts. Check out Creating a Custom Layout.

Layout + Data Binding

When creating the item views for a Layout based on a source of data items, you may have code that looks similar to this:

void UpdateItems (StackLayout layout, IEnumerable newDataItems)
{
    layout.Children.Clear ();
    foreach (object dataItem in newDataItems) {
        View itemView = CreateItemView(dataItem);
        layout.Children.Add (itemView);
    }
}

However, wiring up a collection of data items to a layout view needs to take into account and handle several aspects. For example, we need to check if the data item collection is observable (it implements INotifyCollectionChanged). In this case, it subscribes to the collection change event. If the collection instance is replaced, we need to make sure we remove it by listening to the old collection. We also need to handle creating the item view for each data item. Then bind the item view to the data item. Sometimes we may also want the item view to be different depending on the data item.

Bindable Layout

Starting with Xamarin.Forms 3.5.0 pre2, we can use the BindableLayout set of attached properties on any Layout<T> view:

  • IEnumerable ItemsSource { get; set; }
  • DataTemplate ItemTemplate{ get; set; }
  • DataTemplateSelector ItemTemplateSelector{ get; set; }

These should be familiar to you if you’ve used a ListView before.

The simplest usage is to set a data items source. This automatically creates a Label child View for every data item in the item source. It makes sense to use it when the data source is a collection of strings:

XAML:

<StackLayout BindableLayout.ItemsSource=”{Binding Sports}” />

C#:

IEnumerable sportsSource = …;
BindableLayout.SetItemsSource(sportsPanel, sportsSource);

When the default Label doesn’t suffice, you can configure a custom template which the Layout View uses to create a child View for every data item in the item source:

XAML:

<StackLayout BindableLayout.ItemsSource="{Binding Users}">
    <BindableLayout.ItemTemplate>
        <DataTemplate>
            <StackLayout Orientation="Horizontal">
                <Image Source="{Binding Avatar}" />
                <Label Text="{Binding Name}" />
            </StackLayout>
        </DataTemplate>
    </BindableLayout.ItemTemplate>
</StackLayout>

C#:

DataTemplate useItemTemplate = null;
BindableLayout.SetItemTemplate(usersPanel, userItemTemplate);

More information on creating and using DataTemplates can be found here: Creating a Xamarin.Forms DataTemplate.

If you want to have the Layout create the child View depending on the data item, you can then set a DataTemplateSelector:

XAML:

<FlexLayout BindableLayout.ItemsSource="{Binding Users}"
            BindableLayout.ItemTemplateSelector="{StaticResource UserItemTemplateSelector}" />

C#:

DataTemplateSelector userItemTemplateSelector = …;
BindableLayout.SetItemTemplateSelector(usersPanel, userItemTemplateSelector);

For more information on how to create and use a DataTemplateSelector, please check out Creating a Xamarin.Forms DataTemplateSelector.

Vertical List

When it comes to binding a source of items and displaying it in a vertical list, the ListView has the same features. Its behavior is different and it’s important to understand its limitations:

  • ListView is inherently a scrollable container. Unless you set a specific height, it takes as much height as possible, because its nature is to display a scrollbar in order to scroll the item views. Having a scrollable view along with other views on a scrollable page is not desirable for your users. There are workarounds to make a ListView display without scrolling. By setting the right height based on the containing views for example, but it’s not pretty or ideal.
  • The StackLayout for example, in contrast, doesn’t have this issue. It takes as much space as it needs for its child elements on the direction of its orientation (vertical/horizontal), so it computes its size based on its child views.
  • In ListView, the items are selectable and provide a visual feedback when items are pressed/released. ListView can disable selection (SelectionMode=”None”), but it does not eliminate the highlight when tapping the item. More tweaking is needed and it requires renderers.
  • You could try to disable the interaction with the a ListView. This way both the scrolling and selection won’t be an issue anymore, however this won’t work in the case that you want your users to interact with the items.
  • Because of its more complex features, the ListView is heavier to render than the StackLayout, for example.

Upcoming CollectionView

Like I mentioned earlier, when using the current built-in Layouts available on the framework (StackLayout, FlexLayout, Grid, etc.), the bindable layout functionality is useful when you know that the items source is of few items. The items display must also not be scrollable or selectable. You can always wrap the layout with a ScrollView, though in the future a much better option would be to use CollectionView instead. This does exactly what you need and it’s more efficient.

Try it Today!

I put together a sample app (screenshot below) which uses different bindable layouts in various scenarios that I think it makes sense. Check out this BindableLayoutsApp on Github.

0 comments

Discussion are closed.