
- •В ведение в ado.Net
- •Потребитель данных
- •Поставщики данных
- •Источник данных — xml-файл
- •Элементы управления на форме Меню
- •Как устроены планки меню, статуса и панелей инструментов
- •Планка инструментов (ToolStrip)
- •Навигатор связей
- •Контейнер, расщепляющий форму
- •Создание таблиц и внедрение их в DataSet
- •Пояснения
- •Запись и чтение данных
- •Отображение данных связанной таблицы
- •Добавляем ограничение
- •Вторая, подчиненная таблица
- •Автоматическая навигация по записям связанной таблицы
- •Элемент управления BindingNavigator
- •Реакции на события в DataTable
- •Канонизация имен студентов
- •Вычисляемые колонки DataTable (Expression-Based DataColumn Objects)
- •Поиск в таблице DataTable и в компоненте DataGridView
- •Коррекция пользовательского интерфейса
- •Результат поиска
- •Поиск в DataGridView
- •Отбор данных из DataTable и отображение их в ListView
- •Как легко вносятся ошибки
- •Образ таблицы DataView
- •Методы Find и FindRows класса DataView
- •Задание
- •Элемент управления, который позволяет управлять отображением колонок DataGridView
Элемент управления BindingNavigator
BindingNavigator предоставляет стандартные способы управления UI (интерфейсом пользователя) тех элементов формы, которые привязаны к источникам данных (BindingSource). Так как навигатор связывания является довольно сложным компонентом, использующим ресурсы, то вводить его в форму лучше с помощью дизайнера студии. Мы это уже сделали.
Обратите внимание на массу ресурсов (картинок), которые студия автоматически добавила в проект при добавлении навигатора (см. файл FormMain.resx). Всю эту работу можно (в качестве полезного упражнения) сделать и вручную, но она потребует от вас заметных усилий. Технология работы с ресурсами — это особая статья трудозатрат программиста, она требует большого внимания и осторожности, так как легко подпортить уже работающий проект.
Важно помнить, что ресурсы бывают двух типов: внедряемые в файл ресурсов (Embedded in .resx) или подключаемые при компиляции (Linked at compile time). Внедряемые ресурсы в конце концов попадают в exe-файл, их нельзя редактировать и поэтому их лучше использовать в случаях, кода вы не хотите, чтобы кто-либо, кроме вас, мог изменить облик приложения. Подключаемые ресурсы хранятся в виде отдельных файлов проекта (например в формате jpg), их легко заменить в уже готовом проекте и, тем самым, изменить облик приложения.
Итак, навигатор связывания BindingNavigator готов к работе и ждет, когда его подключат. Сделайте это одной строкой кода, местоположение которой вычислите путем анализа логики использования навигатора.
bn.BindingSource = bs[0];
Проверьте работу приложения и убедитесь, что механизм DataBinding реализует желаемую синхроизацию таблиц, то есть, выборочное отображение экзаменов.
Реакции на события в DataTable
Таблица экзаменов является дочерней (подчиненной) по отношению к таблице студентов и связана с ней по ключевым полям (ID и StudID). Такой тип связи часто называют PK-FK (Primary-Key-Forign Key). При вводе новой записи в родительскую таблицу подчиненная таблица ведет себя инертно — в ней не появляются новые строки со списком экзаменов нового студента. Вам придется вручную ввести несколько строк в таблицу экзаменов. Эта рутинная работа будет сильно утомлять не только вас, но и пользователей вашего приложения. В некоторых сценариях работы с данными хочется это исправить и автоматизировать процесс появления строк в подчиненной таблице.
Очевидно, что добавление экзаменов надо делать в ответ на событие, которое соответствует появлению нового студента. В документации по классам библиотеки .NET Framework находим, что класс DataTable способен обработать два события (RowChanging и RowChanged), которые прямо связаны с нашим намерением. Первое происходит в момент изменения строки таблицы, а второе — после того, как изменения в строке успешно завершились. Выбираем второе и вводим реакцию на него в класс формы. Это делается стандартным способом — добавлением делегата в список делегатов события RowChanged. Введите следующий код в начало метода RelateAndBind.
//=== Добавляем адрес функции обработки события в коллекцию делегатов, поддерживаемую событием RowChanged
ds.Tables[0].RowChanged +=
Если вы правильно манипулировали механизмом IntelliSense при вводе предыдущего оператора (вводе, а не копировании текста отсюда), то заготовка метода, реагирующего на событие добавления новой записи, была автоматически создана редактором студии. Если заготовки нет, то введите ее руками, или научитесь работать с IntelliSense и повторите ввод.
Просмотрите код заготовки. Я изменил автоматически сгенерированное имя функции на OnStudsRowChanged. Теперь оно больше соответствует своему назначению. Второй параметр функции (задания делегата) передает ссылку на объект вспомогательного класса DataRowChangeEventArgs, который содержит важную информацию о конкретном типе изменения. Для того, чтобы узнать какие изменения в принципе отслеживаются, смотрите справку по перечислению DataRowAction. Мы используем эту информацию для фильтрации только одного типа изменений (Add).
void OnStudsRowChanged(object sender, DataRowChangeEventArgs e)
{
DataTable exams = ds.Tables[1]; // Ссылка на подчиненную таблицу экзаменов
if (e.Action == DataRowAction.Add) // Если в первой таблице появилась новая строка
{
int num = 0;
foreach (string c in courses) // Массив с именами предметов должен существовать
{
if (num++ != rand.Next(courses.Length))
ds.Tables[1].Rows.Add(CreateRandomExam((int)e.Row["ID"], c));
}
}
}
В нашей версии предполагается, что названия курсов лекций фиксированы и хранятся в массиве текстовых строк courses. Если пользователь вставил в таблицу студентов новую запись (строку), то во второй таблице появляются несколько строк. Для связывания их с родительской строкой первой таблицы (связь типа PK-FK) необходимо правильно установить значение столбца StudID, которое соответствует ключевому полю ID первой таблицы (см. код присвоения e.Row["ID"]).
Попытайтесь отладить приложение и убедиться, что в начальный момент для каждой новой записи, вносимой в первую таблицу, во второй таблице (экзаменов) появляются несколько, связанных с ней, записей.
Это действительно так, если строки добавляются в новую, пустую таблицу. Однако, обработчик события дает сбой при попытке вставить новые строки в старую таблицу, то есть ту, которая восстанавливается из файла. В чем же дело? Попробуйте самостоятельно вычислить причину такого поведения и способ его коррекции.
Вы, вероятно, догадались, что при добавлении строк, которое происходит при чтении данных из файла, возбуждается событие RowChanged и мы автоматически добавляем лишнее множество экзаменов к тем, которые уже есть в файле. Вот один из способов решения этой проблемы. Перед восстановлением всего набора данных из файла, надо выключить реакцию на событие RowChanged и вновь включить ее после прочтения данных из файла. Для этого надо пользоваться операциями –= и += события RowChanged нужной таблицы.