Home > Net >  ObservableCollection in ViewModel doesn't render after filtering in - Maui/WinUI
ObservableCollection in ViewModel doesn't render after filtering in - Maui/WinUI

Time:06-10

I'm playing around with a MAUI application, and have an ObservableCollection bound to a FlexLayout in Xaml ContentPage. The collection displays fine initially, but I've added some filtering logic, and after filtering the collection fails to render. This appears to only occur on WinUI, as it filters and renders correctly on the Android emulator, I've not yet set up an IoS simulator. The issue also appears to be specific to the FlexLayout, as when I change the flex layout to a ListView, it filters and renders correctly as well.

Any advice on how to fix this and continue to use the flex layout would be greatly appreciated.

The ContentPage

<?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"
             xmlns:Views="clr-namespace:Views"
             x:Class="Collection"
             Title="Collection">
    <ContentPage.Content>
        <ScrollView>
            <StackLayout>
                <!--Search-->
                <Views:SearchAndFilterView x:Name="SearchView"
                                           Placeholder="Search collection..." 
                                           TextChanged="SearchView_TextChanged"/>


                <!-- Collection-->
                <FlexLayout x:Name="CollectionFlexLayout"
                            BindableLayout.ItemsSource="{Binding Collection}"
                            HorizontalOptions="CenterAndExpand"
                            Wrap="Wrap">
                    <BindableLayout.ItemTemplate>
                        <DataTemplate>
                            <Views:CollectionCardView 
                                                      IconImageSource="{Binding Image}"
                                                      CardTitle="{Binding Name}"
                                                      Designer="{Binding Designers[0]}"
                                                      Publisher="{Binding Publishers[0]}"
                                                      YearPublished="{Binding YearPublished}"
                                                      PlayCount="{Binding NumPlays}"
                                                      MaxPlayers="{Binding MaxPlayers}"
                                                      MinPlayers="{Binding MinPlayers}"
                                                      PlayTime="{Binding PlayTime}"
                                                      MinAge="{Binding MinAge}"/>
                        </DataTemplate>
                    </BindableLayout.ItemTemplate>                    
                </FlexLayout>
            </StackLayout>
        </ScrollView>
    </ContentPage.Content>
</ContentPage>


The ContentPage code behind

public partial class Collection : ContentPage
{
    public ICollectionViewModel collectionViewModel;

    public Collection()
    {
        InitializeComponent();
        Init();
        InitAsync();
    }

    public void Init()
    {
        collectionViewModel = BootStrapper.Resolve<ICollectionViewModel>();
        BindingContext = collectionViewModel;
    }

    public async void InitAsync()
    {
        await collectionViewModel.GetCollection("testAcct");
    }

   

    private void SearchView_TextChanged(object sender, TextChangedEventArgs e)
    {
        collectionViewModel.FilterCollection(SearchView.SearchText, SearchView.AgeText, SearchView.PlayerCountText, SearchView.PlayTimeText);
    }

The ViewModel:

public class CollectionViewModel : ViewModelBase, ICollectionViewModel
    {
        private readonly ILogger _logger;
        private readonly ICollectionHandler _collectionHandler;
        private readonly ICollectionHelper _collectionHelper;
        private readonly IThingHandler _thingHandler;

        private ObservableCollection<CollectionPageItem> _collection = new();
        private IEnumerable<CollectionPageItem> _fullCollection;
        private bool _isBusy;

        public CollectionViewModel(ILogger logger, ICollectionHandler collectionHandler, IThingHandler thingHandler,
            ICollectionHelper collectionHelper)
        {
            _logger = logger;
            _collectionHandler = collectionHandler;
            _collectionHelper = collectionHelper;
            _thingHandler = thingHandler;
        }

        /// <summary>
        /// True if the ViewModel is querying data.
        /// </summary>
        public bool IsBusy
        {
            get => _isBusy;
            set { _isBusy = value; OnPropertyChanged(nameof(IsBusy)); }
        }

        /// <summary>
        /// The Collection to display.
        /// </summary>
        public ObservableCollection<CollectionPageItem> Collection
        {
            get { return _collection; }
            set { _collection = value; OnPropertyChanged(nameof(Collection)); }
        }

        /// <summary>
        /// Loads the <see cref="Collection"/> property for the given user.
        /// </summary>
        /// <param name="userName">The user to load a collection for.</param>
        public async Task GetCollection(string userName)
        {
            IsBusy = true;

            try
            {
                var collectionResponse = await _collectionHandler.GetCollectionByUserName(userName);
                var things = await _thingHandler.GetThingsById(collectionResponse.Item.Items.Select(x => x.Id).ToList());

                _fullCollection = _collectionHelper.CoalesceCollectionData(collectionResponse.Item.Items, things.Item.Things);
                Collection = _fullCollection.ToObservableCollection();

            }
            catch (Exception ex)
            {
                _logger.Error(ex.Message);
            }
            finally
            {
                IsBusy = false;
            }
        }

        //TODO: Make this work
        public void FilterCollection(string name, string age, string playercount, string time)
        {
            IEnumerable<CollectionPageItem> query = _fullCollection;

            if (!string.IsNullOrWhiteSpace(name))
            {
                query = query.Where(x => x.Name.ToLower().Contains(name));
            }

            if (int.TryParse(age, out int parsedAge))
            {
                query = query.Where(x => x.MinAge >= parsedAge);
            }

            if (int.TryParse(playercount, out int parsedPlayercount))
            {
                query = query.Where(x => x.MinPlayers >= parsedPlayercount);
            }

            if (int.TryParse(time, out int parsedTime))
            {
                query = query.Where(x => x.PlayTime <= parsedTime);
            }

            Collection = query.ToObservableCollection();
        }
    }

CodePudding user response:

FlexLayout seems to have some spacing issues in Windows. A workaround is setting HorizontalOptions="Center" for the FlexLayout and giving to it HeightRequest and WidthRequest values.

  • Related