James Palmer
James Palmer

Reputation: 49

MVVM Searchbar Xamarin.Forms

I am trying to implement a searchbar using MVVM in Xamarin.forms. so far I have managed to borrow some code from around the internet and it seems to do go through the motions of the search. the only issue is I don't know what code to put in the command.

I would like the search bar to search recipeNames from a list of Recipes. this information is all stored on a local database and displayed using an observable collection.

please can you help me work it out.

XAML

<SearchBar x:Name="SearchBar" 
               Placeholder="Search" 
               SearchCommand="{Binding SearchCommand}" 
               SearchCommandParameter="{Binding Text, Source={x:Reference SearchBar}}"
               Text="{Binding SearchText, Mode=TwoWay}">
        <SearchBar.Behaviors>
            <local:TextChangedBehavior />
        </SearchBar.Behaviors>
    </SearchBar>
    <ListView x:Name="ListViewItems"
          ItemsSource="{Binding Recipes}"
          IsPullToRefreshEnabled="True"
          Refreshing="ListViewItems_Refreshing"
          SelectedItem="{Binding SelectedRecipe}">

Text changed Behaviour

    class TextChangedBehavior: Behavior<Xamarin.Forms.SearchBar>
{
    protected override void OnAttachedTo(Xamarin.Forms.SearchBar bindable)
        {
            base.OnAttachedTo(bindable);
            bindable.TextChanged += Bindable_TextChanged;
        }

        protected override void OnDetachingFrom(Xamarin.Forms.SearchBar bindable)
        {
            base.OnDetachingFrom(bindable);
            bindable.TextChanged -= Bindable_TextChanged;
        }

        private void Bindable_TextChanged(object sender, TextChangedEventArgs e)
        {
            ((Xamarin.Forms.SearchBar)sender).SearchCommand?.Execute(e.NewTextValue);
        }

    }

and viewModel

public class RecipeListViewModel : ObservableCollection<Recipe>
{
    private ObservableCollection<Recipe> Recipes {get; set;}
    public INavigation Navigation { get; internal set; }
    public ICommand NewAddPage { get; protected set; }
    public RecipeListViewModel(INavigation navigation)
    {
        this.Navigation = navigation;
        Recipes = new ObservableCollection<Recipe>();
        this.NewAddPage = new Command(async () => await CreateNewAddPage());
        Init();
    }

    // Gets all recipes from the database and adds them to the observable collection
    private void Init()
    {
        var enumarator = App.RecipeDbcontroller.GetRecipe();
        if (enumarator == null)
        {
            App.RecipeDbcontroller.SaveRecipe(new Recipe { RecipeName = "Moussaka", Serves = 6, PrepTime = "30", CookTime = "2 Hours", MealType = "Dinner" });

            enumarator = App.RecipeDbcontroller.GetRecipe();
        }
        while (enumarator.MoveNext())
        {
            //cleans database of all empty records
            if (enumarator.Current.RecipeName == null || enumarator.Current.CookTime == null)
            {
                App.RecipeDbcontroller.DeleteRecipe(enumarator.Current.RecipeID);
            }
            else
                Add(enumarator.Current);
        }
    }


    private ICommand _searchCommand;
    public ICommand SearchCommand
    {
        get
        {
            return _searchCommand ?? (_searchCommand = new Command<string>((text) =>
            {
                **// THIS IS WHAT I DON'T KNOW WHAT TO DO**
            }));
        }
    }

    private string _searchText { get; set; }
    public string SearchText
    {
        get { return _searchText; }
        set
        {
            if (_searchText != value)
            {
                _searchText = value;
            }
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

RecipeDatabaseController Class

 public RecipeDatabaseController()
        {
            this.database = DependencyService.Get<ISQLite>().GetConnection();
            this.database.CreateTable<Recipe>();
        }

        //Recipe CRUD
        public IEnumerator<Recipe> GetRecipe()
        {
            lock (locker)
            {
                if (database.Table<Recipe>().Count() == 0)
                {
                    return null;
                }
                else
                {
                    return this.database.Table<Recipe>().GetEnumerator();
                }
            }
        }

        public IEnumerator<Recipe> GetRecipeBySearchTerm(text)
        {
            var enumarator = GetRecipe();
            lock (locker)
            {
                while (enumarator.MoveNext)
                {
                    if(enumarator.Current.RecipeName.Contains(text)
                        return this.
                }
            }
        }

        public int SaveRecipe(Recipe recipe)
        {
            lock (locker)
            {
                if (recipe.RecipeID != 0)
                {
                    this.database.Update(recipe);
                    return recipe.RecipeID;
                }
                else
                {
                    return this.database.Insert(recipe);
                }
            }
        }

        public int DeleteRecipe(int Id)
        {
            lock (locker)
            {
                return this.database.Delete<Recipe>(Id);
            }
        }

Upvotes: 1

Views: 6772

Answers (2)

James Palmer
James Palmer

Reputation: 49

right so the search command ought to look like this.

public ICommand SearchCommand => _searchCommand ?? (_searchCommand = new Command<string>((text) =>
{
    if (text.Length >=1)
    {
        Recipes.Clear();
        Init();
        var suggestions = Recipes.Where(c => c.RecipeName.ToLower().StartsWith(text.ToLower())).ToList();
        Recipes.Clear();
        foreach (var recipe in suggestions)
         Recipes.Add(recipe);

    }
    else
    {
        Recipes.Clear();
        Init();
        ListViewVisible = true;
        SuggestionsListViewVisible = false;
    }

}));

Upvotes: 2

Luminous_Dev
Luminous_Dev

Reputation: 614

using System.Linq;

    //Recipe CRUD
    public IEnumerable<Recipe> GetRecipe()
    {
        lock (locker)
        {
            return this.database.Table<Recipe>();
        }
    }

    public IEnumerable<Recipe> GetRecipeBySearchTerm(string text)
    {
        var recipes = GetRecipe();
        lock (locker)
        {
            return recipes.Where(m => m.RecipeName.ToLower().Contains(text));
        }
    }

Add the using System.Linq reference

Change those two methods and return IEnumerable

Note. RecipeName is the property you want to filter your Recipe with.

And your search command as below

private ICommand _searchCommand;
public ICommand SearchCommand
{
    get
    {
        return _searchCommand ?? (_searchCommand = new Command<string>((text) =>
        {
            var filteredRecipes = App.RecipeDbcontroller.GetRecipeBySearchTerm(text);

            recipes.Clear();
            foreach(var recipe in filteredRecipes )
                 recipes.Add(recipe);
        }));
    }
}

I have not tested this code, so not sure where I might get errors, but you can work out the rest because the logic is given to you

Good luck

Upvotes: 1

Related Questions