Resolving DataTemplate in resources by collection type

TerryH 55 Reputation points
2023-02-25T19:44:08.5966667+00:00

I'm trying to display a collection of items in a CollectionView . The items in the collection are of different types, but share the same base class: BaseThing:ObservableObject, ThingA:BaseThing and ThingB:BaseThing, and the ItemsSource for the CollectionView is ObservableCollection<BaseThing>

I have the DataTemplate defined in the Page level resources:

<ContentPage.Resources>
  <DataTemplate x:DataType="{x:Type vm:ThingA}"> 
     <HorizontalStackLayout>
         <Label Text="{Binding PropertyA}"/>
     </HorizontalStackLayout>
    </DataTemplate>
    <DataTemplate x:DataType="{x:Type vm:ThingB}"> 
         <HorizontalStackLayout>
             <Label Text="{Binding PropertyB}"/>
         </HorizontalStackLayout>
     </DataTemplate>
</ContentPage.Resources>

Note: This does not compile unless there is a Key assigned to it. Not the real issue here, but I thought it was relevant as a difference between MAUI and WPF.
Error XFC0126 Resources in ResourceDictionary require a x:Key attribute.

In WPF a template defined without the Key would resolve based on the actual derived type of the item in the collection. That does not seem to be the case in MAUI.

The docs say:

A DataTemplateSelector can be used to choose a DataTemplate at runtime based on the value of a data-bound property. This enables multiple data templates to be applied to the same type of object, to choose their appearance at runtime.

I interpret this as the DataTemplateSelector should only be used to switch off of a property in a collection of the same type, and not the class type itself. Can anyone provide some help or clarification on this?

In the page view, I have this:

<CollectionView ItemsSource="{Binding BaseThingCollection}" />

If I specify a named template in the ItemTemplate property it will work, but I would like to be able to have it pick the correct template based on Type.

My question is, "Is there a way to resolve the DataTemplate based on derived type alone? Or does it require the use of a DataTemplateSelector with named DataTemplates in the resources?" If so, what is the recommended way to deal with distinguishing the Type?

Thanks!

.NET MAUI
.NET MAUI
A Microsoft open-source framework for building native device applications spanning mobile, tablet, and desktop.
3,094 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,535 questions
XAML
XAML
A language based on Extensible Markup Language (XML) that enables developers to specify a hierarchy of objects with a set of properties and logic.
779 questions
0 comments No comments
{count} votes

Accepted answer
  1. Wenyan Zhang (Shanghai Wicresoft Co,.Ltd.) 28,071 Reputation points Microsoft Vendor
    2023-02-27T05:37:11.9033333+00:00

    Hello,

    x:DataType is to use compiled bindings, and it means the VisualElement and its children will bind to this type of the object. Setting the CollectionView.ItemTemplate property to a DataTemplateSelector object means choose the appearance of each item in the CollectionView at runtime.

    Form your code snippets, you define two DataTemplate in the ResourceDictionary of the Page, but you didn't define the item appearance in the CollectionView by setting the CollectionView.ItemTemplate . Defining both the data to display and its appearance is necessary for a CollectionView.

    And you said "If I specify a named template in the ItemTemplate property it will work, but I would like to be able to have it pick the correct template based on Type." If so, it's recommended that you set the CollectionView.ItemTemplate property to a DataTemplateSelector object, it requires the use of a DataTemplateSelector in the resources(the name can be DataTemplates or others)

    Please refer to the following code:

    is operator to check the type

    public class ThingDataTemplateSelector : DataTemplateSelector
        {
            public DataTemplate ThingATemplate { get; set; }
            public DataTemplate ThingBTemplate { get; set; }
    
            protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
            {
                return item is ThingA ? ThingATemplate : ThingBTemplate;
            }
        }
    

    ResourceDictionary

     <ContentPage.Resources>
            <DataTemplate x:Key="ThingATemplate"... >
               ...
            </DataTemplate>
            <DataTemplate x:Key="ThingBTemplate"...>
               ...
            </DataTemplate>
            <vm:ThingDataTemplateSelector x:Key="ThingSelector"
                                                 ThingATemplate="{StaticResource ThingATemplate}"
                                                 ThingBTemplate="{StaticResource ThingBTemplate}" />
        </ContentPage.Resources>
    
    

    Populate the data and set item appearance

        <CollectionView ItemsSource="{Binding BaseThingCollection}"
                         ItemTemplate="{StaticResource ThingSelector}">
           
        </CollectionView>
    

    For more details, you can see:
    Compiled bindings - .NET MAUI | Microsoft Learn
    Populate a CollectionView with Data - .NET MAUI | Microsoft Learn
    Resource dictionaries - .NET MAUI | Microsoft Learn
    Type-testing operators and cast expressions test the runtime type of an object | Microsoft Learn

    Best Regards,

    Wenyan Zhang


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    0 comments No comments

0 additional answers

Sort by: Most helpful