A ListView is a kind of repeater but isn’t always what I want. It’s surprising something like this isn’t included in the framework but making your own is fairly simple.
namespace PSC.Controls { /// <summary> /// Repeater view. /// </summary> public class RepeaterView : StackLayout { /// <summary> /// The item template property. /// </summary> public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create( "ItemTemplate", typeof(DataTemplate), typeof(RepeaterView), null, propertyChanged: (bindable, value, newValue) => Populate(bindable)); /// <summary> /// The items source property. /// </summary> public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create( "ItemsSource", typeof(IEnumerable), typeof(RepeaterView), null, BindingMode.OneWay, propertyChanged: (bindable, value, newValue) => Populate(bindable)); /// <summary> /// Gets or sets the items source. /// </summary> /// <value>The items source.</value> public IEnumerable ItemsSource { get => (IEnumerable)this.GetValue(ItemsSourceProperty); set => this.SetValue(ItemsSourceProperty, value); } /// <summary> /// Gets or sets the item template. /// </summary> /// <value>The item template.</value> public DataTemplate ItemTemplate { get => (DataTemplate)this.GetValue(ItemTemplateProperty); set => this.SetValue(ItemTemplateProperty, value); } /// <summary> /// Populate the specified bindable. /// </summary> /// <returns>The populate.</returns> /// <param name="bindable">Bindable.</param> private static void Populate(BindableObject bindable) { var repeater = (RepeaterView)bindable; // Clean repeater.Children.Clear(); // Only populate once both properties are received if (repeater.ItemsSource == null || repeater.ItemTemplate == null) return; foreach (var viewModel in repeater.ItemsSource) { var content = repeater.ItemTemplate.CreateContent(); if (!(content is View) && !(content is ViewCell)) { throw new Exception( $"Invalid visual object {nameof(content)}"); } var view = content is View ? content as View : ((ViewCell)content).View; view.BindingContext = viewModel; repeater.Children.Add(view); } } } }
The view is based on StackLayout which will take care of positioning the items.
There are a couple of bindable properties – ItemTemplate for the item layout and ItemsSource which provides the items. Note that ItemsSource hooks an action to PropertyChanging – I’ll come back to this later on.
Usage in XAML is similar to ListView, with an ItemTemplate, DataTemplate, and ViewCell.
<ui:Repeater x:Name="MainRepeater"> <ui:Repeater.ItemTemplate> <DataTemplate> <ViewCell> <StackLayout Orientation="Horizontal"> <Label Text="{Binding Title}" HorizontalOptions="StartAndExpand" VerticalTextAlignment="Center"/> <Button Text="Select" /> </StackLayout> </ViewCell> </DataTemplate> </ui:Repeater.ItemTemplate> </ui:Repeater>
Don’t forget to define ui
<ContentPage xmlns="https://xamarin.com/schemas/2014/forms" xmlns:x="https://schemas.microsoft.com/winfx/2009/xaml" xmlns:ui="clr-namespace:PSC.Controls;assembly=PSC.Controls">