На MSDN’овском форуме задали вопрос о сохранении порядка сортировки выборки при использовании Linq. Задача достаточно типовая, поэтому решил написать небольшой мануал, как это сделать используя Dynamic Linq. Для упрощения пусть данные отображается у нас в DataGrid’e и сортируются нажатием мышкой на заголовок столбцов.
Dynamic Linq
Библиотека Dynamic Linq входит в набор примеров кода для Visual Studio 2008 (C#, VB). После выкачивания можно найти исходный код, документацию и пример использования в папке \LinqSamples\DynamicQuery.
Проект с примером использования отлично собирается как под .Net Framework 3.5, так и под 4.0. Сам код библиотеки вынесен в namespace System.Linq.Dynamic и находится в файле Dynamic.cs. Для использования можно скопировать код к себе в проект или создать отдельный проект с библиотекой.
Использование
Итак у нас есть форма, на ней DataGrid с гордым именем dataGrid1.
Простой класс:
class Person { public string FirstName { get; set; } public string LastName { get; set; } public string NickName { get; set; } public DateTime BirthDate { get; set; } }
При инициализации формы подсовываем Grid’у в качестве источника данных массив Person:
var persons = new Person[] { new Person{FirstName = "Сидр", LastName = "Сидоров", NickName = "sid", BirthDate = DateTime.Today.AddYears(-22)}, new Person{FirstName = "Петр", LastName = "Петров", NickName = "pit", BirthDate = DateTime.Today.AddYears(-21)}, new Person{FirstName = "Иван", LastName = "Иванов", NickName = "ian", BirthDate = DateTime.Today.AddYears(-20)}, }; this.dataGrid1.ItemsSource = persons;
Данные отображаются и сортируются нажатием на заголовки колонок, но порядок сортировки не сохраняется между сеансами.
Итак, приступим.
Первое, что нам нужно сделать, это перенести код библиотеки Dynamic Linq к себе в проект. Это можно сделать двумя способами:
- Создать класс и скопировать код из полученного ранее проекта;
- Сразу добавить файл в себе в проект (в Solution Explorer’e щелкнуть право кнопкой на проект, и выбрать Add->Existing Item).
У DataGrid’а добавляем в обработчик события Sorting код, который будет сохранять поле и порядок сортировки в конфигурационном файле
private void dataGrid1_Sorting(object sender, DataGridSortingEventArgs e) { IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForAssembly(); using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream("LastSort.txt", FileMode.Create, store)) { using (StreamWriter writer = new StreamWriter(stream)) { writer.WriteLine(String.Format("{0} {1}", e.Column.SortMemberPath, (e.Column.SortDirection != ListSortDirection.Ascending) ? ListSortDirection.Ascending : ListSortDirection.Descending)); } } }
Небольшое пояснение по поводу IsolatedStorage
IsolatedStorage – это механизм позволяющий сохранять данные в “виртуальные” папки в зависимости от выбранной области ограничения видимости. Использованная функция GetUserStoreForAssembly() возвращает нам “путь” который уникален для пользователя и Assembly, т.е. другие приложения и пользователи не будут иметь доступ к файлу LastSort.txt в котором хранятся данные об используемом порядке сортировки.
Небольшое пояснение по поводу (e.Column.SortDirection != ListSortDirection.Ascending) ? ListSortDirection.Ascending : ListSortDirection.Descending
Дело в том, что Column.SortDirection является Nullable, то есть кроме ListSortDirection.Ascending и ListSortDirection.Descending принимает еще и значение NULL, которое и является значением по умолчанию. Помимо этого в DataGridSortingEventArgs e содержится предыдущее состояние сортировки, поэтому приходится использовать такую конструкцию.
Если вас сильно беспокоит, что порядок записывается в изолированное хранилище каждый раз, когда происходит сортировка, то можно не использовать предложенную функцию а сохранять его при закрытии формы.
private void Window_Closing(object sender, CancelEventArgs e) { foreach (var c in this.dataGrid1.Columns) { if (c.SortDirection.HasValue) { IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForAssembly(); using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream("LastSort.txt", FileMode.Create, store)) { using (StreamWriter writer = new StreamWriter(stream)) { writer.WriteLine(String.Format("{0} {1}", c.SortMemberPath, c.SortDirection)); } } break; } } }
Пишем функцию которая выдергивает значение из файла в изолированном хранилище
private string GetLastSort() { IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForAssembly(); using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream("LastSort.txt", FileMode.OpenOrCreate, store)) { using (StreamReader reader = new StreamReader(stream)) { return reader.ReadLine() ?? "NickName Descending"; } } }
После этого меняем подключение данных для DataGrid’а на
this.dataGrid1.ItemsSource = persons.AsQueryable<Person>().OrderBy(GetLastSort()).Select("new (FirstName, LastName, NickName, BirthDate)");