Search code examples
xamllistviewmaui

MAUI Bug ListView content disappeared by using ListView.GroupHeaderTemplate


I'm using a ListView with Binding Group and IsGroupingEnabled="True " for headers and I don't get any content displayed. Now when I comment out the xaml section of <ListView.GroupHeaderTemplate> or comment in the previously commented out section of <ListView.GroupHeaderTemplate> after launching the app (on Windows), the content is displayed. When I use a common List instead of the (Top-)Group and no IsGroupingEnabled it works fine. The Label width content Test is always shown. When I changed ListView to CollectionView I get the same result. The plain List is shown, but not a grouped list.

Why is the content not shown immediately when the page is called, only after commenting in the code (in xaml)?

XAML:

<toolkit:Expander Margin="0,0,0,30">
    <toolkit:Expander.Header>
        <Label Text="Tops" Style="{StaticResource labelExpander}"  />
    </toolkit:Expander.Header>
    <Border Stroke="#888"
            StrokeThickness="2"
            StrokeShape="RoundRectangle 10"
            HorizontalOptions="Fill">
        <VerticalStackLayout Margin="16">
            <Label>Test</Label>
            <ListView ItemsSource="{Binding TopGroups}" IsGroupingEnabled="True">
                <ListView.GroupHeaderTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <Label Text="{Binding Name}"
                                   FontSize="18"
                                   FontAttributes="Bold" />
                        </ViewCell>
                    </DataTemplate>
                </ListView.GroupHeaderTemplate>
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <Grid Padding="10" >
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto" />
                                </Grid.RowDefinitions>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto" />
                                    <ColumnDefinition Width="10" />
                                    <ColumnDefinition Width="Auto" />
                                </Grid.ColumnDefinitions>
                                <Label Grid.Column="0"    
                                       Text="{Binding Numbering}"/>
                                <Label Grid.Column="2"
                                       Text="{Binding ToText1}"/>
                            </Grid>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </VerticalStackLayout>
    </Border>
</toolkit:Expander>

MyPage.xaml.cs:

public partial class SitzungsPage
{
    public MyPage(MyPageViewModel myPageViewModel)
    {
        BindingContext = myPageViewModel;
        InitializeComponent();
    }
    //...
}

MyPageViewModel.cs (relevant excerpts, using CommunityToolkit.Maui):

[ObservableProperty]
private List<TopGroup> _topGroups;

private void CreateTopGroups()
{
    TopGroups = new List<TopGroup>();
    var tops = new List<Top>();

    foreach (var to in ToDtos)
    {
        var top = new Top();

        switch (to.Status)
        {
            case 1:
            {
                _isPuplicList ??= true;
                if (_isPuplicList is false)
                {
                    TopGroups.Add(new TopGroup("Not Public", tops));
                    tops = new List<Top>();
                    _isPuplicList = true;
                }
                break;
            }
            case 2:
            {
                _isPuplicList ??= false;
                if (_isPuplicList is true)
                {
                    TopGroups.Add(new TopGroup("Public", tops));
                    tops = new List<Top>();
                    _isPuplicList = false;
                }
                break;
            }
        }

        top.ToText1 = to.ToText1;
        top.Numbering = to.Number;
        tops.Add(top);
    }

    if (tops.Count > 0 && _isPuplicList is not null)
    {
        TopGroups.Add(new TopGroup((bool)_isPuplicList ? "Public" : "Not Public", tops));
    }
}

TopGroup:

public class TopGroup : List<Top>
{
    public string Name { get; private set; }

    public TopGroup(string name, IEnumerable<Top> tops) : base(tops)
    {
        Name = name;
    }
}

Top:

public class Top
{
    public string Numbering { get; set; }

    public string ToText1 { get; set; }

    public List<DokumentDto> Documents { get; set; }
}

Solution

  • Now that question has been edited to show how TopGroup is filled, the problem is easier to spot.

    An efficient solution is to not set the property until the list is filled. Create the list in a local variable; set property after filling the local list:

    var topGroups = new List<TopGroup>();
    
    ...
       topGroups.Add(...);   <-- everywhere that an item is added.
    ...
    
    TopGroups = topGroups;   // Finally, set the property.
    

    When a list is being created from scratch, this might be better performance than clearing the property's list, and adding them one at a time, which could result in multiple update events. Or the difference might be negligible, depending on when this is called, how many items in list, complexity of display of each item.