Matt S.
Matt S.

Reputation: 323

Create grouped checkbox lists in .net MAUI

I'm using .NET MAUI and trying to create a set of checkbox lists that will be put inside a FlexLayout so that they will wrap. When I hard code the data, it works fine. Here's the working hard-coded solution:

<FlexLayout
    Grid.Row="5"
    Grid.Column="0"
    Grid.ColumnSpan="2"
    Direction="Row"
    Wrap="Wrap">                
    <VerticalStackLayout HeightRequest="200" WidthRequest="200">
        <HorizontalStackLayout>
            <CheckBox IsChecked="False" />
            <Label Margin="5,15,0,0" Text="Label 1-1" />
        </HorizontalStackLayout>
        <HorizontalStackLayout>
            <CheckBox IsChecked="False" />
            <Label Margin="5,15,0,0" Text="Label 1-2" />
        </HorizontalStackLayout>
        <HorizontalStackLayout>
            <CheckBox IsChecked="False" />
            <Label Margin="5,15,0,0" Text="Label 1-3" />
        </HorizontalStackLayout>
        <HorizontalStackLayout>
            <CheckBox IsChecked="False" />
            <Label Margin="5,15,0,0" Text="Label 1-4" />
        </HorizontalStackLayout>
    </VerticalStackLayout>
    <VerticalStackLayout HeightRequest="200" WidthRequest="200">
        <HorizontalStackLayout>
            <CheckBox IsChecked="False" />
            <Label Margin="5,15,0,0" Text="Label 2-1" />
        </HorizontalStackLayout>
        <HorizontalStackLayout>
            <CheckBox IsChecked="False" />
            <Label Margin="5,15,0,0" Text="Label 2-2" />
        </HorizontalStackLayout>
        <HorizontalStackLayout>
            <CheckBox IsChecked="False" />
            <Label Margin="5,15,0,0" Text="Label 2-3" />
        </HorizontalStackLayout>
        <HorizontalStackLayout>
            <CheckBox IsChecked="False" />
            <Label Margin="5,15,0,0" Text="Label 2-4" />
        </HorizontalStackLayout>
    </VerticalStackLayout>
    <VerticalStackLayout HeightRequest="200" WidthRequest="200">
        <HorizontalStackLayout>
            <CheckBox IsChecked="False" />
            <Label Margin="5,15,0,0" Text="Label 3-1" />
        </HorizontalStackLayout>
        <HorizontalStackLayout>
            <CheckBox IsChecked="False" />
            <Label Margin="5,15,0,0" Text="Label 3-2" />
        </HorizontalStackLayout>
        <HorizontalStackLayout>
            <CheckBox IsChecked="False" />
            <Label Margin="5,15,0,0" Text="Label 3-3" />
        </HorizontalStackLayout>
        <HorizontalStackLayout>
            <CheckBox IsChecked="False" />
            <Label Margin="5,15,0,0" Text="Label 3-4" />
        </HorizontalStackLayout>
    </VerticalStackLayout>
    <VerticalStackLayout HeightRequest="200" WidthRequest="200">
        <HorizontalStackLayout>
            <CheckBox IsChecked="False" />
            <Label Margin="5,15,0,0" Text="Label 4-1" />
        </HorizontalStackLayout>
        <HorizontalStackLayout>
            <CheckBox IsChecked="False" />
            <Label Margin="5,15,0,0" Text="Label 4-2" />
        </HorizontalStackLayout>
        <HorizontalStackLayout>
            <CheckBox IsChecked="False" />
            <Label Margin="5,15,0,0" Text="Label 4-3" />
        </HorizontalStackLayout>
        <HorizontalStackLayout>
            <CheckBox IsChecked="False" />
            <Label Margin="5,15,0,0" Text="Label 4-4" />
        </HorizontalStackLayout>
    </VerticalStackLayout>
</FlexLayout>

and the results, with a bit of wrapping looks like the following:

Good Results

I tried using the CollectionView grouping feature, but the one issue I would always run into was that the GroupedCheckboxLists ( the extension of the list) would cause my XAML error that the IsChecked and Label attributes were not found. I tried all the solutions and never got it to work.

So now I'm trying nested CollectionViews and I can get results, but it shows all the checkboxes vertically and not side-by-side. Here's my View Model:

