MVVM 添加排序说明
MVVM Add Sort Description
我想在我已经实现的两个 Filters
之上添加 Sort
功能。
这就是我的代码目前的样子。
XAML
<Grid Grid.Row="1">
<StackPanel Orientation="Horizontal">
<Border BorderThickness="1" BorderBrush="Red">
<ComboBox
Name="SortComboBox"
SelectionChanged="View_SelectionChanged"
ItemsSource="{Binding sortOptions, Mode=OneWay}"
SelectedValue="{Binding selectedSortOption}"
/>
</Border>
<ComboBox
Name="GenreComboBox"
SelectionChanged="View_SelectionChanged"
ItemsSource="{Binding genreOptions, Mode=OneWay}"
SelectedValue="{Binding selectedGenreOption}"
/>
<TextBox Name="SearchTextBox" Width="154" TextChanged="Search_SelectionChanged" Text="{Binding Path=searchTerm, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Grid>
第一个 SortComboBox
用于 sort
电影,而 GenreComboBox
根据流派过滤电影。 SearchTextBox
是另一个按关键字查找电影的过滤器。
Behind Code
private void View_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
_moviePanelVM.DisplayMovies.View.Refresh();
}
private void Search_SelectionChanged(object sender, TextChangedEventArgs e)
{
_moviePanelVM.DisplayMovies.View.Refresh();
}
ViewModel
public MoviePanelViewModel()
{
...
...
this.DisplayMovies = new CollectionViewSource();
this.DisplayMovies.Source = this.Movies;
this.DisplayMovies.Filter += GenreFilter;
this.DisplayMovies.Filter += SearchFilter;
}
private void GenreFilter(object sender, FilterEventArgs e)
{
MediaDetail media = e.Item as MediaDetail;
if (selectedGenreOption != "All" && !media.genre.Contains(selectedGenreOption))
e.Accepted = false;
}
private void SearchFilter(object sender, FilterEventArgs e)
{
MediaDetail media = e.Item as MediaDetail;
if (!media.title.ToLower().Contains(searchTerm.ToLower()))
e.Accepted = false;
}
SortComboBox
可能有一个选定的值 A-Z
,在这种情况下我会 sort
一个特定的方式。或者它可能具有 Z-A
的值,在这种情况下我会 sort
另一种方式。
我的问题是,我应该在哪里添加 SortDescriptions
以及控制应该发生哪种排序的逻辑,以确保维护 MVVM 模式?
UPDATE
Code Behind
private void Sort_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
_moviePanelVM.SortMovies();
}
将我的 XAML
更改为:
<ComboBox
Name="SortComboBox"
SelectionChanged="Sort_SelectionChanged"
ItemsSource="{Binding sortOptions, Mode=OneWay}"
SelectedValue="{Binding selectedSortOption}"
/>
ViewModel
public void SortMovies()
{
DisplayMovies.SortDescriptions.Clear();
switch (SelectedSortOption)
{
case "A-Z":
DisplayMovies.SortDescriptions.Add(new SortDescription("title", ListSortDirection.Ascending)); break;
case "Z-A":
DisplayMovies.SortDescriptions.Add(new SortDescription("title", ListSortDirection.Descending)); break;
case "Release Date":
DisplayMovies.SortDescriptions.Add(new SortDescription("year", ListSortDirection.Descending)); break;
case "Rating":
DisplayMovies.SortDescriptions.Add(new SortDescription("rating", ListSortDirection.Descending)); break;
}
}
这行得通,但我想知道我是否应该这样做?由于 filters
只是 Refreshing
view
但是 sort
我在 ViewModel
.
中调用一个函数
我觉得你对MVVM设计模式的一些原则不清楚。理想情况下,您根本不希望视图隐藏任何代码,这包括事件。
您需要用命令替换事件。 Click here for a tutorial.
在下面的示例中,我没有在视图上使用任何事件,而是使用一个命令来绑定排序的更改以过滤电影。
以下是我使用的模型:
public class PropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Movie : PropertyChangedBase
{
private string _Name;
public string Name
{
get { return _Name; }
set
{
_Name = value;
OnPropertyChanged("Name");
}
}
}
命令如下:
public class SortChangedCommand : ICommand
{
MoviesViewModel _viewModel;
public SortChangedCommand(MoviesViewModel viewModel)
{
_viewModel = viewModel;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_viewModel.Sort(parameter as string);
}
}
请注意,该命令包含对视图模型的引用,并且在执行命令时它仅调用 Sort 方法。
这是视图模型:
public class MoviesViewModel : PropertyChangedBase
{
public ObservableCollection<Movie> Movies { get; set; }
private ObservableCollection<Movie> _FilteredMovies;
public ObservableCollection<Movie> FilteredMovies
{
get { return _FilteredMovies; }
set
{
_FilteredMovies = value;
//Have to implement property changed because in the sort method
//I am instantiating a new observable collection.
OnPropertyChanged("FilteredMovies");
}
}
public SortChangedCommand SortChangedCommand { get; set; }
public MoviesViewModel()
{
this.Movies = new ObservableCollection<Movie>();
#region Test Data
this.Movies.Add(new Movie()
{
Name = "Movie 1"
});
this.Movies.Add(new Movie()
{
Name = "Movie 2"
});
this.Movies.Add(new Movie()
{
Name = "Movie 3"
});
#endregion
//Copy the movies list to the filtered movies list (this list is displayed on the UI)
this.FilteredMovies = new ObservableCollection<Movie>(this.Movies);
this.SortChangedCommand = new SortChangedCommand(this);
}
public void Sort(string sortOption)
{
switch (sortOption)
{
case "A-Z": this.FilteredMovies = new ObservableCollection<Movie>(this.Movies.OrderBy(x => x.Name)); break;
case "Z-A": this.FilteredMovies = new ObservableCollection<Movie>(this.Movies.OrderByDescending(x => x.Name)); break;
}
}
}
视图模型包含两个列表,一个包含所有电影,另一个包含过滤后的电影列表。过滤后的电影将显示在 UI.
视图如下:
<Window x:Class="BorderCommandExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModels="clr-namespace:BorderCommandExample"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ViewModels:MoviesViewModel x:Key="ViewModel"/>
</Window.Resources>
<Grid DataContext="{StaticResource ViewModel}">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Command="{Binding SortChangedCommand}" CommandParameter="A-Z"
Content="A-Z"/>
<Button Command="{Binding SortChangedCommand}" CommandParameter="Z-A"
Content="Z-A"
Grid.Row="1"/>
<ListBox ItemsSource="{Binding FilteredMovies}"
DisplayMemberPath="Name"
Grid.Row="2"/>
</Grid>
注意:我没有使用 ComboBox,因为 ComboBox 控件没有 Command 属性,但您可以轻松 get around this.
当您点击任何按钮时,命令将执行并调用排序方法。然后 UI 将更新过滤后的电影。
UI 本身可能不是您想要的,但是使用命令来实现它正是您所需要的。
我想在我已经实现的两个 Filters
之上添加 Sort
功能。
这就是我的代码目前的样子。
XAML
<Grid Grid.Row="1">
<StackPanel Orientation="Horizontal">
<Border BorderThickness="1" BorderBrush="Red">
<ComboBox
Name="SortComboBox"
SelectionChanged="View_SelectionChanged"
ItemsSource="{Binding sortOptions, Mode=OneWay}"
SelectedValue="{Binding selectedSortOption}"
/>
</Border>
<ComboBox
Name="GenreComboBox"
SelectionChanged="View_SelectionChanged"
ItemsSource="{Binding genreOptions, Mode=OneWay}"
SelectedValue="{Binding selectedGenreOption}"
/>
<TextBox Name="SearchTextBox" Width="154" TextChanged="Search_SelectionChanged" Text="{Binding Path=searchTerm, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Grid>
第一个 SortComboBox
用于 sort
电影,而 GenreComboBox
根据流派过滤电影。 SearchTextBox
是另一个按关键字查找电影的过滤器。
Behind Code
private void View_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
_moviePanelVM.DisplayMovies.View.Refresh();
}
private void Search_SelectionChanged(object sender, TextChangedEventArgs e)
{
_moviePanelVM.DisplayMovies.View.Refresh();
}
ViewModel
public MoviePanelViewModel()
{
...
...
this.DisplayMovies = new CollectionViewSource();
this.DisplayMovies.Source = this.Movies;
this.DisplayMovies.Filter += GenreFilter;
this.DisplayMovies.Filter += SearchFilter;
}
private void GenreFilter(object sender, FilterEventArgs e)
{
MediaDetail media = e.Item as MediaDetail;
if (selectedGenreOption != "All" && !media.genre.Contains(selectedGenreOption))
e.Accepted = false;
}
private void SearchFilter(object sender, FilterEventArgs e)
{
MediaDetail media = e.Item as MediaDetail;
if (!media.title.ToLower().Contains(searchTerm.ToLower()))
e.Accepted = false;
}
SortComboBox
可能有一个选定的值 A-Z
,在这种情况下我会 sort
一个特定的方式。或者它可能具有 Z-A
的值,在这种情况下我会 sort
另一种方式。
我的问题是,我应该在哪里添加 SortDescriptions
以及控制应该发生哪种排序的逻辑,以确保维护 MVVM 模式?
UPDATE
Code Behind
private void Sort_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
_moviePanelVM.SortMovies();
}
将我的 XAML
更改为:
<ComboBox
Name="SortComboBox"
SelectionChanged="Sort_SelectionChanged"
ItemsSource="{Binding sortOptions, Mode=OneWay}"
SelectedValue="{Binding selectedSortOption}"
/>
ViewModel
public void SortMovies()
{
DisplayMovies.SortDescriptions.Clear();
switch (SelectedSortOption)
{
case "A-Z":
DisplayMovies.SortDescriptions.Add(new SortDescription("title", ListSortDirection.Ascending)); break;
case "Z-A":
DisplayMovies.SortDescriptions.Add(new SortDescription("title", ListSortDirection.Descending)); break;
case "Release Date":
DisplayMovies.SortDescriptions.Add(new SortDescription("year", ListSortDirection.Descending)); break;
case "Rating":
DisplayMovies.SortDescriptions.Add(new SortDescription("rating", ListSortDirection.Descending)); break;
}
}
这行得通,但我想知道我是否应该这样做?由于 filters
只是 Refreshing
view
但是 sort
我在 ViewModel
.
我觉得你对MVVM设计模式的一些原则不清楚。理想情况下,您根本不希望视图隐藏任何代码,这包括事件。
您需要用命令替换事件。 Click here for a tutorial.
在下面的示例中,我没有在视图上使用任何事件,而是使用一个命令来绑定排序的更改以过滤电影。
以下是我使用的模型:
public class PropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Movie : PropertyChangedBase
{
private string _Name;
public string Name
{
get { return _Name; }
set
{
_Name = value;
OnPropertyChanged("Name");
}
}
}
命令如下:
public class SortChangedCommand : ICommand
{
MoviesViewModel _viewModel;
public SortChangedCommand(MoviesViewModel viewModel)
{
_viewModel = viewModel;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_viewModel.Sort(parameter as string);
}
}
请注意,该命令包含对视图模型的引用,并且在执行命令时它仅调用 Sort 方法。
这是视图模型:
public class MoviesViewModel : PropertyChangedBase
{
public ObservableCollection<Movie> Movies { get; set; }
private ObservableCollection<Movie> _FilteredMovies;
public ObservableCollection<Movie> FilteredMovies
{
get { return _FilteredMovies; }
set
{
_FilteredMovies = value;
//Have to implement property changed because in the sort method
//I am instantiating a new observable collection.
OnPropertyChanged("FilteredMovies");
}
}
public SortChangedCommand SortChangedCommand { get; set; }
public MoviesViewModel()
{
this.Movies = new ObservableCollection<Movie>();
#region Test Data
this.Movies.Add(new Movie()
{
Name = "Movie 1"
});
this.Movies.Add(new Movie()
{
Name = "Movie 2"
});
this.Movies.Add(new Movie()
{
Name = "Movie 3"
});
#endregion
//Copy the movies list to the filtered movies list (this list is displayed on the UI)
this.FilteredMovies = new ObservableCollection<Movie>(this.Movies);
this.SortChangedCommand = new SortChangedCommand(this);
}
public void Sort(string sortOption)
{
switch (sortOption)
{
case "A-Z": this.FilteredMovies = new ObservableCollection<Movie>(this.Movies.OrderBy(x => x.Name)); break;
case "Z-A": this.FilteredMovies = new ObservableCollection<Movie>(this.Movies.OrderByDescending(x => x.Name)); break;
}
}
}
视图模型包含两个列表,一个包含所有电影,另一个包含过滤后的电影列表。过滤后的电影将显示在 UI.
视图如下:
<Window x:Class="BorderCommandExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModels="clr-namespace:BorderCommandExample"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ViewModels:MoviesViewModel x:Key="ViewModel"/>
</Window.Resources>
<Grid DataContext="{StaticResource ViewModel}">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Command="{Binding SortChangedCommand}" CommandParameter="A-Z"
Content="A-Z"/>
<Button Command="{Binding SortChangedCommand}" CommandParameter="Z-A"
Content="Z-A"
Grid.Row="1"/>
<ListBox ItemsSource="{Binding FilteredMovies}"
DisplayMemberPath="Name"
Grid.Row="2"/>
</Grid>
注意:我没有使用 ComboBox,因为 ComboBox 控件没有 Command 属性,但您可以轻松 get around this.
当您点击任何按钮时,命令将执行并调用排序方法。然后 UI 将更新过滤后的电影。
UI 本身可能不是您想要的,但是使用命令来实现它正是您所需要的。