Search code examples
c#xamlmvvmdata-bindingmaui

How to implement the TextChanged the CommunityToolkit MVVM way


I am using the .Net Maui's community toolkit and have some mvvm binding going on in a project.

I have a collectionview and a Searchbar connected to a ViewModel. I wish to implement the Searchbar's is TextChanged, but with no luck.

it is saying,

Event "Textchanged" can only be bound to properties of delegate type 'EventHandler'1'

I saw this Solution here on stack overflow, I however am using the COmmunityToolkit.

Xaml Code

public partial class CommunityPostsPagesViewModel : 
ObservableObject
{


private readonly ISecureStorageService _secureStorage;
private readonly IAppSettingGeneratorService _appsettingGenerator;
private readonly ICommunityPostCoreApiService 
 _communitypostService;
private readonly IClientCryptoGraphy _cryptoGraphy;
public CommunityPostsPagesViewModel(ISecureStorageService storage, 
ICommunityPostCoreApiService communitypostService
    , IAppSettingGeneratorService appsettingGenerator, 
IClientCryptoGraphy cryptoGraphy)
{
    _secureStorage = storage;
    _communitypostService = communitypostService;
    _appsettingGenerator = appsettingGenerator;
    _cryptoGraphy = cryptoGraphy;
    GetLastestComunityPost();
}


[ObservableProperty] 
string serchText;

[ObservableProperty]
ObservableCollection<ComunityPostsModel> comunityPosts;

[RelayCommand]
async void SearchTextChanged()
{
    ComunityPosts.Clear();
    var output = new ObservableCollection<ComunityPostsModel>();
    try
    {
        var apiResponse = await _communitypostService.GetAll();
        var searchResponse = apiResponse.Response.Where(m => 
               m.Title.ToLower().Contains(SerchText.ToLower()));
        if (!searchResponse.Any())
        {
            await Shell.Current.DisplayAlert("Oops!", $"There are 
                    currently no available community post with 
                     name: {SerchText}", "Ok");
            return;
        }
        else
        {
            foreach (var response in searchResponse)
            {
                output.Add(new ComunityPostsModel()
                {
                    DatePosted = response.DatePosted,
                    Body = response.Body,
                    Id = response.Id,
                    Title = response.Title

                });
            }
        }
    }
    catch (Exception ex)
    {

        await Shell.Current.DisplayAlert("Oops!", "Could not get 
   any comunity Post", "Ok");
    }
    ComunityPosts = output;
}

[RelayCommand]
async void ComunityPostSelected(ComunityPostsModel input)
{


    await Shell.Current.GoToAsync(nameof(CommunityPostPage), true, 
           new Dictionary<string, object>{
        {nameof(ComunityPostsModel),input}

    });

}

private async Task GetLastestComunityPost()
{
    var output = new ObservableCollection<ComunityPostsModel>();
    try
    {
        var apiResponse = await _communitypostService.GetAll();
        foreach (var response in apiResponse.Response)
        {
            output.Add(new ComunityPostsModel()
            {
                DatePosted = response.DatePosted,
                Body = response.Body,
                Id = response.Id,
                Title = response.Title
            });
        }
    }
    catch (Exception ex)
    {


    }

    ComunityPosts = output;
}

}

For the Xaml

<?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="AE.Pages.CommunityPostsPage"
         Title="Community Beta"
          xmlns:viewmodel ="clr-namespace:AE.ViewModels"
        x:DataType="viewmodel:CommunityPostsPagesViewModel"
          xmlns:itemViewModel ="clr-namespace:AE.Models"
       xmlns:ur="http://schemas.enisn- 
       projects.io/dotnet/maui/uraniumui/material"
         >

  <StackLayout Orientation="Vertical"
           Margin="25" Padding="25"
           >


<StackLayout>

  <SearchBar SearchCommand="{Binding SearchTextChangedCommand}" 
             Placeholder="Search Comunity Posts by Title" Text=" 
 {Binding SerchText}" 
             IsSpellCheckEnabled="True" TextChanged="{Binding 
  SearchTextChangedCommand}" Margin="00,00,00,25"/>
  <CollectionView ItemsSource="{Binding ComunityPosts}" >
    <CollectionView.ItemTemplate >
      <DataTemplate x:DataType="{x:Type 
   itemViewModel:ComunityPostsModel}" >
        <SwipeView  >
          <Frame>
            <Frame.GestureRecognizers>
              <TapGestureRecognizer Command="{Binding Source= 
  {RelativeSource AncestorType={x:Type 
    viewmodel:CommunityPostsPagesViewModel}}, 
  Path=ComunityPostSelectedCommand}" CommandParameter="{Binding .}" 
    />
            </Frame.GestureRecognizers>
            <StackLayout>
              <Label Text="{Binding Title}" FontFamily="Bold"/>
              <Label Text="{Binding DatePosted}" 
  FontSize="Caption"/>
            </StackLayout>
          </Frame>
        </SwipeView>
      </DataTemplate>
    </CollectionView.ItemTemplate>
  </CollectionView>


</StackLayout>

Solution

  • You can use EventToCommandBehavior of CommunityToolkit to bind Command:

    <SearchBar ....>
       <SearchBar.Behaviors>
           <toolkit:EventToCommandBehavior
                    EventName="TextChanged"
                    Command="{Binding TextChangedCommand}" />
       </SearchBar.Behaviors>
    </SearchBar>