在 MVVM 中将 SearchBar 与 ObservableCollection 结合使用
Using SearchBar with ObservableCollection in MVVM
我在我的应用程序中创建了一个搜索页面,我希望能够在 ViewModel 中搜索我的 ObservableCollection
项并将它们显示到 CollectionView
上。到目前为止,这就是我所做的,我得到一个例外,即 System.Reflection.TargetInvocationException: 'Exception has been thrown by the target of an invocation.'
每次我 运行 应用程序。
搜索页面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
}
编辑3:
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>
另一方面,您应该创建原始列表的私有副本并添加与 public 集合相同的项目
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
方法应该清理 public 集合(您正在显示的集合)并使用私有作为备份
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);
}
}
我在我的应用程序中创建了一个搜索页面,我希望能够在 ViewModel 中搜索我的 ObservableCollection
项并将它们显示到 CollectionView
上。到目前为止,这就是我所做的,我得到一个例外,即 System.Reflection.TargetInvocationException: 'Exception has been thrown by the target of an invocation.'
每次我 运行 应用程序。
搜索页面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
}
编辑3:
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>
另一方面,您应该创建原始列表的私有副本并添加与 public 集合相同的项目
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
方法应该清理 public 集合(您正在显示的集合)并使用私有作为备份
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);
}
}