Search code examples
mauimaui-community-toolkit

Show CollectionView in CommunityToolkit.Maui Popup


I'm trying to display a scrollable list in a popup with a CollectionView and I get an exception "Collection was modified; enumeration operation may not execute" as soon as it is displayed. I don't understand why because my binded list with ItemsSource is never modified.

Thanks for your help.

This is PopupTest.xaml

<?xml version="1.0" encoding="utf-8" ?>
<toolkit:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
           xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
           xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
           x:Class="Popup.PopupTest">

<Grid>
    <CollectionView ItemsSource="{Binding Sources}"
                    ItemSizingStrategy="MeasureAllItems"
                    VerticalScrollBarVisibility="Always"
                    SelectionMode="Single">

        <CollectionView.ItemsLayout>
            <LinearItemsLayout Orientation="Vertical"
                               ItemSpacing="0" />
        </CollectionView.ItemsLayout>

        <CollectionView.ItemTemplate>
            <DataTemplate>
                <Grid Margin="10,10,10,10"
                      RowSpacing="5">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>

                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto" />
                        <ColumnDefinition Width="Auto" />
                        <ColumnDefinition Width="50" />
                    </Grid.ColumnDefinitions>

                    <Image Grid.Column="0"
                           Source="{Binding ...}"
                           WidthRequest="40"
                           HeightRequest="40"
                           Margin="15,10,25,10" />

                    <Label Grid.Column="1"
                           Text="{Binding ...}"
                           Margin="0,0,25,0"
                           FontSize="17"
                           LineBreakMode="WordWrap"
                           MaxLines="2"
                           HorizontalOptions="Start"
                           VerticalOptions="Center" />

                    <RadioButton Grid.Column="2"
                                 IsChecked="{Binding ...}" />
                </Grid>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</Grid>
</toolkit:Popup>

This is PopupTest.xaml.cs

public partial class PopupTest : CommunityToolkit.Maui.Views.Popup
{
    public ObservableCollection<...> Sources { get; set; }

    public PopupTest()
    {
        Sources = new ObservableCollection<...>([other list]);

        InitializeComponent();
    }
}

This is how I display the popup

var popup = new PopupTest();
this.ShowPopup(popup);

Solution

  • Here I made a demo using CommunityToolkit Popup. I am using .NET8.0 with CommunityToolkit.Maui 7.0.1 targeting on Android emulator.

    After I create a default maui project. I add a Popup Page to my project mostly based on your code. Here it is,

    1.the XAML for Popup,

    <toolkit:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="MauiApp2.PopupTest"
                 xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
                 >
        <Grid WidthRequest="400" HeightRequest="600">
            <CollectionView ItemsSource="{Binding Sources}"
                        ItemSizingStrategy="MeasureAllItems"
                        VerticalScrollBarVisibility="Always"
                        SelectionMode="Single">
    
                <CollectionView.ItemsLayout>
                    <LinearItemsLayout Orientation="Vertical"
                                   ItemSpacing="0" />
                </CollectionView.ItemsLayout>
    
                <CollectionView.ItemTemplate>
                    <DataTemplate>
                        <Grid Margin="10,10,10,10"
                          RowSpacing="5">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto" />
                            </Grid.RowDefinitions>
    
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="50" />
                            </Grid.ColumnDefinitions>
    
                            <Image Grid.Column="0"
                               Source="{Binding ImageName}"
                               WidthRequest="40"
                               HeightRequest="40"
                               Margin="15,10,25,10" />
    
                            <Label Grid.Column="1"
                               Text="{Binding Name}"
                               Margin="0,0,25,0"
                               FontSize="17"
                               LineBreakMode="WordWrap"
                               MaxLines="2"
                               HorizontalOptions="Start"
                               VerticalOptions="Center" />
    
                            <RadioButton Grid.Column="2"
                                     IsChecked="{Binding IsChecked}" />
                        </Grid>
                    </DataTemplate>
                </CollectionView.ItemTemplate>
            </CollectionView>
        </Grid>
    </toolkit:Popup>
    

    2.the code behind for Popup.

    public partial class PopupTest : Popup
    {
        public PopupTest()
        {
    
            InitializeComponent();
            BindingContext = new MyPopupViewModel();
        } 
    }
    

    3.the ViewModel for Popup. I just add some test data in it,

    public class MyPopupViewModel
    {
        public ObservableCollection<Item> Sources { get; set; }
        public IList<Item> items { get; set; } = new List<Item>();
    
        public MyPopupViewModel() 
        {
            items.Add(new Item { ImageName = "dotnet_bot.png", IsChecked = true, Name = "Item1" });
            items.Add(new Item { ImageName = "dotnet_bot.png", IsChecked = false, Name = "Item2" });
            items.Add(new Item { ImageName = "dotnet_bot.png", IsChecked = false, Name = "Item3" });
            items.Add(new Item { ImageName = "dotnet_bot.png", IsChecked = false, Name = "Item5" });
            items.Add(new Item { ImageName = "dotnet_bot.png", IsChecked = true, Name = "Item4" });
            Sources = new ObservableCollection<Item>(items);
        }
    }
    
    public class Item
    {
        public string Name { get; set; }
        public string ImageName { get; set; }
        public bool IsChecked { get; set; }
    }
    

    And after I click the button on MainPage,

        private void OnCounterClicked(object sender, EventArgs e)
        {
            var popup = new PopupTest();
            this.ShowPopup(popup);
        }
    

    Here is the effect on Android 13.0 - Pixel5 emulator,

    enter image description here