Отбор данных из DataTable и отображение их в ListView — КиберПедия 

История развития хранилищ для нефти: Первые склады нефти появились в XVII веке. Они представляли собой землянные ямы-амбара глубиной 4…5 м...

Механическое удерживание земляных масс: Механическое удерживание земляных масс на склоне обеспечивают контрфорсными сооружениями различных конструкций...

Отбор данных из DataTable и отображение их в ListView

2022-09-11 34
Отбор данных из DataTable и отображение их в ListView 0.00 из 5.00 0 оценок
Заказать работу

Рассмотрим, как реализовать поиск в таблице DataTable (то есть, в памяти) и отобразить все данные, удовлетворяющие критерию поиска. Отобранное множество данных таблицы, возвращаемое методом Select (это — массив объектов DataRow), преобразуем в текстовые строки и заполним ими список (стандартный элемент управления ListView). Список ListView поместим в отдельную, временную форму, которая должна появиться рядом с главным окном.

Сейчас мы собираемся показать результат поиска в элементе ListView, а позже мы добавим еще один тип поиска: фильтрация данных с помощью объекта DataView. Результат поиска при этом будет отображен в DataGridView путем привязки к образу таблицы DataView.

· В классе формы мы уже имеем объявление переменной Form formFound;

· Добавьте метод, который создает эту вспомогательную форму, вычисляет ее координаты, вставляет в коллекцию дочерних элементов формы список с результатами поиска и отображает форму на экране.

void CreateFormFound(Control control, string text)

{

     // Здесь ваш код. Создайте метку (Label msg) с текстом "Ищу то-то в такой-то таблице"

 

formFound = new Form

{

Text = "Search results",

MinimizeBox = false,

MaximizeBox = false,

Owner = this,

Width = Math.Max(msg.Width, control.Width) + 24,

Height = msg.Height + control.Height + 50,

Visible = true,

Location = new Point(Location.X + Width, Location.Y)

};

control.Location = new Point(3, msg.Height + 5);

formFound.Controls.Add(msg);

formFound.Controls.Add(control);

}

Сейчас мы разработаем алгоритм поиска. Если вы уже реализовали поиск в DataGridView, то добейтесь того, чтобы новый алгоритм не мешал старому (он должен функционировать, как и прежде). Окно вспомогательной формы formFound должно создаваться и обновляться при каждом нажатии на кнопку btnFindList.

· Проверьте, что код инициализации переменных: grids, searchTableID и searchColumnID уже существует.

· Добавьте код, который изменяет значениее searchTableID в реакции listTable на событие SelectedIndexChanged. Кроме того в этот момент надо заново заполнить список listColumn.

· Добавьте код, который изменяет значение searchColumnID в реакции listColumn на событие SelectedIndexChanged. Последнее действие можно выполнить с помощью лямбда выражения.

listColumn.SelectedIndexChanged += (s, a) => searchColumnID = listColumn.SelectedIndex;

· Добавьте код в заглушку метода GetSearchCriteria.

string GetSearchCriteria()

{

string criteria = null;

try

{

findWhat = Trim(comboFind.Text);

 

Type type = ds.Tables[searchTableID].Columns[searchColumnID].DataType;

switch (type.Name)

{

    case "String": criteria = " LIKE '" + findWhat + "*'"; break;

    case "Decimal":

    case "Byte":

    // Здесь ваш код. Добавьте ветви для всех числовых типов данных

    case "Boolean":

    case "Int32": criteria = "=" + findWhat; break;

    case "DateTime":

      DateTime dt;

      bool ok = DateTime.TryParse(findWhat, out dt);

      string

        low = dt.ToShortDateString(),

        hi = dt.AddYears(2).ToShortDateString();

      if (ok)

        criteria = " > '" + low + "' AND " + listColumn.Text + " < '" + hi + "'";

      else

        throw new Exception("Could not parse date string");

      break;

}

return criteria;

}

catch (Exception ex) { new FormMsg(ex.Message, 3000); }

return null;

}

