A little and simple Bindable (Horizontal) Scroll View

In 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

Full code is available here

 

  • Earl

    Very nice! The only workable solution to HorizontalScrollView that I have found. Thanks much.

  • Robert

    Thanks for this post. How to detect the tapped item?

  • Pingback: A little and simple Bindable (Horizontal) Scroll View - Handling item tap gesture - Fabio Cozzolino's blog()

  • Pingback: A little and simple Bindable (Horizontal) Scroll View - Add Command pattern - Fabio Cozzolino's blog()

  • Andres Londoño

    This is awesome. thanks

  • Carlos Mendez

    Hi thanks for this great code, just one question, for Shared project would be the same?

    • Hi Carlos, yes, it’s absolutely the same. You must be only careful with assembly reference in xaml for the xmlns:controls alias.

      • Carlos Mendez

        Cool!!! I’m trying to do it on a Shared with Observable Collection!!! Hope I get it to work!!!!

      • Carlos Mendez

        Thanks so much for this code, It made my day, I was just wondering if I can hide the scroll bar of the component?

    • Aly EL Kholy

      hi Item source is not update how can i solve it?

  • Knela

    How do you reference the images, displaying them in the scrollvirew(i’m really newbie in xamarin)

  • Frans Hidayat

    nice article! thanks man..

  • Ashley Ewen

    This is great, but im having some issues when binding to an observable collection. If the item source is defined at runtime the data is displayed. However if i populate the collection using online data (i.e. through an api) I cannot get the data to display. Ive checked the collection has the data, and I have forced a property change event in the view model but the items in the view are not displaying. Any ideas?

  • Chris vard

    Hi @disqus_ftNzdmLHQ1:disqus . I have this to work and it’s great, but I want to be able to change to background color of the ViewCell when it is clicked. Could you give me a hint on how to achieve this as I’m having trouble getting it to change