public partial class CheckBoxViewModel : ObservableObject
{
    [ObservableProperty]
    string id;

    [ObservableProperty]
    string label;

    [ObservableProperty]
    bool isChecked;

    [ObservableProperty]
    int groupNumber;

    [ObservableProperty]
    int sortOrder;
}


public partial class CheckBoxGroupViewModel : ObservableObject
{

    [ObservableProperty]
    string group;

    [ObservableProperty]
    List<CheckBoxViewModel> checkBoxes;

    public CheckBoxGroupViewModel(string name,  List<CheckBoxViewModel> checkBoxes)
    {
        this.group = name;
        this.checkBoxes = checkBoxes;
    }

}

and here's my XAML:

<FlexLayout
    Grid.Row="5"
    Grid.Column="0"
    Grid.ColumnSpan="2"
    Direction="Row"
    Wrap="Wrap">
    <CollectionView HorizontalOptions="FillAndExpand" ItemsSource="{Binding ProjectRooms}">
        <CollectionView.ItemTemplate>
            <DataTemplate x:DataType="vm:Common.CheckBoxGroupViewModel">
                <VerticalStackLayout HeightRequest="200" WidthRequest="200">
                    <CollectionView ItemsSource="{Binding CheckBoxes}">
                        <CollectionView.ItemTemplate>
                            <DataTemplate x:DataType="vm:Common.CheckBoxViewModel">
                                <HorizontalStackLayout>
                                    <CheckBox IsChecked="{Binding IsChecked}" />
                                    <Label Margin="5,15,0,0" Text="{Binding Label}" />
                                </HorizontalStackLayout>
                            </DataTemplate>
                        </CollectionView.ItemTemplate>
                    </CollectionView>
                </VerticalStackLayout>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</FlexLayout>

This shows like the following:

Bad Checks

Any Ideas how to get it to show the groups side-by-side and wrap?

UPDATE:

I'm trying the solution below but the problem is that I want these to be in a FlexLayout so that it'll fit on any device, and wrap the Checkboxes within their Group. Here's the update to a FlexLayout, but still getting all the VerticalLayouts in one column:

<FlexLayout
    Grid.Row="5"
    Grid.Column="0"
    Grid.ColumnSpan="2"
    Direction="Row"
    Wrap="Wrap">
    <CollectionView HorizontalOptions="FillAndExpand" ItemsSource="{Binding ProjectRooms}">
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <VerticalStackLayout
                    BindableLayout.ItemsSource="{Binding CheckBoxes}"
                    HeightRequest="200"
                    WidthRequest="200">
                    <BindableLayout.ItemTemplate>
                        <DataTemplate>
                            <HorizontalStackLayout>
                                <CheckBox IsChecked="{Binding IsChecked}" />
                                <Label Margin="5,15,0,0" Text="{Binding Label}" />
                            </HorizontalStackLayout>
                        </DataTemplate>
                    </BindableLayout.ItemTemplate>
                </VerticalStackLayout>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</FlexLayout>

Upvotes: 1

Views: 712

Answers (1)

Jessie Zhang -MSFT
Jessie Zhang -MSFT

Reputation: 13879

You can try to put BindableLayout inside of CollectionView to achieve this.

Based on your code, I created a demo and achieved this fucntion.

You can refer to the following code:

<?xml version="1.0" encoding="utf-8" ?> 
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MauiInnerListApp0201.MainPage"
             xmlns:vm="clr-namespace:MauiInnerListApp0201.Models" >
    <ContentPage.BindingContext>
        <vm:TestViewModel></vm:TestViewModel>
    </ContentPage.BindingContext>

    <CollectionView ItemsSource="{Binding Groups}"
                        ItemsLayout="VerticalGrid, 3">
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <VerticalStackLayout>

                    <Label Text="{Binding Group}" />
                    <StackLayout BindableLayout.ItemsSource="{Binding CheckBoxes}"   Orientation="Vertical">
                        <BindableLayout.ItemTemplate>
                            <DataTemplate>
                                <HorizontalStackLayout>
                                    <CheckBox IsChecked="{Binding IsChecked}" />
                                    <Label Margin="5,15,0,0" Text="{Binding Label}" />
                                </HorizontalStackLayout>
                            </DataTemplate>
                        </BindableLayout.ItemTemplate>
                    </StackLayout>
                </VerticalStackLayout>

            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</ContentPage>