Метод GetSearchCriteria должен корректировать синтаксис SQL-запроса, в зависимости от типа данных активной колонки таблицы. Дело в том, что при выполнении запроса (метода Select) шаблон вида LIKE работает только для текстовых колонок таблицы. Для остальных типов колонок приходится искать точное совпадение данных. Колонка даты экзамена также вносит специфику в формат строки запроса. Так как синтаксис SQL-запроса для числовых и текстовых колонок отличается, то нам приходится производить ветвление алгоритма в зависимости от типа данных в колонке, определяемой индексом searchColumnID для таблицы, определяемой индексом searchTableID. Проверяемое множество типов данных превышает необходимый нам диапазон, но рассматривайте его, как запас прочности. Ведь в будущем мы можем изменить схему таблиц.

· Добавьте код в заглушку метода ShowList. Здесь мы производим отбор строк выбранной таблицы путем вызова уже знакомого вам метода Select класса DataTable. Напомним, что он осуществляет поиск в памяти.

void ShowList(string criteria)

{

DataRow[] rows = null;

try

{

string filter = listColumn.Text + criteria;

rows = // Отфильтруйте строки нужной таблицы с помощью метода Select

if (rows.Length!= 0)

{

    // Здесь ваш код. Запомните искомый текст в выпадающем списке comboFind

    CreateFormFound(CreateList(rows), criteria);

    return;

}

}

catch (Exception ex) { new FormMsg(ex.Message, 2000); }

SetErrorMsg(criteria, listColumn.Text);

}

Критерий фильтрации, подаваемый на вход метода Select, зависит от активной колонки активной таблицы, поэтому мы вычисляем его таким образом:

string filter = <Имя колонки> + criteria;

· Позвольте Intellisense создать заглушку для метода CreateList и добавьте в нее следующий код.

Control CreateList(DataRow[] rows)

{

ListView list = new ListView

{

View = View.Details,

FullRowSelect = true,

GridLines = true,

Width = 280,

Height = this.Height - 74

};

DataColumnCollection cols = rows[0].Table.Columns;

list.Columns.Add(cols[0].ColumnName, 30);

list.Columns.Add(cols[1].ColumnName, 100);

list.Columns.Add(cols[2].ColumnName, 100);

 

foreach (DataRow row in rows)

list.Items.Add(new ListViewItem(new string[] {

    row[0].ToString(), row[1].ToString(), row[2].ToString()

}));

 

list.SelectedIndexChanged += listFound_SelectedIndexChanged;

return list;

}

В коде метода CreateList мы помещаем результат поиска, который пришел в массиве объектов DataRow, в список, а точнее в элемент управления вида ListView. Для этой цели можно было бы использовать другой элемент — DataGridView, но, когда нет необходимости редактировать данные, то ListView оказывается вполне приемлемым, и вам надо приобрести опыт его использования.

На следующем рисунке вы видите результаты поиска значений 4 в вычисляемой колонке среднего балла. Повторное нажатие кнопки b tn Find List вызывает повторное создание (а, следовательно, и перерисовку) формы formFind. Подумайте, как это исправить. При попытке найти студентов со средним баллом 3,83 мы, скорее всего, получим отказ, даже если вы видите (в DataGridView), что такие студенты существуют. Причина в том, что поиск осуществляется по точному совпадению, а DataGridView настроен на отображение величин, округленных до двух знаков после запятой. Подумайте, как это исправить, то есть как заставить метод Select класса DataTable учитывать приближенное совпадение.

 

Колонка Date таблицы экзаменов также преподносит сюрпризы при выполнении метода Show List. Просмотрите значения этих полей в XML-файле. Частью даты (объекта DataTime) является время: 2006-01-20T00:00:00+03:00. Символы +03:00, как вы догадались, являются региональным сдвигом.

При выполнении метода Select (то есть, SQL-запроса по дате) наличие времени мешает. Причина в том, что мы вынуждены искать точное совпадение, так как шаблон критерия отбора вида LIKE работает только для текстовых колонок таблицы. Нам, безусловно, не хочется вводить дату в таком сложном формате, да еще с учетом регионального времени. Если в XML-файле вручную убрать время (оставив только дату) и заново прочесть файл, то алгоритм фильтрации по дате работает правильно. Но при следующей записи в файл к данным вновь добавится время. Вместо метода ToShortDateString, который я (в целях экономии) использовал при формировании критерия отбора, надо просто использовать метод ToString.

Пока не понятно, как вообще избавиться от времени, которое, в нашем случае является лишним. Сложное лекарство было упомянуто ранее — при записи в файл не пользоваться методом WriteXml, а работать методами класса XmlDocument, на ходу управляя форматом даты.

Задание

· Добавьте возможность поиска студентов по среднему баллу, значение которого попадает в интервал [min, max].

