Для меня оказалось значительным сюрпризом, что CollectionViewSource в WPF и CollectionViewSource в Windows 8 приложениях, работают по разному. На форуме MSDN, уже дважды возникал про это вопрос (здесь и здесь). Ну и простит меня LXGDARK пример я напишу на C#, но думаю у него проблем с этим не возникнет. Итак, под катом пример класса, который принимает коллекцию и позволяет ее фильтровать и сортировать.
Для начала, включим в наш класс функционал по уведомлению внешнего мира, о изменении свойств. Т.к. в Windows 8 приложениях обделили не только CollectionViewSource, но и DependencyProperty, отняв у него замечательный метод RegisterReadOnly. То я буду реализовывать класс с поддержкой INotifyPropertyChanged (зачем он нужен, можно почитать здесь).
Ладно, начинаем:
public event PropertyChangedEventHandler PropertyChanged;
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(p_propertyName));
}
}
}
Теперь добавим поле для хранения коллекции и свойство, через которое будут читать отфильтрованную и отсортированную коллекцию, кстати, не забываем про конструктор:
get
{
return _source;
}
}
_source = p_source;
OnPropertyChanged("View");
}
Ок, теперь, если мы передадим в класс коллекцию, мы сможем ее получить, но нам нужен немного больший функционал. Давайте добавим фильтрацию. Для этого, достаточно добавить свойство и немного поправить свойство View:
get
{
return _filter;
}
set
{
if (_filter != value)
{
_filter = value;
OnPropertyChanged("View");
}
}
}
get
{
IEnumerable
result = _source;
Func<bool>
filter = Filter;
if (filter != null)
{
result = result.Where(item => Filter(item));
}
return result;
}
}
Ок, осталось добавить сортировку. Аналогично, нам понадобится свойство и правка свойства View. В этот раз она будет чуть посложнее. Если у нас есть имя свойства по которому сортировать, то мы извлекаем его значение через Reflection и сортируем по нему, если вместо имени поля у нас пустая строка, то сортируем по самим объектам, ну а если null, то возвращаем в том же порядке, в каком значения были в исходной коллекции.
private string _orderPropertyName = null;
get
{
return _orderPropertyName;
}
set
{
if (_orderPropertyName != value)
{
_orderPropertyName = value;
OnPropertyChanged("View");
}
}
}
get
{
IEnumerable
result = _source;
Func<bool>
filter = Filter;
if (filter != null)
{
result = result.Where(item => filter(item));
}
if (!string.IsNullOrWhiteSpace(OrderPropertyName))
{
result = result.OrderBy(
item =>
{
Type type = item.GetType();
PropertyInfo property = type.GetTypeInfo().GetDeclaredProperty(OrderPropertyName);
return property.GetValue(item);
});
}
else
{
if (OrderPropertyName == "")
{
result = result.OrderBy(item => item);
}
}
return result;
}
}
Ну и давайте небольшой пример, в стиле MVVM.
Для примера, введем вот такой вспомогательный класс:
public string LastName { get; set; }
List<Person> result = new List<Person>();
result.Add(new Person { LastName = "Сидоров" });
result.Add(new Person { LastName = "Федоров" });
result.Add(new Person { LastName = "Иванов" });
result.Add(new Person { LastName = "Петров" });
return result;
}
}
Ну и ViewModel:
public ICommand SearchCommand { get; private set; }
Items = new AlCollectionViewSource<Person>(Person.GetPersonCollection());
SearchCommand = new MyCommand(() => Items.Filter = item => item.LastName.ToLower().Contains(FilterString.ToLower()));
OrderCommand = new MyCommand(() => Items.OrderPropertyName = "LastName");
}
}
Разметка окна:
<RowDefinition Height="auto" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Path=FilterString,Mode=TwoWay}" Width="150" Margin="5" />
<Button Content="Фильтр!" Command="{Binding SearchCommand}" />
<Button Content="Сортировка!" Command="{Binding OrderCommand}" />
</StackPanel>
<ListView x:Name="lvMain" Grid.Row="1" DataContext="{Binding Items}" ItemsSource="{Binding View}" DisplayMemberPath="LastName" />
</Grid>
Ну и картинки, как это работает. Так приложение выглядит после запуска:
Так, после набора в TextBox-е строки "ро" и нажатия на "Фильтр!":
Ну и после нажатия на кнопку "Сортировка!":
Вот как то так.
Для начала, включим в наш класс функционал по уведомлению внешнего мира, о изменении свойств. Т.к. в Windows 8 приложениях обделили не только CollectionViewSource, но и DependencyProperty, отняв у него замечательный метод RegisterReadOnly. То я буду реализовывать класс с поддержкой INotifyPropertyChanged (зачем он нужен, можно почитать здесь).
Ладно, начинаем:
public class AlCollectionViewSource : INotifyPropertyChanged
{public event PropertyChangedEventHandler PropertyChanged;
protected void
OnPropertyChanged(string p_propertyName)
{PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(p_propertyName));
}
}
}
Теперь добавим поле для хранения коллекции и свойство, через которое будут читать отфильтрованную и отсортированную коллекцию, кстати, не забываем про конструктор:
IEnumerable
_source;
public IEnumerable View
{get
{
return _source;
}
}
public
AlCollectionViewSource(IEnumerable
p_source)
{_source = p_source;
OnPropertyChanged("View");
}
Ок, теперь, если мы передадим в класс коллекцию, мы сможем ее получить, но нам нужен немного больший функционал. Давайте добавим фильтрацию. Для этого, достаточно добавить свойство и немного поправить свойство View:
private Func<bool> _filter
= null;
public Func<bool> Filter
{get
{
return _filter;
}
set
{
if (_filter != value)
{
_filter = value;
OnPropertyChanged("View");
}
}
}
public IEnumerable View
{get
{
IEnumerable
Func<
if (filter != null)
{
result = result.Where(item => Filter(item));
}
return result;
}
}
Ок, осталось добавить сортировку. Аналогично, нам понадобится свойство и правка свойства View. В этот раз она будет чуть посложнее. Если у нас есть имя свойства по которому сортировать, то мы извлекаем его значение через Reflection и сортируем по нему, если вместо имени поля у нас пустая строка, то сортируем по самим объектам, ну а если null, то возвращаем в том же порядке, в каком значения были в исходной коллекции.
private string _orderPropertyName = null;
public string
OrderPropertyName
{get
{
return _orderPropertyName;
}
set
{
if (_orderPropertyName != value)
{
_orderPropertyName = value;
OnPropertyChanged("View");
}
}
}
public IEnumerable View
{get
{
IEnumerable
Func<
if (filter != null)
{
result = result.Where(item => filter(item));
}
if (!string.IsNullOrWhiteSpace(OrderPropertyName))
{
result = result.OrderBy(
item =>
{
Type type = item.GetType();
PropertyInfo property = type.GetTypeInfo().GetDeclaredProperty(OrderPropertyName);
return property.GetValue(item);
});
}
else
{
if (OrderPropertyName == "")
{
result = result.OrderBy(item => item);
}
}
return result;
}
}
Ну и давайте небольшой пример, в стиле MVVM.
Для примера, введем вот такой вспомогательный класс:
public class Person
{public string LastName { get; set; }
public static List<Person>
GetPersonCollection()
{List<Person> result = new List<Person>();
result.Add(new Person { LastName = "Сидоров" });
result.Add(new Person { LastName = "Федоров" });
result.Add(new Person { LastName = "Иванов" });
result.Add(new Person { LastName = "Петров" });
return result;
}
}
Ну и ViewModel:
public class MyViewModel
{public ICommand SearchCommand { get; private set; }
public ICommand OrderCommand { get; private set; }
public AlCollectionViewSource<Person>
Items { get; private set; }
public string
FilterString { get; set; }
public
MyViewModel()
{Items = new AlCollectionViewSource<Person>(Person.GetPersonCollection());
SearchCommand = new MyCommand(() => Items.Filter = item => item.LastName.ToLower().Contains(FilterString.ToLower()));
OrderCommand = new MyCommand(() => Items.OrderPropertyName = "LastName");
}
}
Разметка окна:
<Grid Background="{StaticResource
ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions><RowDefinition Height="auto" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Path=FilterString,Mode=TwoWay}" Width="150" Margin="5" />
<Button Content="Фильтр!" Command="{Binding SearchCommand}" />
<Button Content="Сортировка!" Command="{Binding OrderCommand}" />
</StackPanel>
<ListView x:Name="lvMain" Grid.Row="1" DataContext="{Binding Items}" ItemsSource="{Binding View}" DisplayMemberPath="LastName" />
</Grid>
Ну и картинки, как это работает. Так приложение выглядит после запуска:
Так, после набора в TextBox-е строки "ро" и нажатия на "Фильтр!":
Ну и после нажатия на кнопку "Сортировка!":
Вот как то так.
Алексей. А готовый проект для изучения можете сбросить?
ОтветитьУдалитьКто лентяй и не хочет сам, с нуля написать? А?
УдалитьВот, если что, ссылка:
https://skydrive.live.com/redir?resid=9F0ADE63C4A0A8B9!197&authkey=!AB2SRQeflwfZ1oY
Просто запутался. Где какой класс, какой код к основному окну, какой к вспомогательным классам. Неделю уже перелопачиваю всякие варианты. Модель MVVM не знаю поэтому и трудности.
УдалитьСпасибо за ссылку :)
Удалить