dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
22k stars 1.72k forks source link

[Feature] Auto compiled binding on CollectionView's ItemTemplate, GroupHeaderTemplate and GroupFooterTemplate #8161

Open yairp03 opened 2 years ago

yairp03 commented 2 years ago

Description

This feature determines automatically the DataType of a DataTemplate inside <CollectionView.ItemTemplate>, <CollectionView.GroupHeaderTemplate> and/or <CollectionView.GroupFooterTemplate>, without need of specifying x:DataType manually. Also, it allows using an x:DataType on a page when there is Group Header/Footer Template that using group attributes.

Normal CollectionView

For example, I have this Monkey model:

public class Monkey
{
    public string Species { get; set; }
}

Inside the ViewModel, I have this list of monkeys:

public partial class MonkeysViewModel
{
    public List<Monkey> MonkeysList { get; } = new();
}

And in the page, I have a CollectionView with a binding to the MonkeysList:

<ContentPage x:DataType="viewmodel:MonkeysViewModel">
    <CollectionView ItemsSource="{Binding MonkeysList}">
    </CollectionView>
</ContentPage>

Currently, in order to use the ItemTemplate without getting any binding error, I need to specify the type of the item using x:DataType on the DataTemplate tag, like this:

<ContentPage x:DataType="viewmodel:MonkeysViewModel">
    <CollectionView ItemsSource="{Binding MonkeysList}">
        <CollectionView.ItemTemplate>
            <DataTemplate x:DataType="model:Monkey"> <!--Required in order to use the Species member-->
                <Label Text="{Binding Species}" />
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</ContentPage>

Feature benefits

This feature will make the code cleaner, will enable doing the same thing without specifying the x:DataType, and will automatically determine that because the CollectionView's ItemSource is a Collection of Monkey's, then each item is a Monkey.

Grouped CollectionView

In a grouped CollectionView, the ItemSource needs to be an IEnumerable of IEnumerables. For example:

public partial class MonkeysViewModel
{
    public List<List<Monkey>> MonkeysGroups { get; } = new();
}
<CollectionView IsGrouped="True" ItemsSource="{Binding MonkeysGroups}">
</CollectionView>

If we want to use <CollectionView.GroupHeaderTemplate> or <CollectionView.GroupFooterTemplate>, we can't use compiled binding on the page, because then it will require us to specify the x:DataType for the DataTemplate, which we can't do. For example:

<ContentPage x:DataType="viewmodel:MonkeysViewModel"> <!--Specifying x:DataType for page-->
    <CollectionView IsGrouped="True" ItemsSource="{Binding MonkeysList}">
        <CollectionView.GroupHeaderTemplate>
            <DataTemplate> <!--Can't use x:DataType, there's no syntax for List<Monkey> type-->
                <Label Text="{Binding Count}" /> <!--Will raise a 'member not found' error-->
            </DataTemplate>
        </CollectionView.GroupHeaderTemplate>
        <CollectionView.ItemTemplate>
            <DataTemplate x:DataType="model:Monkey">
                <Label Text="{Binding Species}" />
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</ContentPage>

Feature benefits

Besides the code being cleaner, this feature will allow using compiled binding on a page that has a grouped CollectionView in it, that uses the group's header and/or footer templates.

Final code with the feature

<ContentPage x:DataType="viewmodel:MonkeysViewModel">
    <CollectionView IsGrouped="True" ItemsSource="{Binding MonkeysList}">
        <CollectionView.GroupHeaderTemplate>
            <DataTemplate> <!--No need for x:DataType, automatically determines the type is 'List<Monkey>'-->
                <Label Text="{Binding Count}" /> <!--Working, because 'List<Monkey>' has a 'Count' member-->
            </DataTemplate>
        </CollectionView.GroupHeaderTemplate>
        <CollectionView.ItemTemplate>
            <DataTemplate> <!--No need for x:DataType, automatically determines the type is 'Monkey'-->
                <Label Text="{Binding Species}" /> <!--Working, because 'Monkey' has a 'Species' member-->
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</ContentPage>

Public API Changes

Code

The example will use this code:

public class Monkey
{
    public string Species { get; set; }
}
public partial class MonkeysViewModel
{
    public List<Monkey> MonkeysList { get; } = new();
    public List<List<Monkey>> MonkeysGroups { get; } = new();
}

Normal CollectionView

For a non-grouped CollectionView, the xaml code, instead of this:

<ContentPage x:DataType="viewmodel:MonkeysViewModel">
    <CollectionView ItemsSource="{Binding MonkeysList}">
        <CollectionView.ItemTemplate>
            <DataTemplate x:DataType="model:Monkey"> <!--Required in order to use the Species member-->
                <Label Text="{Binding Species}" />
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</ContentPage>

Will look like this:

<ContentPage x:DataType="viewmodel:MonkeysViewModel">
    <CollectionView ItemsSource="{Binding MonkeysList}">
        <CollectionView.ItemTemplate>
            <DataTemplate> <!--No need for x:DataType-->
                <Label Text="{Binding Species}" /> <!--Working and has intellisense-->
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</ContentPage>

Grouped CollectionView

For a grouped CollectionView, the xaml code, instead of this:

<ContentPage> <!--Can't use x:DataType, because using groups members-->
    <CollectionView IsGrouped="True" ItemsSource="{Binding MonkeysList}">
        <CollectionView.GroupHeaderTemplate>
            <DataTemplate> <!--Can't use x:DataType, there's no syntax for List<Monkey> type-->
                <Label Text="{Binding Count}" /> <!--'No DataContext found' and no intellisense-->
            </DataTemplate>
        </CollectionView.GroupHeaderTemplate>
        <CollectionView.ItemTemplate>
            <DataTemplate x:DataType="model:Monkey">
                <Label Text="{Binding Species}" />
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</ContentPage>

Will look like this:

<ContentPage x:DataType="viewmodel:MonkeysViewModel"> <!--Specifying x:DataType-->
    <CollectionView IsGrouped="True" ItemsSource="{Binding MonkeysList}">
        <CollectionView.GroupHeaderTemplate>
            <DataTemplate> <!--No need for x:DataType-->
                <Label Text="{Binding Count}" /> <!--Working and has intellisense-->
            </DataTemplate>
        </CollectionView.GroupHeaderTemplate>
        <CollectionView.ItemTemplate>
            <DataTemplate> <!--No need for x:DataType-->
                <Label Text="{Binding Species}" /> <!--Working and has intellisense-->
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</ContentPage>

Intended Use-Case

The feature will be used on any page that has a CollectionView in it and will make the code cleaner. Also, it will enable to use compiled bindings on the page, even if it has a grouped CollectionView that uses group members inside the group header and/or footer templates.

yairp03 commented 2 years ago

I suggest adding area/collectionView tag

ghost commented 9 months ago

We've added this issue to our backlog, and we will work to address it as time and resources allow. If you have any additional information or questions about this issue, please leave a comment. For additional info about issue management, please read our Triage Process.

ArmandoJR008 commented 4 months ago

If we want to use or , we can't use compiled binding on the page, because then it will require us to specify the x:DataType for the DataTemplate, which we can't do.

I think this should be in the official Documentation as a warning.

Maybe someone else would've realized sooner than me but I spent a long time today thinking I was doing something wrong because of this. I resorted to not using and made a nested CollectionView inside the so I could properly classify my data in groups and still use compiled bindings. But this is redundant and likely not efficient.