· В данный момент команды поиска отдаются с помощью кнопок на панели инструментов. Реакцию на выбор одноименных команд меню, а также отслеживание их состояний, вы без особого труда реализуете самостоятельно. Если сочтете нужным, задействуйте и другие команды меню.

· Добавьте возможность выбора строки в списке ListView и синхронизации (отслеживания) позиции в активном DataGridView. Это не так просто, потому, что ListView способен отображать данные обеих таблиц. При переходе к экзамену другого студента необходимо синхронизировать gridStud (прокрутить строки, выбрать строку и выделить ячейку). Если вы сделаете это правильно, то gridExam должен перегрузиться (отследить позицию) автоматически.

Детали

Полезно добавить возможность автоматического открытия файла последнего документа. Это должно происходить при начальном запуске приложения. Для этой цели в момент записи документа в файл мы создаем или открываем ключ Windows-реестра и запоминаем его файловый путь. Другой (более современный) способ — использовать global::Properties. Settings. Рассмотрим способ с использованием реестра.

· В момент записи в файл производим запись в реестр:

RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\MySoft\StudentsSet");

key.OpenSubKey("StudentsSet", true);

key.SetValue("Last File", fn);

· В момент загрузки формы (в реакции на событие Load), мы обращаемся к реестру, узнаем путь к последнему файлу и открываем его.

void DoLoad()

{

SetDesktopLocation(20, 20);

InitDataSet();

 

RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\MySoft\StudentsSet");

if (key!= null && (fileName = (string)key.GetValue("Last File"))!= null && File.Exists(fileName))

Open(fileName);

else

InitTables();

FillSearchBox();

 

RelateAndBind();

//...

}

· Другим подходом (вместо использования Windows-реестра) считается использование XML-файла настроек Settings.settings. Такой файл уже есть в вашем проекте, но он пока не задействован. Его можно задействовать, добавив настройку типа string (например, с именем LastFile) и код вида:

fileName = Settings.Default.LastFile;

if (fileName!= null)

Open(fileName);

else

InitTables();

В реакции на событие FormClosing можно добавить сохранение настройки:

Settings.Default.Save();

Просмотрите настройки в студии (раскройте узел Properties в окне Solution Explorer и откройте файл Settings.settings). Неприятность состоит в том, что так можно работать с настройками только уровня User (колонка Scope). Вам не удастся сохранить настройку уровня Application. Настройки уровня User хранятся в специальной папке с адресом:

C:\Users\Ваше имя\AppData\Local\Ваше приложение\...

Я очень не люблю все специальные папки, поэтому не пользуюсь настройками типа User.

Как легко вносятся ошибки

Изменение в логике начальной загрузки формы (и данных) кажется очевидным, но такого рода изменения часто приводят к трудно обнаружимым ошибкам. Интересно, заметили ли вы ошибку, которую мы только что внесли?

· Просмотрите код метода Open. В нем есть пара операторов, необходимость которых мы показали ранее.

ds.Tables[0].RowChanged -= OnStudsRowChanged;

     // Чтение DataSet

ds.Tables[0].RowChanged += OnStudsRowChanged;

· Просмотрите код метода RelateAndBind, который вызывается после вызова Open. В нем есть оператор:

ds.Tables[0].RowChanged += OnStudsRowChanged;

Вопрос: сколько заданий в списке InvocationList делегата с сигнатурой RowChanged? Ответ: два. Во избежание подобных ситуаций во многих статьях MSDN рекомендуют всегда добавлять делегатов таким образом.

ds.Tables[0].RowChanged -= OnStudsRowChanged;

ds.Tables[0].RowChanged += OnStudsRowChanged;

Первая строка выглядит глупо, если вы впервые обращаетесь к списку заданий делегата события, но она предотвращает ошибку при повторном обращении. Удаление несуществующей ссылки не нарушает логики приложения, но спасает от повторных реакций на события. Именно так и предлагаю исправить нашу ошибку. В метод RelateAndBind добавьте строку, удаляющую задание делегата перед тем, как его добавить.

Образ таблицы DataView

Этот параграф содержит обзор свойств класса DataView. Вы можете его пропустить, если недостаточно времени. Объект класса DataView не имеет своих данных, он работает с данными ассоциированного с ним объекта DataTable. Методы класса DataView позволяют преобразовать табличные данные. Их можно отфильтровать, или отсортировать. Следующий фрагмент иллюстрирует сказанное.

