在MVVM中将SearchBar与ObservableCollection一起使用

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在MVVM中将SearchBar与ObservableCollection一起使用相关的知识,希望对你有一定的参考价值。

我已经在我的应用程序中创建了一个搜索页面,我希望能够在ViewModel中的ObservableCollection中搜索项目并将其显示在CollectionView上。到目前为止,这是我所做的,并且每次运行应用程序时都会出现异常,即System.Reflection.TargetInvocationException: 'Exception has been thrown by the target of an invocation.'SearchPage XAML

<!--Doctors Search Result-->
        <Grid Grid.Row="1">
            <CollectionView ItemsSource="Binding RecentDoctors">

                <CollectionView.ItemsLayout>
                    <ListItemsLayout Orientation="Vertical" ItemSpacing="15"/>
                </CollectionView.ItemsLayout>

                <CollectionView.ItemTemplate>
                    <DataTemplate>
                        <StackLayout Orientation="Horizontal">
                            <!--Image-->
                            <Frame BackgroundColor="Black"
                                   HeightRequest="20"
                                   WidthRequest="20"
                                   CornerRadius="100"
                                   Margin="20,0,0,0"
                                   HorizontalOptions="Start"
                                   VerticalOptions="Center"
                                   IsClippedToBounds="True">

                                <Image HorizontalOptions="Center"
                                       VerticalOptions="Center"/>
                            </Frame>

                            <StackLayout Orientation="Vertical"
                                         VerticalOptions="Center"
                                         Spacing="-3">
                                <!--Fullname-->
                                <Label Text="Binding DoctorsName"
                                       FontSize="19"
                                       FontAttributes="Bold"/>

                                <!--Specialization-->
                                <Label Text="Binding Specialization"
                                       FontSize="14"
                                       TextColor="LightGray"/>
                            </StackLayout>
                        </StackLayout>
                    </DataTemplate>
                </CollectionView.ItemTemplate>
            </CollectionView>
        </Grid>

        <!--Doctors Search Bar-->
        <Grid Grid.Row="0" ColumnSpacing="0" RowSpacing="0">
            <pancake:PancakeView BackgroundColor="#0F8DF4"
                                 HasShadow="True">
                <Grid>
                    <!--The SearchBar-->
                    <renderers:CustomSearchBar x:Name="doctorsSearchBar"
                               Placeholder="Search Doctors by Name, Specialization"
                               VerticalOptions="Center"
                               FontSize="17"
                               TextColor="Black"
                               WidthRequest="320"
                               Text="Binding SearchedText"
                               SearchCommand="Binding SearchBarCommand"
                               SearchCommandParameter="Binding Text, Source=x:Reference doctorsSearchBar"/>
                </Grid>
            </pancake:PancakeView>
        </Grid>

SearchPage ViewModel

public class TelemedSearchPageViewModel : BaseViewModel
    
        private string _searchedText;
        public string SearchedText
        
            get  return _searchedText; 

            set
            
                _searchedText = value;
                OnPropertyChanged();
                Search();
            
        

        public ObservableCollection<RecentDoctorsInfo> RecentDoctors  get; set;  = new ObservableCollection<RecentDoctorsInfo>();

        public ICommand SearchBarCommand  get; set; 

        /// <summary>
        /// Main Constructor
        /// </summary>
        public TelemedSearchPageViewModel()
        
            SearchBarCommand = new RelayCommand(Search);

            //RecentDoctorsList
            RecentDoctors.Add(new RecentDoctorsInfo()
            
                DoctorsName = "Steven Strange",
                Specialization = "Sorcerer Supreme",
                Location = "177a Bleecker St. | USA"
            );

            RecentDoctors.Add(new RecentDoctorsInfo()
            
                DoctorsName = "Peter Parker",
                Specialization = "Spiderman",
                Location = "177a Bleecker St. | USA"
            );

            RecentDoctors.Add(new RecentDoctorsInfo()
            
                DoctorsName = "Bruce Banner",
                Specialization = "The Hulk",
                Location = "177a Bleecker St. | USA"
            );

            RecentDoctors.Add(new RecentDoctorsInfo()
            
                DoctorsName = "Reed Richards",
                Specialization = "Mr.Fantastic",
                Location = "177a Bleecker St. | USA"
            );  
        

        #region METHODS
        public void Search()
        
            if (RecentDoctors != null && RecentDoctors.Count >0)
            
                var temp = RecentDoctors.Where(x => x.DoctorsName.ToLower().Contains(SearchedText.ToLower()));

                foreach (var item in temp)
                
                    RecentDoctors.Add(item);
                
            
        

        #endregion
    

Edit3:

if (RecentDoctors != null && RecentDoctors.Count > 0)
            
                var results = RecentDoctors.Where(x => x.DoctorsName.ToLower().Contains(SearchedText.ToLower()));
                SearchResults.Clear();
                foreach (RecentDoctorsInfo item in results)
                
                    SearchResults.Add(item);
                
            
            else
            
                RecentDoctors.Clear();
            
