Using collection view with mixed types elements ? (heterogeneous collection) #27235
-
Hi guys !
This is using different data models (one for the date separator, the other one the actual card content). I saw that something of this sort might be achievable via DataTemplateSelector, and I would like to do it with a mix of code behind and Xaml (I find data template selector cumbersome, where we could rely on Custom ContentViews to provide a rendering entry point.) Assuming that we have :
Would it be possible to have on the main page : <ContentPage.Content>
<CollectionView SelectionMode="None"
ItemsSource="{Binding CustomCardsList}" > <!-- Where this CustomCardsList would be a list of ContentView -->
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical"
ItemSpacing="5" />
</CollectionView.ItemsLayout>
<!-- And we wouldn't need to write specific DataTemplates here -->
</CollectionView> and in the Page's ViewModel : class MyViewModel : ObservableObject
{
[ObservableProperty] private List<ContentView> _customCardsList;
private void UpdateUI()
{
var lightModels = new List<SimplifiedModel>
{
{
Name = "test",
Amount = 35
},
{
Name = "Another one",
Amount = 32
},
}
// Populate UI based on built models
var customList = new List<ContentView>();
foreach (var model in lightModels)
{
customList.Add(new PaymentCardView(model)); // We inject the data model via the constructor, which is not allowed by DataTemplateSelectors
customList.Add(new DateCardView("23 Sept 2024"))
}
CustomCardsList = paymentCards;
}
}
The idea would be that in absence of custom DataTemplate (which is very convenient for development), we'd rely on the ContentViews list only to derive the look of the CollectionView ? I also tried ListView control, but it seems it works more or less the same way, regarding how data is displayed. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
What's wrong with setting your DataTemplate to a ContentView? You can defined 3 ControlTemplates in a ResourceDictionary and you use one of the ControlTemplate as the default and you can switch on either of the other two with the use of DataTriggers. <!-- MainPage.xaml -->
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="MauiCustomView.MainPage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MauiCustomView"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
x:Name="this"
x:DataType="local:MainPage"
BindingContext="{Reference this}">
<ContentPage.Resources>
<ResourceDictionary x:Name="resourceDictionary">
<toolkit:MultiMathExpressionConverter x:Key="MultiMathExpressionConverter" />
<ControlTemplate x:Key="LabelControlTemplate">
<Label Text="Label" />
</ControlTemplate>
<ControlTemplate x:Key="ButtonControlTemplate">
<Button Text="Button" />
</ControlTemplate>
<ControlTemplate x:Key="EntryControlTemplate">
<Entry Placeholder="Entry" />
</ControlTemplate>
</ResourceDictionary>
</ContentPage.Resources>
<CollectionView ItemsSource="{Binding Widgets}">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="{x:Type x:String}">
<ContentView ControlTemplate="{StaticResource LabelControlTemplate}">
<ContentView.Triggers>
<DataTrigger
Binding="{Binding .}"
TargetType="ContentView"
Value="Button">
<Setter Property="ControlTemplate" Value="{StaticResource ButtonControlTemplate}" />
</DataTrigger>
<DataTrigger
Binding="{Binding .}"
TargetType="ContentView"
Value="Entry">
<Setter Property="ControlTemplate" Value="{StaticResource EntryControlTemplate}" />
</DataTrigger>
</ContentView.Triggers>
</ContentView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentPage> If you don't want to use a DataTrigger, then, you can construct a StringToResourceConverter. <!-- MainPage.xaml -->
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="MauiCustomView.MainPage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MauiCustomView"
x:Name="this"
x:DataType="local:MainPage"
BindingContext="{Reference this}">
<ContentPage.Resources>
<ResourceDictionary x:Name="resourceDictionary">
<ControlTemplate x:Key="LabelControlTemplate">
<Label Text="Label" />
</ControlTemplate>
<ControlTemplate x:Key="ButtonControlTemplate">
<Button Text="Button" />
</ControlTemplate>
<ControlTemplate x:Key="EntryControlTemplate">
<Entry Placeholder="Entry" />
</ControlTemplate>
</ResourceDictionary>
</ContentPage.Resources>
<CollectionView ItemsSource="{Binding Widgets}">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="{x:Type x:String}">
<ContentView ControlTemplate="{Binding ., Converter={local:StringToResourceConverter}, ConverterParameter={Reference resourceDictionary}}" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentPage> // MainPage.xaml.cs
using System.Collections.ObjectModel;
using System.Globalization;
namespace MauiCustomView;
public class StringToResourceConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is string stringValue && parameter is Microsoft.Maui.Controls.ResourceDictionary resourceDictionary)
{
return resourceDictionary?.TryGetValue($"{stringValue}ControlTemplate", out var resource) == true ? resource : null;
}
return null;
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public partial class MainPage : ContentPage
{
public ObservableCollection<string> Widgets { get; } = new ObservableCollection<string>()
{
"Label", "Button", "Entry"
};
public MainPage()
{
InitializeComponent();
}
} If you don't want to use converters, you can use multiple DataTrigger instead. One for each |
Beta Was this translation helpful? Give feedback.
What's wrong with setting your DataTemplate to a ContentView?
You can defined 3 ControlTemplates in a ResourceDictionary and you use one of the ControlTemplate as the default and you can switch on either of the other two with the use of DataTriggers.