Previously, I explained how to extend the FlexLayout class with ItemsSource and ItemTemplate properties, so that it can bind to data stored in a collection. However, the resulting ExtendedFlexLayout class is only a minimally viable implementation as it omits a number of required features.
One of the missing features is that it doesn’t allow a DataTemplateSelector to choose a DataTemplate at runtime based on the value of a bound property. Instead it only permits a single defined DataTemplate to be used.
The purpose of this blog post is to further extend the ExtendedFlexLayout class so that it does allow a DataTemplateSelector to choose a DataTemplate at runtime. This enables scenarios such as the ExtendedFlexLayout binding to a collection of objects where the appearance of each object can be chosen at runtime by a data template selector returning a particular DataTemplate.
The sample this code comes from can be found on GitHub.
Adding DataTemplateSelector Support
As I explained previously, the ExtendedFlexLayout class has a CreateChildView method, which is called for each item in the collection that the ItemsSource property binds to, to load the DataTemplate referenced by the ItemTemplate property and set its binding context:
View CreateChildView(object item) { ItemTemplate.SetValue(BindableObject.BindingContextProperty, item); return (View)ItemTemplate.CreateContent(); }
This method can be extended to support a DataTemplateSelector choosing a DataTemplate at runtime:
View CreateChildView(object item) { if (ItemTemplate is DataTemplateSelector) { var dts = ItemTemplate as DataTemplateSelector; var itemTemplate = dts.SelectTemplate(item, null); itemTemplate.SetValue(BindableObject.BindingContextProperty, item); return (View)itemTemplate.CreateContent(); } else { ItemTemplate.SetValue(BindableObject.BindingContextProperty, item); return (View)ItemTemplate.CreateContent(); } }
This additional code checks if the ItemTemplate property references a class that inherits from DataTemplateSelector. If it is, it calls the DataTemplateSelector.SelectTemplate method to return the particular DataTemplate. Then, as before, the DataTemplate is loaded and its binding context is set.
Consuming the ExtendedFlexLayout
A data template selector is implemented by creating a class that inherits from DataTemplateSelector. The OnSelectTemplate method is then overridden to return a particular DataTemplate, as shown in the following code example:
public class MonkeyDataTemplateSelector : DataTemplateSelector { public DataTemplate NormalMonkeyTemplate { get; set; } public DataTemplate CynicalMonkeyTemplate { get; set; } protected override DataTemplate OnSelectTemplate(object item, BindableObject container) { return ((Monkey)item).Name.Contains("Face-Palm") ? CynicalMonkeyTemplate : NormalMonkeyTemplate; } }
Here, the OnSelectTemplate method returns the appropriate template based on the value of the Name property. The template to return is the template referenced by the NormalMonkeyTemplate property or the CynicalMonkeyTemplate, which are set when consuming the MonkeyDataTemplateSelector.
The MonkeyDataTemplateSelector is instantiated by declaring it as a resource:
<ContentPage.Resources> <Style x:Key="normalMonkeyFrame" TargetType="Frame"> <Setter Property="BackgroundColor" Value="LightYellow" /> <Setter Property="BorderColor" Value="Blue" /> <Setter Property="Margin" Value="10" /> <Setter Property="CornerRadius" Value="15" /> </Style> <Style x:Key="cynicalMonkeyFrame" TargetType="Frame"> <Setter Property="BackgroundColor" Value="Red" /> <Setter Property="BorderColor" Value="Black" /> <Setter Property="Margin" Value="10" /> <Setter Property="CornerRadius" Value="15" /> </Style> ... <DataTemplate x:Key="normalMonkeyTemplate"> <Frame WidthRequest="300" HeightRequest="480" Style="{StaticResource normalMonkeyFrame}"> ... </Frame> </DataTemplate> <DataTemplate x:Key="cynicalMonkeyTemplate"> <Frame WidthRequest="300" HeightRequest="480" Style="{StaticResource cynicalMonkeyFrame}"> ... </Frame> </DataTemplate> <local:MonkeyDataTemplateSelector x:Key="monkeyDataTemplateSelector" NormalMonkeyTemplate="{StaticResource normalMonkeyTemplate}" CynicalMonkeyTemplate="{StaticResource cynicalMonkeyTemplate}" /> </ContentPage.Resources>
The ResourceDictionary above defines two DataTemplate instances, and a MonkeyDataTemplateSelector instance. The MonkeyDataTemplateSelector instance sets its NormalMonkeyTemplate and CynicalMonkeyTemplate properties to the appropriate DataTemplate instances through the StaticResource markup extension. The two DataTemplates are largely identical, aside from setting different colours on the Frame instances.
The MonkeyDataTemplateSelector instance is consumed by assigning it to the ExtendedFlexLayout.ItemTemplate property:
<local:ExtendedFlexLayout ItemsSource="{Binding Monkeys}" ItemTemplate="{StaticResource monkeyDataTemplateSelector}" />
At runtime, the MonkeyDataTemplateSelector.OnSelectTemplate method is called (by the ExtendedFlexLayout class via the DataTemplateSelector.SelectTemplate method call) for each of the items in the underlying collection, with the call passing the data object as the item parameter. The DataTemplate that is returned by the method is then applied to that object.
The following screenshots show the result of the ExtendedFlexLayout applying the MonkeyDataTemplateSelector to each object in the underlying collection:
Any Monkey object that has a Name property that contains “Face-Palm” is displayed with a red background, as a warning to be wary of cynical monkeys, with the remaining objects being displayed with a a light yellow background.
Summary
This blog post has explained how to further extend the ExtendedFlexLayout class so that it allows a DataTemplateSelector to choose a DataTemplate at runtime. This enables scenarios such as the ExtendedFlexLayout binding to a collection of objects where the appearance of each object can be chosen at runtime by the data template selector returning a particular DataTemplate. My next blog post will look at further extending the ExtendedFlexLayout class with additional functionality.
The sample this code comes from can be found on GitHub.
No comments:
Post a Comment