不要在视图中创建CollectionViewSource。 相反,在视图模型中创建一个类型为ICollectionView的属性,并将ListView.ItemsSource绑定到它。完成这些操作后,您可以在FilterText属性的Setter中放置逻辑,每当用户更改该属性时调用ICollectionView上的Refresh()。
您会发现这也简化了排序问题:您可以将排序逻辑构建到视图模型中,然后公开视图可以使用的命令。
编辑:
这是一个非常简单的MVVM动态排序和过滤集合视图演示。此演示不实现FilterText,但一旦您理解了其工作原理,就不难实现使用该属性而不是现在正在使用的硬编码过滤器的谓词的FilterText属性。
(请注意,这里的视图模型类不实现属性更改通知。这只是为了保持代码简单:由于此演示中实际上没有更改属性值,因此不需要属性更改通知。)
首先是您的项目的一个类:
public class ItemViewModel
{
public string Name { get; set; }
public int Age { get; set; }
}
现在,为应用程序创建一个视图模型。这里有三件事情要做:首先,它创建并填充自己的ICollectionView
;第二,它公开一个ApplicationCommand
(见下文),供视图使用以执行排序和过滤命令;最后,它实现一个Execute
方法,用于对视图进行排序或过滤:
public class ApplicationViewModel
{
public ApplicationViewModel()
{
Items.Add(new ItemViewModel { Name = "John", Age = 18} );
Items.Add(new ItemViewModel { Name = "Mary", Age = 30} );
Items.Add(new ItemViewModel { Name = "Richard", Age = 28 } );
Items.Add(new ItemViewModel { Name = "Elizabeth", Age = 45 });
Items.Add(new ItemViewModel { Name = "Patrick", Age = 6 });
Items.Add(new ItemViewModel { Name = "Philip", Age = 11 });
ItemsView = CollectionViewSource.GetDefaultView(Items);
}
public ApplicationCommand ApplicationCommand
{
get { return new ApplicationCommand(this); }
}
private ObservableCollection<ItemViewModel> Items =
new ObservableCollection<ItemViewModel>();
public ICollectionView ItemsView { get; set; }
public void ExecuteCommand(string command)
{
ListCollectionView list = (ListCollectionView) ItemsView;
switch (command)
{
case "SortByName":
list.CustomSort = new ItemSorter("Name") ;
return;
case "SortByAge":
list.CustomSort = new ItemSorter("Age");
return;
case "ApplyFilter":
list.Filter = new Predicate<object>(x =>
((ItemViewModel)x).Age > 21);
return;
case "RemoveFilter":
list.Filter = null;
return;
default:
return;
}
}
}
排序有些糟糕;你需要实现一个IComparer
:
public class ItemSorter : IComparer
{
private string PropertyName { get; set; }
public ItemSorter(string propertyName)
{
PropertyName = propertyName;
}
public int Compare(object x, object y)
{
ItemViewModel ix = (ItemViewModel) x;
ItemViewModel iy = (ItemViewModel) y;
switch(PropertyName)
{
case "Name":
return string.Compare(ix.Name, iy.Name);
case "Age":
if (ix.Age > iy.Age) return 1;
if (iy.Age > ix.Age) return -1;
return 0;
default:
throw new InvalidOperationException("Cannot sort by " +
PropertyName);
}
}
}
要触发视图模型中的Execute
方法,需要使用一个ApplicationCommand
类,它是ICommand
的一个简单实现,将视图中按钮上的CommandParameter
路由到视图模型的Execute
方法。我采用这种方式实现是因为我不想在应用程序视图模型中创建一堆RelayCommand
属性,并且我想将所有的排序/过滤都放在一个方法中,以便可以轻松地查看它是如何完成的。
public class ApplicationCommand : ICommand
{
private ApplicationViewModel _ApplicationViewModel;
public ApplicationCommand(ApplicationViewModel avm)
{
_ApplicationViewModel = avm;
}
public void Execute(object parameter)
{
_ApplicationViewModel.ExecuteCommand(parameter.ToString());
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
}
最后,这是应用程序的MainWindow
:
<Window x:Class="CollectionViewDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:CollectionViewDemo="clr-namespace:CollectionViewDemo"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<CollectionViewDemo:ApplicationViewModel />
</Window.DataContext>
<DockPanel>
<ListView ItemsSource="{Binding ItemsView}">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Name}"
Header="Name" />
<GridViewColumn DisplayMemberBinding="{Binding Age}"
Header="Age"/>
</GridView>
</ListView.View>
</ListView>
<StackPanel DockPanel.Dock="Right">
<Button Command="{Binding ApplicationCommand}"
CommandParameter="SortByName">Sort by name</Button>
<Button Command="{Binding ApplicationCommand}"
CommandParameter="SortByAge">Sort by age</Button>
<Button Command="{Binding ApplicationCommand}"
CommandParameter="ApplyFilter">Apply filter</Button>
<Button Command="{Binding ApplicationCommand}"
CommandParameter="RemoveFilter">Remove filter</Button>
</StackPanel>
</DockPanel>
</Window>
bool Include(object o)
),这样我就不需要在代码后台中拥有事件处理程序了? - Pieter Müller