-
Notifications
You must be signed in to change notification settings - Fork 2
data templates
Data Templates are used to bind definitions of cells inside of a Collection View. Shield MVVM added a generic Data Template that can be used with BindingHelper to guarantee that the list being bound to matches the DataTemplate type. However, sometimes a DataTemplateSelector is needed due to the cells being displayed in different ways. While support is added for this, there can no longer be any type-safety guarantee. An included DataTemplateSelectorBase<> class is included to make creating these selectors easier, as well.
In order to make using the generic DataTemplates much easier to work with, developers should have all cells inherit from ViewCellBase<>. The generic parameters include the model being bound to and a reference to itself. This reference is used later to make it easier to bind. There is a bit more XAML setup involved compared to a page, but not much. Here is an example of the XAML markup:
<?xml version="1.0" encoding="utf-8" ?>
<base:ViewCellBase
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:base="clr-namespace:CoreBTS.Maui.ShieldMVVM.DataTemplates;assembly=CoreBTS.Maui.ShieldMVVM"
x:Class="MauiSample.Features.About.Cells.AboutCell"
xmlns:m="clr-namespace:MauiSample.Features.About.Cells"
xmlns:self="clr-namespace:MauiSample.Features.About.Cells"
x:TypeArguments="m:AboutItem, self:AboutCell"
>
<VerticalStackLayout
Spacing="4">
<HorizontalStackLayout
Spacing="8"
>
<Label
x:Name="Name"
/>
<Label
x:Name="Description"
/>
</HorizontalStackLayout>
<Label
HeightRequest="1"
BackgroundColor="DarkGray"
/>
</VerticalStackLayout>
</base:ViewCellBase>
xmlns, xmlns:x, and xmls:base would always be the same, regardless of the page created. x:Class is the page that was just created. xlmns:m is the model that the cell is bound to. It could be a view model, but it doesn't have to be. xmls:self is a reference to this specific class's namespace. Finally, x:TypeArguments defines the model being bound to first and then the name of this class.
From there, you can treat this cell as if it were a page or dialog - it works exactly the same way. Here is what the code-behind would look like:
public partial class AboutCell : ViewCellBase<AboutItem, AboutCell>
{
protected override void SetupBindings()
{
Binder.WithControls(Name)
.For(c => c.BindText(), vm => vm.Name);
Binder.WithControls(Description)
.For(c => c.BindText(), vm => vm.Description);
}
}
The biggest difference here is that the constructor doesn't take a model. It is set dynamically by the system while being bound to a list. Thus, there is just the one method developers have to worry about.
BindingHelper has some special methods specifically for lists in order to setup all the appropriate bindings.
The first method allows users to bind the list of items to the list with type-safety. First, the list parameter from the View Model is given and second is the static ItemTemplate property from the cell that was created. Since the control has a reference to itself, a static ItemTemplate property can return the needed data. If the two types don't match, the code won't compile. The binding from the page containing the list would look like this:
Binder.WithControl(List)
.ForTemplate(vm => vm.AboutItems, AboutCell.ItemTemplate);
However, sometimes a DataTemplateSelector is needed because the cells need to display different depending on certain data or its a mix of items. In that case, there can't be any type-safety checks as it would never be 1 to 1. However, a DataTemplateSelectorBase<> class is provided to make setting these up easier. .ForTemplate would then just take the ItemTemplate property from the newly created DataTemplateSelector in a similar way.
Finally, two helper methods are added in case the list needs to support selecting item(s). In both cases, the developer simply has to give the command to bind to and BindingHelper takes care of the rest. For SingleSelection, the View Model command will need to take the model as a parameter. For MultiSelection, however, the built in event only works with object, so the command will need to take IList< object >, ICollection< object >, etc. for the call to work (and the CollectionView needs to be set to SelectionMode="Multiple"). Perhaps there will be a way to make that generic in the future, but not for now. The code would look something like this:
Binder.WithControl(List)
.ForTemplate(vm => vm.AboutItems, AboutCell.ItemTemplate)
.ForSingleSelection(vm => vm.SelectedCommand);