【问题标题】:MVVM Searchbar Xamarin.FormsMVVM 搜索栏 Xamarin.Forms
【发布时间】:2019-02-19 23:05:02
【问题描述】:

我正在尝试在 Xamarin.forms 中使用 MVVM 实现搜索栏。到目前为止,我已经设法从互联网上借用了一些代码,它似乎确实经历了搜索的过程。唯一的问题是我不知道在命令中输入什么代码。

我希望搜索栏从食谱列表中搜索食谱名称。这些信息都存储在本地数据库中,并使用可观察的集合显示。

请你帮我解决一下。

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}">

文字改变了行为

    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);
        }

    }

和视图模型

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 类

 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);
            }
        }

【问题讨论】:

  • 我想你已经知道该怎么做了,使用用户输入(命令 func 中的文本参数)搜索你的本地数据库,然后添加/删除变量 recipes,它是 observablecollection。此外,您的 listview itemsource 应该是 ItemsSource="{Binding recipes}"
  • 谢谢老兄,在代码中会是什么样子?
  • 当我将绑定设置为食谱时,它也不显示列表
  • 你的食谱是 privatepublic
  • App.RecipeDbcontroller.GetRecipe(); 这是什么?这是在获取所有食谱吗?

标签: c# xamarin.forms


【解决方案1】:

正确,所以搜索命令应该是这样的。

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;
    }

}));

【讨论】:

    【解决方案2】:
    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));
            }
        }
    

    添加using System.Linq 引用

    改变这两个方法并返回IEnumerable

    注意。 RecipeName 是您要过滤配方的属性。

    你的搜索命令如下

    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);
            }));
        }
    }
    

    我没有测试过这段代码,所以不确定我可能会在哪里出错,但是你可以解决剩下的问题,因为逻辑已经给你了

    祝你好运

    【讨论】:

    • 你是个天才,谢谢。绑定仍然不起作用。我已将食谱更改为公开,但它没有做任何事情。
    • @JamesPalmer 您是否在您的 cs.xaml 中添加了 BindingContext = new RecipeListViewModel ();
    • @JamesPalmer 我不知道为什么绑定不起作用,除非我在我的机器上运行你的代码
    • 是的,我做了它看起来像这样 BindingContext = new RecipeListViewModel(Navigation);
    • 对不起,如果不调试您的代码,我不知道发生了什么
    猜你喜欢
    • 2016-11-18
    • 2016-04-02
    • 1970-01-01
    • 2014-09-17
    • 2018-01-23
    • 1970-01-01
    • 1970-01-01
    • 2019-10-10
    • 1970-01-01
    相关资源
    最近更新 更多