答案

如果要在用户键入时执行搜索,则应使用doscs建议的行为

public class SearchBarTextChangedBehavior : Behavior<SearchBar>

    protected override void OnAttachedTo(SearchBar bindable)
    
        base.OnAttachedTo(bindable);
        bindable.TextChanged += this.SearchBar_TextChanged;
    

    protected override void OnDetachingFrom(SearchBar bindable)
    
        base.OnDetachingFrom(bindable);
        bindable.TextChanged -= this.SearchBar_TextChanged;
    

    private void SearchBar_TextChanged(object sender, TextChangedEventArgs e)
    
        ((SearchBar)sender).SearchBarCommand?.Execute(e.NewTextValue);
    

然后将行为附加到您的SearchBar

<renderers:CustomSearchBar
    x:Name="doctorsSearchBar"
    Placeholder="Search Doctors by Name, Specialization"
    VerticalOptions="Center"
    FontSize="17"
    TextColor="Black"
    WidthRequest="320"
    Text="Binding SearchedText"
    SearchCommand="Binding SearchBarCommand">
    <renderers:CustomSearchBar.Behaviors>
        <behaviors:SearchBarTextChangedBehavior />
    </renderers:CustomSearchBar.Behaviors>
</renderers:CustomSearchBar>

另一方面,您应该创建原始列表的私有副本,并添加与公共收藏夹相同的项目

private List<RecentDoctorsInfo> originalRecentDoctorsList = new List<RecentDoctorsInfo>();

public ObservableCollection<RecentDoctorsInfo> RecentDoctors  get; set;  = new ObservableCollection<RecentDoctorsInfo>();

public ICommand SearchBarCommand  get; set; 

public TelemedSearchPageViewModel()

    SearchBarCommand = new RelayCommand(Search);

    //RecentDoctorsList
    RecentDoctors.Add(new RecentDoctorsInfo()
    
        DoctorsName = "Steven Strange",
        Specialization = "Sorcerer Supreme",
        Location = "177a Bleecker St. | USA"
    );

    RecentDoctors.Add(new RecentDoctorsInfo()
    
        DoctorsName = "Peter Parker",
        Specialization = "Spiderman",
        Location = "177a Bleecker St. | USA"
    );

    RecentDoctors.Add(new RecentDoctorsInfo()
    
        DoctorsName = "Bruce Banner",
        Specialization = "The Hulk",
        Location = "177a Bleecker St. | USA"
    );

    RecentDoctors.Add(new RecentDoctorsInfo()
    
        DoctorsName = "Reed Richards",
        Specialization = "Mr.Fantastic",
        Location = "177a Bleecker St. | USA"
    );

    // Backup copy list.
    originalRecentDoctorsList.Add(new RecentDoctorsInfo()
    
        DoctorsName = "Steven Strange",
        Specialization = "Sorcerer Supreme",
        Location = "177a Bleecker St. | USA"
    );

    originalRecentDoctorsList.Add(new RecentDoctorsInfo()
    
        DoctorsName = "Peter Parker",
        Specialization = "Spiderman",
        Location = "177a Bleecker St. | USA"
    );

    originalRecentDoctorsList.Add(new RecentDoctorsInfo()
    
        DoctorsName = "Bruce Banner",
        Specialization = "The Hulk",
        Location = "177a Bleecker St. | USA"
    );

    originalRecentDoctorsList.Add(new RecentDoctorsInfo()
    
        DoctorsName = "Reed Richards",
        Specialization = "Mr.Fantastic",
        Location = "177a Bleecker St. | USA"
    );  

最后,您的Search方法应清除公共集合(您正在显示的集合),并使用私有作为备份

private void Search()

    if (!string.IsNullOrEmpty(SearchedText))
    
        var filteredDoctors = RecentDoctors
                    .Where(x =>
                        x.DoctorsName.ToLower().Contains(SearchedText.ToLower()))
                    .ToList();

        RecentDoctors.Clear();

        foreach(var recentDoctor in filteredDoctors)
            RecentDoctors.Add(recentDoctor);
    
    else
    
        // This is when you clean the text from the search

        RecentDoctors.Clear();

        foreach(var originalRecentDoctor in originalRecentDoctorsList)
            RecentDoctors.Add(originalRecentDoctor);
    

以上是关于在MVVM中将SearchBar与ObservableCollection一起使用的主要内容,如果未能解决你的问题,请参考以下文章

WPF MVVM:组合框 SelectedValue 绑定

如何在新的 navigationItem.searchController 中将 SearchBar 居中?

Kendo MVVM 数据绑定 Style

MVVM 模块中闭包的目的是啥? [复制]

如何从 kendo maskedtextbox mvvm 值绑定中删除提示字符

在 MVVM 中将 Observable 集合绑定到 wrappanel