Monday, 18 March 2019
Patrick Allwood
4 minute read
class BindableFlexLayout : FlexLayout
{
public static readonly BindableProperty ItemsSourceProperty
= BindableProperty.Create(nameof(ItemsSource), typeof(IList), typeof(BindableFlexLayout));
public IList ItemsSource
{
get { return (IList)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly BindableProperty ItemTemplateProperty
= BindableProperty.Create(nameof(ItemTemplate), typeof(DataTemplate), typeof(BindableFlexLayout));
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
}
We're going to defer all layout and positioning decisions to the base class. In order to do this, all we need to do is add the child UI elements to the Children collection in the correct order. The snippet below allows either a static DataTemplate, or a DataTemplateSelector to be used. For this article we're going to use the simplest solution of rebuilding the whole collection each time.
private void BuildLayout()
{
Children.Clear();
foreach (var item in ItemsSource)
{
View view = null;
if (ItemTemplate is DataTemplateSelector)
{
view = (View)ItemTemplate.CreateContent(item, null);
}
else
{
view = (View)ItemTemplate.CreateContent();
}
view.BindingContext = item;
Children.Add(view);
}
}
Finally, we need to trigger the rebuild whenever the DataTemplate or ItemsSource properties change. Add property change event handlers to our two bindable properties.
public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create("ItemsSource", typeof(IList), typeof(StackLayoutList), propertyChanged: OnItemsSourceChanged);
public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create("ItemTemplate", typeof(DataTemplate), typeof(StackLayoutList), propertyChanged: OnItemTemplateChanged);
private static void OnItemTemplateChanged(BindableObject obj, object oldValue, object newValue)
{
var layout = obj as StackLayoutList;
if (layout?.ItemsSource != null && layout?.ItemTemplate != null)
layout.BuildLayout();
}
private static void OnItemsSourceChanged(BindableObject obj, object oldValue, object newValue)
{
var layout = obj as StackLayoutList;
if (layout?.ItemsSource != null && layout?.ItemTemplate != null)
layout.BuildLayout();
}
That's all there is to it, complete copy-pastable sample is below. The same implementation can also be applied to other types of layouts. Worth noting that this has a short shelf life - the Xamarin team have seen the need for bindable layouts and as of Xamarin.Forms 3.5, will be providing the same functionality via attached properties. Neat.
public class BindableFlexLayout : FlexLayout
{
public static readonly BindableProperty ItemsSourceProperty
= BindableProperty.Create("ItemsSource", typeof(IList), typeof(BindableFlexLayout), propertyChanged: OnItemsSourceChanged);
public IList ItemsSource
{
get { return (IList)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly BindableProperty ItemTemplateProperty
= BindableProperty.Create("ItemTemplate", typeof(DataTemplate), typeof(BindableFlexLayout), propertyChanged: OnItemTemplateChanged);
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
private static void OnItemTemplateChanged(BindableObject obj, object oldValue, object newValue)
{
var layout = obj as BindableFlexLayout;
if (layout?.ItemsSource != null && layout?.ItemTemplate != null)
layout.BuildLayout();
}
private static void OnItemsSourceChanged(BindableObject obj, object oldValue, object newValue)
{
var layout = obj as BindableFlexLayout;
if (layout?.ItemsSource != null && layout?.ItemTemplate != null)
layout.BuildLayout();
}
private void BuildLayout()
{
Children.Clear();
foreach (var item in ItemsSource)
{
View view = null;
if (ItemTemplate is DataTemplateSelector)
{
view = (View)ItemTemplate.CreateContent(item, null);
}
else
{
view = (View)ItemTemplate.CreateContent();
}
view.BindingContext = item;
Children.Add(view);
}
}
}
Last updated: Monday, 19 June 2023
Senior Software Developer
He/him
Patrick is a Senior Software Developer at Rock Solid Knowledge.
We're proud to be a Certified B Corporation, meeting the highest standards of social and environmental impact.
+44 333 939 8119