Ответ 1
Не создавайте CollectionViewSource
в своем представлении. Вместо этого создайте свойство типа ICollectionView
в вашей модели представления и привяжите к нему ListView.ItemsSource
.
Как только вы это сделаете, вы можете поместить логику в средство настройки свойств FilterText
, которое вызывает Refresh()
в ICollectionView
всякий раз, когда пользователь меняет его.
Вы обнаружите, что это также упрощает проблему сортировки: вы можете построить логику сортировки в модели представления, а затем выставить команды, которые может использовать вид.
ИЗМЕНИТЬ
Вот довольно простая демонстрация динамической сортировки и фильтрации коллекции с использованием 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
on в представлении модели представления 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>