Home > Blockchain >  MVVM binding SerchBar to Observable collection error
MVVM binding SerchBar to Observable collection error

Time:08-08

I am trying to use the search navigation on a collection view in an MAUI project. I, however, have put myself into a bind.

When I try to search for some content, I get the error:

System.InvalidOperationException: 'Collection was modified; enumeration operation may not execute.'

Below is the view and ViewModel:

The View:

<?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="KigooPCMauiSimple.Views.PropertyPage"
          xmlns:viewModel="clr-namespace:KigooPCMauiSimple.ViewModels.AppViewModel"
         Title="Properties">

<ContentPage.BindingContext>
 <viewModel:PropertiesViewModel/>
</ContentPage.BindingContext>
<StackLayout >

<SearchBar HorizontalOptions="FillAndExpand" 
           VerticalOptions="Start"
           Placeholder="Search Property"
           BackgroundColor="{AppThemeBinding Light={StaticResource Primary},
  Dark={StaticResource Gray500}}"
           Text="{Binding SearchTerm, Mode=TwoWay}"
           SearchCommand="{Binding SearchNameCommand}"
           
   />

<ListView ItemsSource="{Binding Properties}" 
          Margin="00,10,00,00"
          SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
          >
 
  
  
  <ListView.ItemTemplate >
    <DataTemplate>
      <ViewCell>
        <StackLayout Orientation="Horizontal">
          <StackLayout.GestureRecognizers>
            <TapGestureRecognizer CommandParameter="{Binding .}"
                                    Command="{Binding Source={RelativeSource AncestorType={x:Type viewModel:PropertiesViewModel}},Path=GoToDetailsCommand}"/>

          </StackLayout.GestureRecognizers>
          <Image HeightRequest="75" WidthRequest="75" 
                 Aspect="AspectFill" Source="{Binding MainImageName}"/>

          <StackLayout Orientation="Vertical" HorizontalOptions="StartAndExpand">
            <Label Text="{Binding Name}"/>
            <Label Text="{Binding Address}" FontSize="Subtitle"/>
          </StackLayout>

        </StackLayout>
      </ViewCell>
         
    </DataTemplate>
  </ListView.ItemTemplate>
</ListView>

And the Viewmodel

using CommunityToolkit.Mvvm.ComponentModel;
using KigooPCMauiSimple.Models;
using KigooPCMauiSimple.Services;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using CommunityToolkit.Mvvm.Input;
using System.Threading.Tasks;
using KigooPCMauiSimple.KigooTask;
using KigooPCMauiSimple.Views;

namespace KigooPCMauiSimple.ViewModels.AppViewModel
{



  public partial class PropertiesViewModel : BasePageViewModel
  {
 

   public PropertiesViewModel()
   {
     this.properties = GetProperties().Result;
   }




[ObservableProperty]
ObservableCollection<Property> properties;

[ObservableProperty]
Property selectedItem;


[ObservableProperty]
public string searchTerm;


[RelayCommand]
public void SearchName()
{



  if (string.IsNullOrEmpty(searchTerm))
  {
    searchTerm = string.Empty;
    
  }
  else
  {
    var zeSource = GetProperties().Result;
    searchTerm = searchTerm.ToLowerInvariant();
    var fiteredItems = zeSource.Where(m =>
    m.Name.ToLowerInvariant().Contains(searchTerm)
    || m.Address.ToLowerInvariant().Contains(searchTerm));

    if (fiteredItems is null)
    {
      foreach (var item in Properties)
      {
        properties.Remove(item);
      }
    }
    else
    {
      foreach (var item in Properties)
      {
        properties.Remove(item);
      }

      foreach (var item in fiteredItems)
      {
        properties.Add(item);
      }

    }

  }





}



[RelayCommand]
async Task GoToDetails(Property property)
{
  if (property is null)
  {
    return;
  }
  var detail = GetData.GetPropertyDetail(property.Id).Result;
  var description = Converters.HTMLToText(detail.Property.Description);
  detail.Property.Description = description;
  await Shell.Current.GoToAsync($"{nameof(DetailsPage)}", true,
    new Dictionary<string, object>()
    {
      {"PropertyDetailViewModel",detail }
    }

    );
}



public static async Task<ObservableCollection<Property>> GetProperties()
{
  var homeviewModel = await new PropertiesService().GetUserPropertiesViewModelAsync();

  var properties = new ObservableCollection<Property>();
  foreach (var item in homeviewModel.Properties)
  {
    properties.Add(item);
  }

  return properties;
}

}
}

You can clone the project and see the whole scope on Github.

I can no longer trace the fault.

CodePudding user response:

instead of doing this, which is not allowed in C# (you cannot modify a collection while you are iterating it)

foreach (var item in Properties)
  {
    properties.Remove(item);
  }

do this

Properties.Clear();
  • Related