TestViewModel.cs

public class TestViewModel
{
    public List<CheckBoxGroupViewModel> Groups { get; set; } = new List<CheckBoxGroupViewModel>();

    public TestViewModel()
    {
        List<CheckBoxViewModel> Items1 = new List<CheckBoxViewModel>();

        CheckBoxGroupViewModel group1;

        Items1.Add (new CheckBoxViewModel() {  GroupNumber = 1 ,  IsChecked= true, Id= "1", Label= "Label 1-1", SortOrder = 01});
        Items1.Add (new CheckBoxViewModel() {  GroupNumber = 1 ,  IsChecked= true, Id= "2", Label= "Label 1-1", SortOrder = 01});
        Items1.Add (new CheckBoxViewModel() {  GroupNumber = 1 ,  IsChecked= false, Id= "3", Label= "Label 1-1", SortOrder = 01});
        Items1.Add (new CheckBoxViewModel() {  GroupNumber = 1 ,  IsChecked= true, Id= "4", Label= "Label 1-1", SortOrder = 01});

        group1 = new CheckBoxGroupViewModel("group-1", Items1);

        List<CheckBoxViewModel> Items2 = new List<CheckBoxViewModel>();
        CheckBoxGroupViewModel group2;

        Items2.Add(new CheckBoxViewModel() { GroupNumber = 2, IsChecked = true, Id = "1", Label = "Label 2-1", SortOrder = 01 });
        Items2.Add(new CheckBoxViewModel() { GroupNumber = 2, IsChecked = true, Id = "2", Label = "Label 2-1", SortOrder = 01 });
        Items2.Add(new CheckBoxViewModel() { GroupNumber = 2, IsChecked = false, Id = "3", Label = "Label 2-1", SortOrder = 01 });
        Items2.Add(new CheckBoxViewModel() { GroupNumber = 2, IsChecked = true, Id = "4", Label = "Label 2-1", SortOrder = 01 });

        group2 = new CheckBoxGroupViewModel("group-2", Items2);

        List<CheckBoxViewModel> Items3 = new List<CheckBoxViewModel>();
        CheckBoxGroupViewModel group3;

        Items3.Add(new CheckBoxViewModel() { GroupNumber = 3, IsChecked = true, Id = "1", Label = "Label 3-1", SortOrder = 01 });
        Items3.Add(new CheckBoxViewModel() { GroupNumber = 3, IsChecked = true, Id = "2", Label = "Label 3-1", SortOrder = 01 });
        Items3.Add(new CheckBoxViewModel() { GroupNumber = 3, IsChecked = false, Id = "3", Label = "Label 3-1", SortOrder = 01 });
        Items3.Add(new CheckBoxViewModel() { GroupNumber = 3, IsChecked = true, Id = "4", Label = "Label 3-1", SortOrder = 01 });

        group3 = new CheckBoxGroupViewModel("group-3", Items3);

        Groups.Add(group1);
        Groups.Add(group2);
        Groups.Add(group3);
    }
}

Note:

You can adjust the column number by ItemsLayout="VerticalGrid, 3".

For more information, you can check: Specify CollectionView layout.

Update:

So the main issue is that I want these Checkbox Groups in a FlexLayout as I do not know how many will actually be in the list and it'll be shown on different devices. I'd rather not set it to static 3 columns.

Please try the following code:

<FlexLayout
        BindableLayout.ItemsSource="{Binding Groups}"  Direction="Row" AlignContent="Start" AlignItems="Start"
        VerticalOptions="StartAndExpand"  JustifyContent="Start"
        Wrap="Wrap">
        <BindableLayout.ItemTemplate>
                <DataTemplate>
                    <VerticalStackLayout
                       BindableLayout.ItemsSource="{Binding CheckBoxes}"  >
                        <BindableLayout.ItemTemplate>
                            <DataTemplate>
                                <HorizontalStackLayout>
                                    <CheckBox IsChecked="{Binding IsChecked}" />
                                    <Label Margin="5,15,0,0" Text="{Binding Label}" />
                                </HorizontalStackLayout>
                            </DataTemplate>
                        </BindableLayout.ItemTemplate>
                    </VerticalStackLayout>
                </DataTemplate>
            </BindableLayout.ItemTemplate>
    </FlexLayout>

Upvotes: 2

Related Questions