A little and simple Bindable (Horizontal) Scroll View
10 Jan 2016In one project I was working on, a page needs to show a short horizontal and scrollable list of items (something like the Apple App Store app). Xamarin.Forms offers the ability to use a ScrollView control to show a list of items horizontally but, in my case, the items I must to show comes from a dinamically populated list. In that case I’ve extended the ScrollView control and added the ItemsSource and ItemTemplate property.
public class TLScrollView : ScrollView
{
public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create("ItemsSource", typeof(IEnumerable), typeof(TLScrollView), default(IEnumerable));
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly BindableProperty ItemTemplateProperty =
BindableProperty.Create("ItemTemplate", typeof(DataTemplate), typeof(TLScrollView), default(DataTemplate));
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
}
After that, I’ve added a simple method, Render, to populate the ScrollView:
public void Render ()
{
if (this.ItemTemplate == null || this.ItemsSource == null)
return;
var layout = new StackLayout ();
layout.Orientation = this.Orientation == ScrollOrientation.Vertical
? StackOrientation.Vertical : StackOrientation.Horizontal;
foreach (var item in this.ItemsSource) {
var viewCell = this.ItemTemplate.CreateContent () as ViewCell;
viewCell.View.BindingContext = item;
layout.Children.Add (viewCell.View);
}
this.Content = layout;
}
That method will be called from the TLScrollViewRenderer (iOS and Android are pretty similar). The renderer is a special class that adapts the Xamarin.Forms control into a native control:
[assembly: ExportRenderer(typeof(TLScrollView), typeof(TLScrollViewRenderer))]
namespace TitiusLabs.Forms.iOS.Controls
{
public class TLScrollViewRenderer : ScrollViewRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
var element = e.NewElement as TLScrollView;
element?.Render();
}
}
}
Now you can use the TLScrollView in your xaml and create your custom template in this way:
<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:controls="clr-namespace:TitiusLabs.Forms.Controls;assembly=TitiusLabs.Forms" xmlns:local="clr-namespace:FormSamples" x:Class="FormSamples.Core.Views.FormSamplesPage">
<StackLayout Padding="20">
<controls:TLScrollView Orientation="Horizontal" ItemsSource="{Binding Items}" HeightRequest="100">
<controls:TLScrollView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="5">
<controls:TLImageCircle Source="{Binding Image}" HeightRequest="80" WidthRequest="80" />
</StackLayout>
</ViewCell>
</DataTemplate>
</controls:TLScrollView.ItemTemplate>
</controls:TLScrollView>
</StackLayout>
</ContentPage>
This is the final result:
Amazing!
**UPDATE 12.04.2016: some small refactoring to the custom horizontal ScrollView control
**