DataView view = new DataView(ds.Tables[0]); // Создаем образ таблицы студентов

view.RowFilter = "Name LIKE 'C*'"; // Настраиваем фильтр отбора строк

view.Sort = "Addr DESC";       // Настраиваем режим сортировки

viewGrid.DataSource = view;      // Привязываем viewGrid к образу таблицы

Предполагается, что на форме есть еще один DataGridView с именем viewGrid. Последний оператор оправдывает необходимость существования класса DataView. Дело в том, что отфильтровать и отсортировать данные можно было бы с помощью метода Select класса DataTable. Для этой цели класс DataView не нужен. Но вот привязать элемент отображения (DataGridView, или ComboBox) к данным, возвращаемым методом Select, нам бы не удалось, так как привязка осуществляется с помощью свойств, а Select возвращает массив объектов DataRow[], каждый из которых можно представить в виде массива обезличенных объектов. Напомню, что класс DataRow имеет два индексатора для выбора полей данных (колонок). Вывод: DataView умеет делать все, что делает метод Select класса DataTable, но добавляет способность привязки к своим данным.

Вы, конечно знаете, что отфильтровать и отсортировать данные реальной таблицы базы данных (БД) можно другим способом — создать SQL-запрос и выполнить его относительно БД. Метод Select класса DataTable — слабое подобие этого подхода.

Класс DataView позволяет фильтровать данные не только по критерию отбора содержимого таблицы, но и по состоянию ее строк. Строки таблицы могут быть в разных состояниях. Следующая таблица поясняет сказанное.

DataViewRowState Описание Значение
None None 0
Unchanged Неизмененные (немодифицированные) строки 2
Added Вновь добавленные строки 4
Deleted Удаленные строки 8
ModifiedCurrent Текущие версии модифицированных строк 16
ModifiedOriginal Оригинальные версии модифицированных строк 22
CurrentRows Неудаленные строки (объединение флагов ModifiedCurrent, Added, Unchanged) 32
OriginalRows Оригинальнне версии удаленных, модифицированных и немодифицированных строк (объединение флагов ModifiedOriginal, Deleted, Unchanged) 42

Для того, чтобы задействовать фильтрацию на основе состояния, к предыдущим настройкам добавим еще одну.

view.RowStateFilter = DataViewRowState.Added;

После этого в DataView попадут только вновь добавленные строки. Благодаря предыдущим настройкам (view.RowFilter = "Name LIKE 'C*'"; и view.Sort = "Addr DESC"), они будут отсортированы по убыванию адресов и в них будут отобраны студенты, имена которых начинаются на букву C. Просмотреть результат фильтрации можно с помощью следующего кодового фрагмента.

for (int i = 0; i < view.Count; i++)

{

DataRowView rv = view[i];

Console.WriteLine(rv["Name"] + ", " + rv["Addr"]);

}

Здесь работает класс DataRowView, который инкапсулирует функциональность образа строки таблицы. Напомним, что в Windows-приложениях просмотр консоли (в окне Output) работает только в режиме отладки.

Все настройки объекта DataView можно задать при помощи одного из конструкторов и не обращаться к свойствам RowFilter, Sort и RowStateFilter, как было показано выше. Например.

DataView view = new DataView (ds.Tables[0], "Name LIKE 'C*'", "Addr DESC", DataViewRowState.Deleted)

{

AllowNew = false,

AllowEdit = false,

AllowDelete = false

};

viewGrid.DataSource = view;

Возможности DataView не превышают возможностей SQL-запросов, они значительно скромнее. Например, с помощью DataView нельзя объединять данные нескольких таблиц (JOIN queries), или исключать какие-то колонки. DataView фильтрует и сортирует полные строки таблицы, результат всегда содержит все колонки таблицы.


Поделиться с друзьями:

Своеобразие русской архитектуры: Основной материал – дерево – быстрота постройки, но недолговечность и необходимость деления...

Организация стока поверхностных вод: Наибольшее количество влаги на земном шаре испаряется с поверхности морей и океанов (88‰)...

Архитектура электронного правительства: Единая архитектура – это методологический подход при создании системы управления государства, который строится...

Археология об основании Рима: Новые раскопки проясняют и такой острый дискуссионный вопрос, как дата самого возникновения Рима...



© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!

0.065 с.