Все необходимые для выполнения данной работы программы можно найти в прилагаемом каталоге.
В этой работе мы последовательно познакомимся с технологией ADO.NET (.mdb. Наш выбор основан на том, что не требуется устанавливать
дополнительные программы, типа SQL Server, для такого рода баз. Пока
мы будем пользоваться готовой учебной базой данных Northwind.mdb,
расположенной в прилагаемом к работе каталоге Source. Позднее мы научимся
создавать свои базы данных либо с помощью СУБД Microsoft
Office Access, либо с помощью COM объектов в оболочке Visual
Studio 2008.
Создание базы данных начинается с ее проектирования с помощью диаграмм. Затем создается сам файл (или несколько взаимосвязанных файлов) БД и устанавливается структура БД, определяющая таблицы, поля и их типы, назначение ключей и индексов. Затем проектируется приложение, управляющее данными БД. Мы займемся разработкой приложения, решающего типовые задачи по редактированию и представлению данных БД.
Базы данных называют источниками данных. Microsoft ADO.NET - это часть библиотеки .NET Framework, позволяющая создавать приложения для управления данными в структурированных файловых или серверных хранилищах. Библиотечные средства ADO.NET обеспечивают подключение к источникам данных, выполнение команд SQL, обработку и выборку данных.
ADO.NET представлено набором программных средств, прежде всего классов,
которые находятся в библиотечной
сборке System.Data.dll. Применительно к формату OLE DB наиболее
важными из них являются специализированные классы пространства имен System.Data.OleDb:
OleDbConnection - обоспечивает подключение к источнику данныхOleDbDataAdapter - обеспечивает связь между источником данных и набором
данных в оперативной памятиOleDbCommand - объект команд SQL для работы в источнике данныхВ совокупности экземпляры этих классов в приложении называются поставщиком
данных (Data Provider). Далее идет независимый от формата БД слой для хранения
данных в кэше оперативной памяти и просмотра данных из пространства имен System.Data:
DataSet - набор кэшированных данных, выборочно загруженных из источникаDataView - позволяет создать несколько представлений одной и той
же таблицыНачнем с подготовительных действий и созданию связанного с данными списка, дающего возможность только просматривать данные БД.
Создайте новый проект
типа Windows Forms Application с именем FormsADO и решением ADO
В палитре компонентов Toolbox откройте вкладку дата и перетащите на форму компонент OleDbDataAdapter.
Если в настройках проекта такого компонента не окажется, то щелкните на
вкладке Data палитры Toolbox правой кнопкой мыши и подключите искомый
компонент командой Choose Items
В появившемся окне
мастера
настройки поставщика щелкните по кнопке New Connection и выберите
файл подключаемой БД Northwind.mdb из прилагаемого к работе каталога Source
После щелчка на кнопке Next оболочка выдаст сообщение с предложением скопировать
присоединенный файл БД в каталог проекта

Щелкните на кнопке
" Да " и перейдите к следующей вкладке мастера
Здесь в текстовом поле предлагается указать SQL -запрос, который поставщик
данных должен выполнять по умолчанию при заполнении набора данных DataSet.
Мы хотим прочитать содержимое двух столбцов CustomerID и CompanyName из
таблицы Customers,
поэтому либо руками, либо с помощью мастера нужно в многострочном текстовом
поле интерфейса мастера заполнить команду:
Щелкните на кнопке Query Builder, в диалоговом окне Add Table выберите
из списка таблицу Customers и по щелчку на кнопке Add вкладки Tables (потом на Close ) отметьте в интерфейсе флажки против нужных полей
таблицы, чтобы мастер написал для них текст запроса
Щелкните на кнопке OK окна Query Builder и текст запроса будет помещен в
поле мастера
По умолчанию мастер готов создать операторы SQL не только для выборки, но и для вставки, обновления и удаления данных.
Нам на данном этапе
потребуется только чтение данных и другие возможности нужно отключить щелчком
на кнопке Advanced Options,
сбросив ненужные флажки
Дойдите до последней вкладки
мастера настройки поставщика и убедитесь, что включен только режим Select (но
не Update, Insert, Delete)
Щелкните на кнопке Finish, чтобы завершить создание и настройку объектов поставщика
данных (экземпляров OleDbDataAdapter и OleDbConnection ) и сохранить параметры в установках проекта
В панели Solution
Explorer откройте файл Form1.Designer.cs и убедитесь, что мастер создал необходимый
код с настройками поставщика, в состав которого входят экземпляры классов OleDbCommand, OleDbDataAdapter и OleDbConnectionОписанную процедуру можно в любом месте прервать щелчком на кнопке Cancel мастера
настроек и вернуться к ней в любое время. Для этого достаточно щелкнуть в нижней
части рабочей области на экземпляре класса OleDbDataAdapter правой
кнопкой мыши и выполнитиь команду Configure Data Adapter (или выделить
объект поставщика и выполнить ту же команду через пункт Data главного меню
оболочки)

Выделите в проекте
файл БД и через панель Properties установите его свойство Copy
to Output Directory в значение Copy if newer, чтобы он находился
вместе с исполняемой сборкой в одном каталоге bin проектаИтак, у нас есть объект соединения с БД и объект-посредник между базой и
набором данных (кэшем оперативной памяти), который должен выполнять команды SQL и
управлять потоками данных. Теперь нужно создать сам набор данных как экземпляр
класса DataSet.
Щелкните правой кнопкой
мыши на экземпляре oleDbDataAdapter1 в нижней части рабочей области
и выполните команду Generate DataSet (то же самое можно выполнить
через меню Data оболочки)
В появившемся окне
мастера Generate DataSet щелкните на кнопке OK
Будет создан объект набора данных dataSet11, который будет загружать два столбца
таблицы Customers согласно SQL-запроса, указанного в настройках поставщика oleDbDataAdapter1.
В качестве элемента, отображающего набор данных объекта dataSet11,
применим список ListBox.
Поместите на форму
из вкладки Common Controls палитры компонентов экземпляр компонента ListBox и настройте его в соответствии с таблицей| Свойство | Значение |
|---|---|
( Name ) |
listBox |
DataSource |
dataSet11 |
DisplayMember |
Customers.CompanyName |
Dock |
Fill |
Хотя набор данных dataSet11 правильно связан со свойствами элемента
управления ListBox, но если выполнить приложение, то список останется пустым,
потому, что набор данных не был заполнен данными. Код заполнения поместим в
обработчик события Load формы.
В панели Properties выделите форму из раскрывающегося списка (или щелкните на заголовке формы)
и в режиме Events создайте обработчик события Load, который заполните такusing System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace FormsADO
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// Заполняем набор данных настроенным адаптером данных
oleDbDataAdapter1.Fill(dataSet11);
// Устанавливаем курсор списка в конец
listBox.SelectedIndex = listBox.Items.Count - 1;
}
}
}
Запустите приложение
и вы увидите данные поля Customers.CompanyName, связанного со свойством DisplayMember списка ListBox,
подключенного к набору данных dataSet11 через свойство DataSource
Метод Fill() поставщика данных загружает все данные указанных столбцов
таблицы в набор данных. Часто не все данные нужны пользователю, к тому же их
может быть очень много, что перегружает ресурсы компьютера. В этом разделе
будет показано, как создать параметризованный оператор SQL для заполнения набора
данных не всеми строками таблицы БД, а пока только одной. Мы
продолжим развитие нашего проекта, который ограничивает выборку данных из источника
определенным условием, а заодно и установим строку подключения к БД вручную.
Если выделить объект строки подключения и посмотреть свойство ConnectionString,
то можно убедиться, что оно имеет следующее строковое значение:
Provider=Microsoft.Jet.OLEDB.4.0; Data Source=|DataDirectory|\Northwind.mdb;Persist Security Info=True
Переопределим его программно на другое значение.
Вставьте в
класс Form1 следующий кодusing System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace FormsADO
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
String ConnectionString(String fileNameNotExt)
{
string JetEngineString = @"Provider=Microsoft.Jet.OLEDB.4.0;Jet OLEDB:Engine Type=5;Data Source=";
string pathToFile = Application.StartupPath.ToString() + "\\Data\\";
return JetEngineString + pathToFile + fileNameNotExt.Trim() + ".mdb";
}
private void Form1_Load(object sender, EventArgs e)
{
// Задаем строку соединения
oleDbConnection1.ConnectionString = ConnectionString("Northwind");
// Заполняем набор данных настроенным адаптером данных
oleDbDataAdapter1.Fill(dataSet11);
// Устанавливаем курсор списка в конец
listBox.SelectedIndex = listBox.Items.Count - 1;
}
}
}
В панели Solution
Explorer для узла проекта FormsADO вызовите контекстное
меню, командой Add/New
Folder добавьте каталог Data и курсором перенесите в него БД Northwind.mdb
Выделите файл
БД и через панель Properties убедитесь, что его свойство Copy to Output
Directory установлено в значение Copy if newer
Запустите приложение
- программа FormsADO.exe работает нормальноТеперь установим фильтр на заполнение данными набора данных DataSet через
команду SQL -запроса. Если открыть файл Form1.Designer.cs, то
мы увидим примерно следующий код в методе InitializeComponent() формы
private void InitializeComponent()
{
this.oleDbSelectCommand1 = new System.Data.OleDb.OleDbCommand();
this.oleDbConnection1 = new System.Data.OleDb.OleDbConnection();
this.oleDbDataAdapter1 = new System.Data.OleDb.OleDbDataAdapter();
this.dataSet11 = new FormsADO.DataSet1();
this.listBox = new System.Windows.Forms.ListBox();
((System.ComponentModel.ISupportInitialize)(this.dataSet11)).BeginInit();
this.SuspendLayout();
//
// oleDbSelectCommand1
//
this.oleDbSelectCommand1.CommandText = "SELECT CustomerID, CompanyName\r\nFROM Customers";
this.oleDbSelectCommand1.Connection = this.oleDbConnection1;
//
// oleDbConnection1
//
this.oleDbConnection1.ConnectionString =
"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|\\Northwind.mdb;Persis" +
"t Security Info=True";
//
// oleDbDataAdapter1
//
this.oleDbDataAdapter1.SelectCommand = this.oleDbSelectCommand1;
this.oleDbDataAdapter1.TableMappings.AddRange(new System.Data.Common.DataTableMapping[] {
new System.Data.Common.DataTableMapping("Table", "Customers", new System.Data.Common.DataColumnMapping[] {
new System.Data.Common.DataColumnMapping("CustomerID", "CustomerID"),
new System.Data.Common.DataColumnMapping("CompanyName", "CompanyName")})});
...........................................
}
...............................................
private System.Data.OleDb.OleDbCommand oleDbSelectCommand1;
Обратите внимание на выделенные строки в приведенном листинге. Объявляется
поле-ссылка на объект команд oleDbSelectCommand1,
затем создается сам объект, его свойству CommandText присваивается SQL-запрос
и, наконец, объект команд подключается к одноименному свойству объекта адаптера
данных. Теперь метод Fill() адаптера данных будет выбирать из БД нужные
данные и передавать их набору данных DataSet. Если в коде события Load формы
поменять команду в свойстве CommandText, то она и будет выполняться адаптером
данных.
Таким образом, изменим для адаптера команду выборки данных из БД и добавим к форме необходимый для этого пользовательский интерфейс.
Добавьте в обработчик
события Load формы код перенастройки объекта oleDbSelectCommand1private void Form1_Load(object sender, EventArgs e)
{
// Настраиваем объект команд
oleDbSelectCommand1.CommandText = "SELECT CustomerID, CompanyName FROM Customers\r\n"
+ "WHERE (CompanyName LIKE ? + '%')";
oleDbSelectCommand1.Parameters.Add(new System.Data.OleDb.OleDbParameter());
oleDbSelectCommand1.Parameters[0].Value = "A";
// Задаем строку соединения
oleDbConnection1.ConnectionString = ConnectionString("Northwind");
// Заполняем набор данных настроенным адаптером данных
oleDbDataAdapter1.Fill(dataSet11);
// Устанавливаем курсор списка в конец
listBox.SelectedIndex = listBox.Items.Count - 1;
}
В данном коде пояснения требует только SQL-команда, написанная на диалекте SQL для формата файловых баз данных, принятого в Microsoft Office Access 2003
SELECT CustomerID, CompanyName FROM Customers\r\n"
+ "WHERE (CompanyName LIKE ? + '%')
\r\n - управляющие символы "возврат каретки - перевод строки"WHERE условие - условие отбора данных из источникаLIKE шаблон - проверка на соответствие заданному шаблону,
в котором могут использоваться специальные символы, означающие подстановки
значений параметров команды. В качестве шаблона поиска могут быть использованы
два подстановочных знака% - любое количество символов в текущей позицииВ нашем случае выражение после ключевого слова LIKE означает в своей
совокупности, что поиск данных в источнике будет проведен по любому значению,
которое введет пользователь в текстовое поле формы. Следуеть помнить, что если
в команде присутствует LIKE шаблон, то даже если для поля
поиска существует индекс, то он не работает, а проверка условия выполняется
последовательным перебором всех записей таблицы от начала до конца. Это очень
замедляет выборку данных на таблицах с большим количеством записей.
Измените
свойство Dock экземпляра класса ListBox с Fill на Bottom и
освободите место сверху клиентской области формы
Поместите
на форму, как ее дочерние элементы, экземпляры компонентов Label, TextBox, Button в
соответствии со снимком и настройте их как показано в таблице свойств ниже
| Тип | Свойство | Значение |
|---|---|---|
ListBox |
Dock |
Bottom |
Label |
Text |
Customer |
TextBox |
(Name) |
txtCustLimit |
Text |
A (латинское) |
|
Button |
(Name) |
btnLoadList |
Text |
Load |
Создайте обработчик
события Click кнопки btnLoadList и заполните его кодом переназначения
содержимого параметра SQL-запросаprivate void btnLoadList_Click(object sender, EventArgs e)
{
NewLoad();
}
void NewLoad()
{
// Извлекаем значение текстового поля без пробелов
String text = txtCustLimit.Text.Trim();
txtCustLimit.Text = text; // Сохраняем без пробелов для удобства
oleDbSelectCommand1.Parameters[0].Value = text;// Устанавливаем значение параметра
// Очищаем набор данных
dataSet11.Clear();
// Заполняем набор данных командой с новым значением параметра
oleDbDataAdapter1.Fill(dataSet11);
// Устанавливаем курсор списка в конец
listBox.SelectedIndex = listBox.Items.Count - 1;
}
Создайте обработчик
события KeyDown для текстового поля txtCustLimit с тем,
чтобы набор данных обновлялся и по нажатию пользователем клавиши Enter при
завершении ввода очередного значения параметра, и заполните его следующим
кодомprivate void txtCustLimit_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
NewLoad();
}
Запустите приложение
и поэкспериментируйте с его функциональностью, заложенной намиВ данном разделе мы продолжим развитие проекта и добавим
еще по одному дополнительному экземпляру классов OleDbDataAdapter и DataSet.
Для них мы сформируем SQL-команду, которая будет выбирать из источника одну
запись таблицы Customers (клиенты), соответствующую выделенному в списке ListBox конкретному клиенту по его идентификатору. Значения полей этой
записи будем отображать в текстовых полях формы, маркерованных именами столбцов
таблицы. Структура таблицы Customers файловой БД Northwind.mdb,
открытая в Microsoft
Office Access 2003, приведена на снимке

Перейдите в режим View Designer и поместите на форму еще один компонент OleDbDataAdapter с автоматически
сгенерированным именем oleDbDataAdapter2
В мастере настроек
адаптера Data Adapter Configuration Wizard перейдите
на вкладку Generate the SQL statements, щелкните
на кнопке Advanced Options и сбросьте все флажки вкладки Advanced Options,
поскольку поставщик будет только читать данные
Щелкните на кнопке Query Builder построителя SQL-запроса, в появившемся окне выберите
таблицу Customers и щелкните один раз на кнопке Add, а затем - Close
В появившемся списке
полей таблицы Customers отметьте флажки против каждого поля, из которых
должен читать данные БД настраиваемый адаптер данных
В поле Filter строки CustomerID второй секции построителя команд введите =?, после чего
в третьей секции будет сгенерирована командаSELECT CustomerID, CompanyName, ContactName, ContactTitle, Address, City, Region, PostalCode, Country, Phone, Fax FROM Customers WHERE (CustomerID = ?)
показанная на снимке

Завершите построение
запроса кнопкой OK и указанная команда окажется на текущей вкладке
мастера настройки поставщика данных, который по ней будет заполнять будущий
набор данных класса DataSet
Дойдите до последней
вкладки мастера настройки поставщика и убедитесь, что включен только режим Select (но не Update, Insert, Delete )
Щелчком на кнопке Finish завершите настройку поставщика данных и обратите внимание, что в нижней части
появился объект строки соединения для текущего адаптераТеперь нужно создать связанный с поставщиком объект набора данных DataSet,
который будет хранить данные таблицы Customers. Этот набор данных должен
быть согласован по структуре с настройками поставщика, поэтому создадим его
с помощью мастера. В панели Solution
Explorer обратите внимание, что пока в приложении присутствует только
один узел DataSet1.xsd, который был создан мастером на предыдущих
этапах выполнения работы и который описывает класс набора данных, соответствующий
хранению значений только двух полей таблицы Customers. Сейчас создадим
еще один такой узел.
Щелкните правой кнопкой
мыши на объекте oleDbDataAdapter2, выполните команду Generate DataSet контекстного
меню (или в Data главного меню проекта) и настройте одноименное окно мастера
как показано на снимке
После щелчка на кнопке OK будет создан объект набора данных, согласованный
с объектом oleDbDataAdapter2 второго поставщика данных, в обязанность
которого входит поставка данных в соответствии со строкой запроса.
Сейчас нужно изменить
пользовательский интерфейс формы, разместив в клиентской области справа от
списка элементы Label и TextBox с целью просмотра значений
полей таблицы Customers для
выделенной в списке записи.
Измените свойство Dock списка на значение None и оформите интерфейс формы в соответствии с рисунком
На форму было помещено 11 элементов Label и 11 элементов TextBox.
У текстовых меток Label в свойство текст были скопированы названия полей
таблицы Customers из SQL-команды Select, приведенной выше. Свойство AutoSize элементов Label было установлено в false, а свойство TextAlign -
в значение MiddleRight. После выравнивания элементов на форме с помощью
панели инструментов Layout они были выделены и зафиксированы командой Format/Lock Controls,
чтобы случайно не нарушить пользовательский интерфейс формы. Такого же самого
эффекта можно достигнуть, если установить свойство Locked всех элементов
в значение true.
Здесь необязательно, но для ручного программирования было бы удобнее, присвоить объектам текстовых полей осмысленные имена, поэтому, если хотите, выполните следующее действие:
Выделите последовательно
все добавленные элементы TextBox и присвойте им значение свойства (Name) равное именам столбцов таблицы Customers с префиксом txt, например, первый
элемент будет txtCustomerID, второй - txtCompanyName, и т.д.Обратите внимание, что при выполнении предыдущих разделов мы заполняли набор
данных из двух полей таблицы Customers в соответствии с командой Select поставщика oleDbDataAdapter1: поля CustomerID и CompanyName, а использовали
только данные поля CompanyName, настроив свойство DisplayMember объекта
списка listBox согласно приведенной ранее таблице
| Свойство | Значение |
|---|---|
(Name) |
listBox |
DataSource |
dataSet11 |
DisplayMember |
Customers.CompanyName |
Пришла пора присоединить к объекту listBox и поле CustomerID,
значения которого будут заполняться в коллекцию Items списка. Эта коллекция
не будет видна пользователю, но
значение ее любого элемента всегда можно получить для выделенной
строки списка и в дальнейшем использовать как параметр в команде второго поставщика.
Выделите на форме объект listBox и
установите его свойство ValueMember в значение Customers.CustomerID через
раскрывающийся список
Выделите объект listBox и
через панель Properties в режиме Events создайте для события SelectedIndexChanged обработчик,
который заполните такprivate void listBox_SelectedIndexChanged(object sender, EventArgs e)
{
// Очистить второй набор данных
dataSet21.Clear();
// Если выбран элемент списка, заполнить второй набор
if (listBox.SelectedIndex != -1)
{
oleDbSelectCommand2.Parameters[0].Value = listBox.SelectedValue;
oleDbDataAdapter2.Fill(dataSet21);
}
}
Запустите приложение
- вы увидите пустые значения текстовых полейЭто происходит потому, что хоть второй набор данных и заполняется в обработчике
записью, соответствующей переданному в качестве параметра SQL-команды значению
поля CustomerID,
но эти данные никак не связаны с элементами текстовых полей.
Исправим это:
Выделите на форме
элемент txtCustomerID текстового поля, в панели Properties раскройте
его свойство (DataBindings).Text и присоедините к нему поле CustomerID из
второго набора данных, как показано на снимке
Проделайте то же самое
для остальных элементов TextBox формы, связывая их с соответствующими
полями второго набора данных
Запустите приложение
- теперь все в порядке, для выбранной в списке строки в элементах TextBox отображаются значения всех полей текущей записи таблицы Customers
Здесь есть одна несогласованность, которую желательно сразу же устранить.
Дело в том, что текстовые поля мы используем только для просмотра данных, а
они еще и допускают видимость редактирования, которая на данном этапе никак
не отразится на самих данных в БД. Поэтому, чтобы
не вводить пользователя в заблуждение, редактирование в них нужно запретить.
Это можно сделать установкой их свойства ReadOnly в значение true,
но тогда они станут "тускло некрасивыми". Но есть другой способ их
'стерилизовать':
Поместите на форму
над текстовыми полями компонент CheckBox и настройте его так| Свойство | Значение |
|---|---|
(Name) |
checkEdit |
CheckAlign |
MiddleRight |
CheckState |
Unchecked |
Text |
Edit |
Выделите на форме
все расположенные справа элементы TextBox и в панели Properties в режиме Events создайте для них общий обработчик события KeyPress, который заполните
такprivate void txtCustomerID_KeyPress(object sender, KeyPressEventArgs e)
{
if(!checkEdit.Checked)
e.Handled = true;
}
Если сейчас запустить приложение, то можно убедиться (при сброшенном флажке), что хоть теперь текстовые поля не реагируют на ввод с клавиатуры символов, но осталась функциональность управляющих редактированием клавиш Delete, Home, End и стрелок. Чтобы окончательно 'добить' текстовые поля, выполните следующее:
Обведите блоком курсора
все текстовые поля, чтобы сразу опять выделить их, и создайте для них общий
обработчик события KeyDown, который заполните такprivate void txtFax_KeyDown(object sender, KeyEventArgs e)
{
if(!checkEdit.Checked)
e.Handled = true;
}
Несмотря на то, что в двух последних обработчиках использован одинаковый код,
не пытайтесь их объединить, поскольку они возбуждаются разными событиями и
передаваемый в них объект e имеет разный тип.
Запустите приложение
- теперь (при сброшенном флажке) поля не реагируют и на управляющие редактированием
клавиши, но в то же время остаются светлыми и даже лучезарнымиФорма, развитая в предыдущем разделе, пригодна только для просмотра данных БД. Даже если пользователь будет пытаться редактировать текстовые поля, это никак не отразится на хранящихся в БД данных. Однако более важной является возможность редактирование данных в текстовых полях и обновление их в БД. В этом разделе мы введем такую функциональность в приложение.
Поместите на форму
(перед этим выделите форму) над текстовыми полями две кнопки Button,
которые настройте в соответствии с таблицей свойств| Свойство | Значение |
|---|---|
(Name) |
btnSave |
Text |
Save |
(Name) |
btnCancel |
Text |
Cancel |
Кнопка Save будет сохранять содержимое полей редактирования в БД, а
кнопка Cancel - восстанавливать поля повторным чтением соответствующей записи
из БД. Вначале запрограммируем кнопку Cancel. Обработчик события кнопки Cancel должен выполнить ту же самую работу по заполнению текстовых полей из
базы, что у нас уже делает обработчик SelectedIndexChanged списка ListBox,
поэтому
Выделите на форме кнопку Cancel, зайдите в панель Properties в режиме Events и
для события Click через
раскрывающийся список его поля присоедините созданный ранее обработчик listBox_SelectedIndexChanged
Запустите приложение
и убедитесь в правильном функционировании кнопки CancelДля сбора редактируемой в текстовых полях информации нужно с них ее собрать
и обновить кэш набора данных, после чего выполнить метод Update() поставщика
данных, чтобы измененния набора данных сохранились в БД. Для сбора данных
с редактируемых полей, связанных с данными, мы применим экземпляр библиотечного
класса System.Windows.Forms.BindingContext.
Объект BindingContext наследуется как общедоступное виртуальное свойство
класса Control, следовательно содержится в любом элементе управления, в том
числе и в самой форме. Этот объект позволяет управлять процессом редактирования
и обновления данных, а также их передачей в набор данных. Он обеспечивает синхронизацию
наборов данных со связанными элементами управления. Воспользуемся объектом BindingContext как
Двойным щелчком на
кнопке Save создайте для нее обработчик события Click, который заполните
такprivate void btnSave_Click(object sender, EventArgs e)
{
SaveRecord();
}
private void SaveRecord()
{
// Применить свойство формы типа BindingContext
// и вытолкнуть изменения со всех дочерних элементов формы,
// связанных с данными, в кэш набора данных для заданной таблицы
this.BindingContext[dataSet21, "Customers"].EndCurrentEdit();
// Обновить данные в БД из кэша набора данных dataSet21
oleDbDataAdapter2.Update(dataSet21, "Customers");
// Принять изменения
// (лишнее!!! - вызывается автоматически методом Update)
//dataSet21.AcceptChanges();
// Обновить первый набор данных и список listBox
// с сохранением позиции выделенного элемента списка
// на случай, если вдруг редактировалось поле CompanyName,
// отображаемое в списке
int index = listBox.SelectedIndex;
NewLoad();
int count = listBox.Items.Count - 1;
index = index < count ? index : count; // Однострочный условный оператор
listBox.SelectedIndex = index;
}
Запустите приложение
- будет выброшено исключение, что адаптер данных не поддерживает команду Update()Да, действительно, наш второй поставщик данных был ранее настроен только на чтение данных из базы (как и первый), поэтому нужно его перенастроить, чтобы добавить функциональность обновлять данные в БД.
В режиме View Designer формы
вызовите контекстное меню для объекта oleDbDataAdapter2 и выполните
команду Configure Data Adapter
Дойдите до вкладки Generate the SQL statements мастера,
щелкните на кнопке Advanced Options и установите все флажки вкладки Advanced Options
Дойдите до последней
вкладки мастера настройки поставщика, убедитесь, что включены все режимы Select, Insert, Update, Delete и
щелкните на кнопке Finish, чтобы завершить настройку
В обработчике кнопки Save мы использовали обновление данных
в базе в два этапа: вначале вытолкнули изменения из связанных элементов управления
в набор данных, и лишь потом дали команду на окончательное внесение изменений
из набора в саму базу. Такой подход реализован в среде ADO.NET для обеспечения отсоединенного
подключения к
базе данных.
Механизм работы таков: поставщик данных при выполнении запроса SELECT на выборку
данных кратковременно подключается к базе данных, загружает в кэш найденные
данные и сразу же отключается от базы. Это дает возможность работать с базой
данных максимальному количеству пользователей. Поставщик данных после разъединения
имитирует теперь собой базу данных в кэше оперативной памяти и пользователь
работает с данными через набор данных не замечая, что фактически работает с
копией данных.
Пользователь в отсоединенном режиме может сохранять данные якобы в базе, но при каждом сохранении поставщик данных фактически создает у себя по несколько версий изменений, которые можно всегда отменить (наподобие уровней откатов). При окончательном сохранении изменений в базе данных разработчик через программирование поставщика данных может реализовать необходимую стратегию обновления и разрешения конфликтных ситуаций.
Механизм ADO.NET работы в отсоединенном режиме опирается на принцип оптимистического параллелизма обработки данных. Данные в самой базе не блокируются и могут потребляться сразу несколькими пользователями. Поставщики данных каждого пользователя при попытке сохранить проверяют изменения подведомственных данных в базе и позволят сохранить изменения только тех данных, с которыми не работают в данный момент другие пользователи.
Для многопользовательского режима работы с БД наиболее распространены следующие типы проверки нарушения параллелизма и стратегий обновления данных
Функционирование среды ADO.NET в отсоединенном режиме основано на создании
временных данных в файле на языке XML. Чтобы убедиться в этом, достаточно заглянуть
в панель Solution Explorer и увидеть файлы с расширением .xsd и
с именами классов, созданных оболочкой для наших наборов данных. Если открыть,
например, файл DataSet2.xsd через команду контекстного меню Open With/Source
Code (
Для нас из этой скромной теории важно то, что значение ключевого поля существующей записи редактировать нельзя.
Ранее мы запрограммировали кнопку Cancel немного нерационально, посадив
ее на обработчик события SelectedIndexChanged списка. Это ведет к лишним
затратам ресурсов, заставляя приложение при откатах заново считывать данные
во второй набор данных dataSet21. Более правильный способ - заставить
набор данных вытолкнуть свое неизменившееся содержимое в элементы редактирования.
В режиме View Designer формы
выделите кнопку Cancel и через панель Properties в режиме Events очистите
поле события Click кнопки
Двойным щелчком на
кнопке Cancel создайте для нее обработчик, который заполните следующим кодомprivate void btnCancel_Click(object sender, EventArgs e)
{
// Вытолкнуть первоначальные данные из
// набора данных в поля редактирования
this.BindingContext[dataSet21, "Customers"].CancelCurrentEdit();
}
Запустите приложение
- теперь восстановление текстовых полей редактирования из набора данных происходит
мгновенноВ этом разделе мы добавим функциональность удаления или добавления по одной
записи в редактируемую таблицу через второй поставщик данных. Как и при редактировании,
будем использовать возможности объекта BidingContext формы и действовать через
него на связанные с данными элементы управления.
Добавьте на форму две
кнопки и настройти их в соответствии с таблицей свойств| Элемент | Свойство | Значение |
|---|---|---|
Button |
(Name) |
btnAdd |
Text |
Add |
|
Button |
(Name) |
btnDelete |
Text |
Delete |
Создайте обработчики
события Click для обеих кнопок, которые заполните такprivate void btnAdd_Click(object sender, EventArgs e)
{
// Добавить пустую запись в набор и
// показать ее в элементах редактирования
this.BindingContext[dataSet21, "Customers"].AddNew();
}
private void btnDelete_Click(object sender, EventArgs e)
{
if (listBox.Items.Count <= 0)// Список пустой, удалять нечего
return;
// Отметить в наборе текущую запись как удаленную
int index = this.BindingContext[dataSet21, "Customers"].Position;
this.BindingContext[dataSet21, "Customers"].RemoveAt(index);
SaveRecord();// Записать изменения в БД
}
Теперь, когда мы ввели функциональность по изменению записей, начальное состояние флажка редактирования можно изменить для удобства
Выделите флажок редактирования
и установите его свойство checkEdit.Checked=trueВнешне окно запущенного приложения на данном этапе должно быть примерно таким

Если пользователь редактировал данные и забыл их сохранить, то при закрытии формы сохраним изменения сами.
Выделите форму и создайте
обработчик для ее события FormClosed, которое возбуждается при закрытии
формы. В обработчик вставьте вызов функции сохранения данныхprivate void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
SaveRecord();
}
Работа с БД подвержена внешним ошибкам, которые могут возникнуть при обращении
к БД. Программист должен заготовить код на такой случай, чтобы необработанные
исключения не поставили пользователя в тупик или повредили данные. Попробуйте
в нашем приложении удалить какую нибудь родную запись БД Northwind,
- будет выброшено необработанное исключение и приложение завершится аварийно.
Стандартным способом перехвата исключений и их обработки является применение
механизма try... catch... finally. Последовательность блоков catch организует
цепочку фильтров для отлавливания исключений в сторону увеличения
их общего характера. Исключения средой исполнения выдаются в виде объектов-экземпляров
классов, порожденных в конечном итоге от класса Exception. Мы воспользуемся
перехватом специализированных объектов исключений типа OleDbException,
поскольку именно они соответствуют типу возможных ошибок. Другие возможные
исключения общего характера будем отлавливать самыми последними в цепочке фильтров.
В нашем проекте уязвимыми с точки зрения ошибок являются операции чтения данных
из БД, сохранение и удаление записей. Ниже приведен полный листинг файла Form1.cs с
кодом обработки ошибок
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Data.OleDb;
namespace FormsADO
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
String ConnectionString(String fileNameNotExt)
{
string JetEngineString = @"Provider=Microsoft.Jet.OLEDB.4.0;Jet OLEDB:Engine Type=5;Data Source=";
string pathToFile = Application.StartupPath.ToString() + "\\Data\\";
return JetEngineString + pathToFile + fileNameNotExt.Trim() + ".mdb";
}
private void Form1_Load(object sender, EventArgs e)
{
// Настраиваем объект команд
oleDbSelectCommand1.CommandText = "SELECT CustomerID, CompanyName FROM Customers\r\n"
+ "WHERE (CompanyName LIKE ? + '%')";
oleDbSelectCommand1.Parameters.Add(new System.Data.OleDb.OleDbParameter());
oleDbSelectCommand1.Parameters[0].Value = "A";
// Задаем строку соединения
oleDbConnection1.ConnectionString = ConnectionString("Northwind");
// Заполняем набор данных настроенным адаптером данных
//oleDbDataAdapter1.Fill(dataSet11);
SafeFill_1();
// Устанавливаем курсор списка в конец
listBox.SelectedIndex = listBox.Items.Count - 1;
}
private void btnLoadList_Click(object sender, EventArgs e)
{
NewLoad();
}
void NewLoad()
{
// Извлекаем значение текстового поля без пробелов
String text = txtCustLimit.Text.Trim();
txtCustLimit.Text = text; // Сохраняем без пробелов для удобства
oleDbSelectCommand1.Parameters[0].Value = text;// Устанавливаем значение параметра
// Очищаем набор данных
dataSet11.Clear();
// Заполняем набор данных командой с новым значением параметра
//oleDbDataAdapter1.Fill(dataSet11);
SafeFill_1();
// Устанавливаем курсор списка в конец
listBox.SelectedIndex = listBox.Items.Count - 1;
}
private void txtCustLimit_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
NewLoad();
}
private void listBox_SelectedIndexChanged(object sender, EventArgs e)
{
// Очистить второй набор данных
dataSet21.Clear();
// Если выбран элемент списка, заполнить второй набор
if (listBox.SelectedIndex != -1)
{
oleDbSelectCommand2.Parameters[0].Value = listBox.SelectedValue;
//oleDbDataAdapter2.Fill(dataSet21);
SafeFill_2();
}
}
private void txtCustomerID_KeyPress(object sender, KeyPressEventArgs e)
{
if(!checkEdit.Checked)
e.Handled = true;
}
private void txtFax_KeyDown(object sender, KeyEventArgs e)
{
if(!checkEdit.Checked)
e.Handled = true;
}
private void btnSave_Click(object sender, EventArgs e)
{
SaveRecord();
}
private void SaveRecord()
{
// Применить свойство формы типа BindingContext
// и вытолкнуть изменения со всех дочерних элементов формы,
// связанных с данными, в кэш набора данных для заданной таблицы
this.BindingContext[dataSet21, "Customers"].EndCurrentEdit();
// Обновить данные в БД из кэша набора данных dataSet21
//oleDbDataAdapter2.Update(dataSet21, "Customers");
SafeUpdate_2();
// Принять изменения
// (лишнее!!! - вызывается автоматически методом Update)
//dataSet21.AcceptChanges();
// Обновить первый набор данных и список listBox
// с сохранением позиции выделенного элемента списка
// на случай, если вдруг редактировалось поле CompanyName,
// отображаемое в списке
int index = listBox.SelectedIndex;
NewLoad();
int count = listBox.Items.Count - 1;
index = index < count ? index : count; // Однострочный условный оператор
listBox.SelectedIndex = index;
}
private void btnCancel_Click(object sender, EventArgs e)
{
// Вытолкнуть первоначальные данные из
// набора данных в поля редактирования
this.BindingContext[dataSet21, "Customers"].CancelCurrentEdit();
}
private void btnAdd_Click(object sender, EventArgs e)
{
// Добавить пустую запись в набор и
// показать ее в элементах редактирования
this.BindingContext[dataSet21, "Customers"].AddNew();
}
private void btnDelete_Click(object sender, EventArgs e)
{
if (listBox.Items.Count <= 0)// Список пустой, удалять нечего
return;
// Отметить в наборе текущую запись как удаленную
int index = this.BindingContext[dataSet21, "Customers"].Position;
this.BindingContext[dataSet21, "Customers"].RemoveAt(index);
SaveRecord();// Записать изменения в БД
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
SaveRecord();
}
private void SafeFill_1()
{
try // Обработка ощибок
{
// Заполняем набор данных настроенным адаптером данных
oleDbDataAdapter1.Fill(dataSet11);
}
catch (OleDbException ext)
{
string message = "Ошибка чтения данных из БД\n";
string caption = "Ошибка обращения к БД";
MessageBox.Show(String.Format(message + "Подробности: {0}", ext.Message),
caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
oleDbConnection1.Close();
}
}
private void SafeFill_2()
{
try // Обработка ощибок
{
// Заполняем набор данных настроенным адаптером данных
oleDbDataAdapter2.Fill(dataSet21);
}
catch (OleDbException ext)
{
string message = "Ошибка чтения данных из БД\n";
string caption = "Ошибка обращения к БД";
MessageBox.Show(String.Format(message + "Подробности: {0}", ext.Message),
caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
oleDbConnection2.Close();
}
}
private void SafeUpdate_2()
{
try // Обработка ощибок
{
// Обновить данные в БД из кэша набора данных dataSet21
oleDbDataAdapter2.Update(dataSet21, "Customers");
}
catch (OleDbException ext)
{
string message = "Ошибка записи данных в БД\n";
string caption = "Ошибка обращения к БД";
MessageBox.Show(String.Format(message + "Подробности: {0}", ext.Message),
caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
oleDbConnection2.Close();
}
}
}
}
Ниже приведены примеры окна сообщений для неверной строки соединения и для несоответствия данных типу поля:
![]() |
![]() |
Конечно, нужно было бы еще защитить методы объекта BindingContext,
но это уже механическая работа и ее можно сделать самостоятельно.
Продолжим работу на примере готовой БД Northwind. В этом упражнении
создадим проект, в котором содержимое поля CompanyName таблицы Customers (заказчики)
будем загружать в раскрывающийся список ComboBox, а при выборе определенного
элемента списка связанные с соответствующей записью данные другой таблицы ( Orders -
заказы) будем отображать пользователю в элементе управления DataGrid.
Для двух разных таблиц нам понадобятся два поставщика OleDbDataAdapter и
два набора данных DataSet. Элементы управления ComboBox и DataGrid свяжем
с наборами данных через их свойство DataSource. Для ComboBox нужно
дополнительно еще связать свойства DisplayMember и ValueMember с
соответствующими полями ( CompanyName и CustomerID ) подключенной
таблицы Customers, а для DataGrid указать таблицу Orders в
свойстве DataMember.
При выборе пользователем элемента списка значение CustomerID будет
передаваться как параметр в команду поставщика данных, связанного с таблицей Orders,
который загрузит из БД в набор нужные записи для отображения их в DataGrid.
Если пользователь редактировал данные в DataGrid, то при смене записи в ComboBox
эти данные нужно сохранить в БД. Для этого нужно создать соответствующую SQL-команду
для поставщика. Итак, приступим.
Добавьте к решению
командой Add/New Project новый проект типа Windows Forms Application с
именем FormsADO2, а прежний проект FormsADO исключите
из решения командой Remove
Поместите в рабочую
область оболочки компонент OleDbDataAdapter, соедините его с файлом Northwind.mdb БД
из прилагаемого каталога Source (на предложение оболочки скопировать
файл БД в проект ответьте Да ) и настройте его только на чтение полей CustomerID и CompanyName таблицы Customers (заказчики),
используя построитель Query Builder, как это мы делали ранее
в предыдущем упражнении

Выделите поставщик oleDbDataAdapter1 и
командой меню Data/Generate Dataset создайте для него класс DataSet1 и
объект набора данных с автоматически сгенерированным именем dataSet11
Поместите на форму
новый поставщик OleDbDataAdapter с именем oleDbDataAdapter2 для
таблицы Orders (заказы),
в его настройках учтите все возможности взаимодействия с данными, а в команде SELECT предусмотрите передачу значения параметра по полю CustomerID и
сортировку по дате выбранных из БД данных. Для правильного формирования команды
используйте окно Query
Builder

Обратите внимание, как в каком порядке мы вызуально формируем в мастере Query Builder условие
выборки по параметру CustomerID: вначале отмечаем поле CustomerID в
верхней вкладке Orders окна мастера, чтобы это поле появилось во второй
вкладке, далее прописываем фильтр =?, а затем в этой же второй вкладке
снимаем флажок Output со столбца CustomerID, чтобы не включать
его данные поставщиком.


Выделите поставщик oleDbDataAdapter2 и
командой меню Data/Generate Dataset создайте для него класс DataSet2 и
объект набора данных с автоматически сгенерированным именем dataSet21
Поместите на форму
компоненты Label, ComboBox и DataGrid (если их нет в
палитре Toolbox, добавьте к соответствующей вкладке командой контекстного
меню Choose Items ), разместите их в соответствии с рисунком и настройте
согласно приведенной таблице свойств
| Компонент | Свойство | Значение |
|---|---|---|
Label |
Text |
Заказчики |
ComboBox |
(Name) |
cbCustomers |
DataSource |
dataSet11 |
|
DisplayMember |
Customers.CompanyName |
|
| ValueMember | Customers.CustomerID |
|
DataGrid |
(Name) |
dgOrders |
DataSource |
dataSet21 |
|
DataMember |
Orders |
|
CaptionText |
Заказы для выбранного заказчика |
Создайте для формы Form1 обработчик
события Load, а для ComboBox - обработчик события SelectedIndexChanged,
которые заполните такusing System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace FormsADO2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// Заполнить dataSet11 и связанный с ним ComboBox
// данными таблицы Customers - Заказчики
oleDbDataAdapter1.Fill(dataSet11);
// Заполнить dataSet21 и связанный с ним DataGrid данными
// таблицы Orders - Заказы для выбранного Заказчика
RefreshOrders();
}
private void RefreshOrders()
{
// Заполнить dataSet21 и связанный с ним DataGrid данными
// таблицы Orders - Заказы для выбранного Заказчика
dataSet21.Clear();
// Если выбран Заказчик в ComboBox
if (cbCustomers.SelectedIndex != -1)
{
oleDbDataAdapter2.SelectCommand.Parameters[0].Value =
cbCustomers.SelectedValue;
oleDbDataAdapter2.Fill(dataSet21);
}
}
private void cbCustomers_SelectedIndexChanged(object sender, EventArgs e)
{
// Заполнить dataSet21 и связанный с ним DataGrid данными
// таблицы Orders - Заказы для выбранного Заказчика
RefreshOrders();
}
}
}
Запустите проект FormsADO2 и
испытайте работу приложения
В предыдущем разделе мы просматривали отфильтрованные записи дочерней таблицы Orders (заказы)
по записи, выбранной в таблице Customers (заказчики). В этом разделе
мы разовьем проект и дадим возможность пользователю отметить в DataGrid конкретный
заказ, после чего в дополнительной форме появятся подробности о выбранном заказе.
Командой меню Project/Add Windows Form добавьте
к проекту новую форму
Установите для нее
свойства , чтобы размеры формы нельзя было менять и при запуске
она бы не отображалась в панели задач, а также убрали системные кнопки свертывания
и развертывания окна
Из палитры Toolbox перенесите
на форму невизуальные пользовательские элементы, указанные в таблице, которые
обеспечать только чтение данных
из БД, и настройте их через мастера как ранее ( только
для Select )Мы будем связывать поставщики с тремя таблицами БД Northwind: Orders, Employees, Order Details и
для них в значениях свойств объектов приведены уже готовые SQL-команды, которые
попытайтесь получить с помощью мастера Query Builder.
| Компонент | Свойство | Значение |
|---|---|---|
OleDbDataAdapter |
(Name) |
oleOrders |
CommandText |
SELECT OrderID, CustomerID, EmployeeID, OrderDate, RequiredDate,
ShippedDate, ShipVia, Freight, ShipName, ShipAddress, ShipCity, ShipRegion,
ShipPostalCode, ShipCountry
FROM Orders
WHERE (OrderID = ?)
|
|
OleDbDataAdapter |
(Name) |
oleEmployees |
CommandText |
SELECT EmployeeID, RTRIM(LastName) + ', ' + RTRIM(FirstName) AS FullName
FROM Employees
WHERE (EmployeeID = ?)
|
|
OleDbDataAdapter |
(Name) |
oleOrderDetails |
CommandText |
SELECT OrderID, ProductID, UnitPrice, Quantity, Discount
FROM [Order Details]
WHERE (OrderID = ?)
|
Для всех поставщиков создадим общий класс набора данных с именем DataSetAll и
один экземпляр этого класса dataSetAll, где будут храниться все три
таблицы БД одновременно.
Вызовите контекстное
меню для поставщика oleOrders, выполните команду Generate DataSet и
заполните окно мастера в соответствии с рисунком
Переименуйте созданный
объект набора данных в dataSetAll
Вызовите контекстное
меню для поставщика oleEmployees, выполните команду Generate DataSet и
заполните окно мастера в соответствии с рисунком
Вызовите контекстное
меню для поставщика oleOrderDetails, выполните команду Generate
DataSet и заполните окно мастера в соответствии с рисунком
Что мы сделали? Мы связали каждый поставщик с общим набором данных, где будут храниться три таблицы одновременно, а постащики будут заполнять свои таблицы по команде из кода. Теперь нужно создать пользовательский интерфейс дополнительной формы.
Добавьте на форму Form2 и
настройте компоненты в соответствии с таблицей свойств, позиционируйте их
в соответствии с рисунком| Компонент | Свойство | Значение |
|---|---|---|
Label |
Text |
Номер заказа: |
TextBox |
BorderStyle |
FixedSingle |
(DataBindings).Text |
dataSetAll - Orders.OrderID |
|
Label |
Text |
Дата заказа: |
DateTimePicker |
(DataBindings).Value |
dataSetAll - Orders.OrderDate |
Format |
Long |
|
Label |
Text |
Принял заказ: |
TextBox |
BorderStyle |
FixedSingle |
(DataBindings).Text |
dataSetAll - Employees.FullName |
|
DataGrid |
(Name) |
dgOrderDetails |
CaptionText |
Подробности заказов | |
DataSource |
dataSetAll |
|
DataMember |
Order Details |

В конструктор класса Form2 файла Form2.cs введите
дополнительный параметр для передачи из Form1 номера выбранного заказаpublic partial class Form2 : Form
{
long orderID;// Поле для идентификатора заказа
// Идентификатор заказа передается
// через конструктор вызывающей формы
public Form2(long orderID)
{
InitializeComponent();
this.orderID = orderID;
}
}
Для формы Form2 создайте
обработчик события Load, который заполните такprivate void Form2_Load(object sender, EventArgs e)
{
// Найти одну запись по номеру orderID ключевого поля
oleOrders.SelectCommand.Parameters[0].Value = orderID;
oleOrders.Fill(dataSetAll, "Orders");
// Найти закрепленного за заказом служащего
oleEmployees.SelectCommand.Parameters[0].Value =
dataSetAll.Orders[0].EmployeeID;
oleEmployees.Fill(dataSetAll, "Employees");
// Найти записи с деталями, соответствующие выбранному заказу
oleOrderDetails.SelectCommand.Parameters[0].Value = orderID;
oleOrderDetails.Fill(dataSetAll, "Order Details");
}
Перейдите в режим View Designer формы Form1,
выделите объект dgOrders и через панель Properties создайте
обработчик события Click, который заполните такprivate void dgOrders_Click(object sender, EventArgs e)
{
// В первом столбце находится OrderID
long orderID = Convert.ToInt64(dataSet21.Orders.Rows
[dgOrders.CurrentCell.RowNumber].ItemArray[0]);
Form frm2 = new Form2(orderID);// Передаем параметр
frm2.ShowDialog();
}
Вторичное окно мы показываем
в модальном режиме, который перехватывает на себя цикл сообщений приложения,
пока не будет закрыто модальное окно
Испытайте работу приложенияСпроектированное приложение читает всех заказчиков в элемент ComboBox и
заполняет элемент DataGrid всеми заказами для выбранного заказчика.
Затем по выделенному заказу во вторую форму передается номер заказа и по нему
заполняется подробностями заказа объект DataGrid и другие элементы представления
данных. Данные, выбранные из разных таблиц, хранятся в одном наборе данных
с именем dataSetAll.
В данном упражнении мы познакомимся с простыми, но мощными возможностями среды проектирования по созданию СУБД. Нам не придется писать ни строчки кода, все через настройки среда сделает за нас.
Добавьте к решению
командой File/Add/New Project новый проект с именем FormsADO3 и
назначьте его стартовым командой Project/Set as StartUp Project (или
первые два проекта исключите из решения командой Remove и единственный
проект автоматически станет стартовым)
Командой Data/Show Data Sources откройте
панель Data Sources
Щелкните на ссылке Add New Data Source,
чтобы создать новый источник (поставщик + набор) данных, и настройте его
в соответствии с приведенной на рисунках последовательностью вкладок мастера:
Source

Categories
После щелчка на кнопке Finish в
панели Data Sources в раскрывающемся списке с именем таблицы Categories выберите
элемент DataGridView, с помощью которого будет отображаться таблица Categories на
форме, а после этого схватите в панели Data Sources курсором
пиктограмму таблицы и перенесите ее на форму
Выделите на форме объект DataGridView и
в панели Properties настройте его так| Компонент | Свойство | Значение | |
|---|---|---|---|
DataGridView |
AutoSizeColumnsMode |
Fill |
|
Dock |
Fill |
||
Columns |
CategoryID.HeaderText |
Номер | |
CategoryName.HeaderText |
Название | ||
Description.HeaderText |
Пояснения | ||
Picture.HeaderText |
Рисунки |
Запустите приложение
- получится примерно такой результат
Сравните таблицу в
нашем приложении с той же таблицей, открытой в Microsoft Office Access -
разницы нет
Откройте в текущем
проекте файлы Form1.cs, Form1.Designer.cs, NorthwindDataSet.Designer.cs и
поудивляйтесь, сколько оболочка нагенерировала за нас кода, а мы сами не
написали ни строчки!!! Вот как раз такое программирование называется визуальным (для
ленивых)
Все необходимые для выполнения данной работы программы можно найти в прилагаемом каталоге.
В этой работе мы последовательно познакомимся с технологией ADO.NET (.mdb. Наш выбор основан на том, что не требуется устанавливать
дополнительные программы, типа SQL Server, для такого рода баз. Пока
мы будем пользоваться готовой учебной базой данных Northwind.mdb,
расположенной в прилагаемом к работе каталоге Source. Позднее мы научимся
создавать свои базы данных либо с помощью СУБД Microsoft
Office Access, либо с помощью COM объектов в оболочке Visual
Studio 2008.
Создание базы данных начинается с ее проектирования с помощью диаграмм. Затем создается сам файл (или несколько взаимосвязанных файлов) БД и устанавливается структура БД, определяющая таблицы, поля и их типы, назначение ключей и индексов. Затем проектируется приложение, управляющее данными БД. Мы займемся разработкой приложения, решающего типовые задачи по редактированию и представлению данных БД.
Базы данных называют источниками данных. Microsoft ADO.NET - это часть библиотеки .NET Framework, позволяющая создавать приложения для управления данными в структурированных файловых или серверных хранилищах. Библиотечные средства ADO.NET обеспечивают подключение к источникам данных, выполнение команд SQL, обработку и выборку данных.
ADO.NET представлено набором программных средств, прежде всего классов,
которые находятся в библиотечной
сборке System.Data.dll. Применительно к формату OLE DB наиболее
важными из них являются специализированные классы пространства имен System.Data.OleDb:
OleDbConnection - обоспечивает подключение к источнику данныхOleDbDataAdapter - обеспечивает связь между источником данных и набором
данных в оперативной памятиOleDbCommand - объект команд SQL для работы в источнике данныхВ совокупности экземпляры этих классов в приложении называются поставщиком
данных (Data Provider). Далее идет независимый от формата БД слой для хранения
данных в кэше оперативной памяти и просмотра данных из пространства имен System.Data:
DataSet - набор кэшированных данных, выборочно загруженных из источникаDataView - позволяет создать несколько представлений одной и той
же таблицыНачнем с подготовительных действий и созданию связанного с данными списка, дающего возможность только просматривать данные БД.
Создайте новый проект
типа Windows Forms Application с именем FormsADO и решением ADO
В палитре компонентов Toolbox откройте вкладку дата и перетащите на форму компонент OleDbDataAdapter.
Если в настройках проекта такого компонента не окажется, то щелкните на
вкладке Data палитры Toolbox правой кнопкой мыши и подключите искомый
компонент командой Choose Items
В появившемся окне
мастера
настройки поставщика щелкните по кнопке New Connection и выберите
файл подключаемой БД Northwind.mdb из прилагаемого к работе каталога Source
После щелчка на кнопке Next оболочка выдаст сообщение с предложением скопировать
присоединенный файл БД в каталог проекта

Щелкните на кнопке
" Да " и перейдите к следующей вкладке мастера
Здесь в текстовом поле предлагается указать SQL -запрос, который поставщик
данных должен выполнять по умолчанию при заполнении набора данных DataSet.
Мы хотим прочитать содержимое двух столбцов CustomerID и CompanyName из
таблицы Customers,
поэтому либо руками, либо с помощью мастера нужно в многострочном текстовом
поле интерфейса мастера заполнить команду:
Щелкните на кнопке Query Builder, в диалоговом окне Add Table выберите
из списка таблицу Customers и по щелчку на кнопке Add вкладки Tables (потом на Close ) отметьте в интерфейсе флажки против нужных полей
таблицы, чтобы мастер написал для них текст запроса
Щелкните на кнопке OK окна Query Builder и текст запроса будет помещен в
поле мастера
По умолчанию мастер готов создать операторы SQL не только для выборки, но и для вставки, обновления и удаления данных.
Нам на данном этапе
потребуется только чтение данных и другие возможности нужно отключить щелчком
на кнопке Advanced Options,
сбросив ненужные флажки
Дойдите до последней вкладки
мастера настройки поставщика и убедитесь, что включен только режим Select (но
не Update, Insert, Delete)
Щелкните на кнопке Finish, чтобы завершить создание и настройку объектов поставщика
данных (экземпляров OleDbDataAdapter и OleDbConnection ) и сохранить параметры в установках проекта
В панели Solution
Explorer откройте файл Form1.Designer.cs и убедитесь, что мастер создал необходимый
код с настройками поставщика, в состав которого входят экземпляры классов OleDbCommand, OleDbDataAdapter и OleDbConnectionОписанную процедуру можно в любом месте прервать щелчком на кнопке Cancel мастера
настроек и вернуться к ней в любое время. Для этого достаточно щелкнуть в нижней
части рабочей области на экземпляре класса OleDbDataAdapter правой
кнопкой мыши и выполнитиь команду Configure Data Adapter (или выделить
объект поставщика и выполнить ту же команду через пункт Data главного меню
оболочки)

Выделите в проекте
файл БД и через панель Properties установите его свойство Copy
to Output Directory в значение Copy if newer, чтобы он находился
вместе с исполняемой сборкой в одном каталоге bin проектаИтак, у нас есть объект соединения с БД и объект-посредник между базой и
набором данных (кэшем оперативной памяти), который должен выполнять команды SQL и
управлять потоками данных. Теперь нужно создать сам набор данных как экземпляр
класса DataSet.
Щелкните правой кнопкой
мыши на экземпляре oleDbDataAdapter1 в нижней части рабочей области
и выполните команду Generate DataSet (то же самое можно выполнить
через меню Data оболочки)
В появившемся окне
мастера Generate DataSet щелкните на кнопке OK
Будет создан объект набора данных dataSet11, который будет загружать два столбца
таблицы Customers согласно SQL-запроса, указанного в настройках поставщика oleDbDataAdapter1.
В качестве элемента, отображающего набор данных объекта dataSet11,
применим список ListBox.
Поместите на форму
из вкладки Common Controls палитры компонентов экземпляр компонента ListBox и настройте его в соответствии с таблицей| Свойство | Значение |
|---|---|
( Name ) |
listBox |
DataSource |
dataSet11 |
DisplayMember |
Customers.CompanyName |
Dock |
Fill |
Хотя набор данных dataSet11 правильно связан со свойствами элемента
управления ListBox, но если выполнить приложение, то список останется пустым,
потому, что набор данных не был заполнен данными. Код заполнения поместим в
обработчик события Load формы.
В панели Properties выделите форму из раскрывающегося списка (или щелкните на заголовке формы)
и в режиме Events создайте обработчик события Load, который заполните такusing System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace FormsADO
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// Заполняем набор данных настроенным адаптером данных
oleDbDataAdapter1.Fill(dataSet11);
// Устанавливаем курсор списка в конец
listBox.SelectedIndex = listBox.Items.Count - 1;
}
}
}
Запустите приложение
и вы увидите данные поля Customers.CompanyName, связанного со свойством DisplayMember списка ListBox,
подключенного к набору данных dataSet11 через свойство DataSource
Метод Fill() поставщика данных загружает все данные указанных столбцов
таблицы в набор данных. Часто не все данные нужны пользователю, к тому же их
может быть очень много, что перегружает ресурсы компьютера. В этом разделе
будет показано, как создать параметризованный оператор SQL для заполнения набора
данных не всеми строками таблицы БД, а пока только одной. Мы
продолжим развитие нашего проекта, который ограничивает выборку данных из источника
определенным условием, а заодно и установим строку подключения к БД вручную.
Если выделить объект строки подключения и посмотреть свойство ConnectionString,
то можно убедиться, что оно имеет следующее строковое значение:
Provider=Microsoft.Jet.OLEDB.4.0; Data Source=|DataDirectory|\Northwind.mdb;Persist Security Info=True
Переопределим его программно на другое значение.
Вставьте в
класс Form1 следующий кодusing System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace FormsADO
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
String ConnectionString(String fileNameNotExt)
{
string JetEngineString = @"Provider=Microsoft.Jet.OLEDB.4.0;Jet OLEDB:Engine Type=5;Data Source=";
string pathToFile = Application.StartupPath.ToString() + "\\Data\\";
return JetEngineString + pathToFile + fileNameNotExt.Trim() + ".mdb";
}
private void Form1_Load(object sender, EventArgs e)
{
// Задаем строку соединения
oleDbConnection1.ConnectionString = ConnectionString("Northwind");
// Заполняем набор данных настроенным адаптером данных
oleDbDataAdapter1.Fill(dataSet11);
// Устанавливаем курсор списка в конец
listBox.SelectedIndex = listBox.Items.Count - 1;
}
}
}
В панели Solution
Explorer для узла проекта FormsADO вызовите контекстное
меню, командой Add/New
Folder добавьте каталог Data и курсором перенесите в него БД Northwind.mdb
Выделите файл
БД и через панель Properties убедитесь, что его свойство Copy to Output
Directory установлено в значение Copy if newer
Запустите приложение
- программа FormsADO.exe работает нормальноТеперь установим фильтр на заполнение данными набора данных DataSet через
команду SQL -запроса. Если открыть файл Form1.Designer.cs, то
мы увидим примерно следующий код в методе InitializeComponent() формы
private void InitializeComponent()
{
this.oleDbSelectCommand1 = new System.Data.OleDb.OleDbCommand();
this.oleDbConnection1 = new System.Data.OleDb.OleDbConnection();
this.oleDbDataAdapter1 = new System.Data.OleDb.OleDbDataAdapter();
this.dataSet11 = new FormsADO.DataSet1();
this.listBox = new System.Windows.Forms.ListBox();
((System.ComponentModel.ISupportInitialize)(this.dataSet11)).BeginInit();
this.SuspendLayout();
//
// oleDbSelectCommand1
//
this.oleDbSelectCommand1.CommandText = "SELECT CustomerID, CompanyName\r\nFROM Customers";
this.oleDbSelectCommand1.Connection = this.oleDbConnection1;
//
// oleDbConnection1
//
this.oleDbConnection1.ConnectionString =
"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|\\Northwind.mdb;Persis" +
"t Security Info=True";
//
// oleDbDataAdapter1
//
this.oleDbDataAdapter1.SelectCommand = this.oleDbSelectCommand1;
this.oleDbDataAdapter1.TableMappings.AddRange(new System.Data.Common.DataTableMapping[] {
new System.Data.Common.DataTableMapping("Table", "Customers", new System.Data.Common.DataColumnMapping[] {
new System.Data.Common.DataColumnMapping("CustomerID", "CustomerID"),
new System.Data.Common.DataColumnMapping("CompanyName", "CompanyName")})});
...........................................
}
...............................................
private System.Data.OleDb.OleDbCommand oleDbSelectCommand1;
Обратите внимание на выделенные строки в приведенном листинге. Объявляется
поле-ссылка на объект команд oleDbSelectCommand1,
затем создается сам объект, его свойству CommandText присваивается SQL-запрос
и, наконец, объект команд подключается к одноименному свойству объекта адаптера
данных. Теперь метод Fill() адаптера данных будет выбирать из БД нужные
данные и передавать их набору данных DataSet. Если в коде события Load формы
поменять команду в свойстве CommandText, то она и будет выполняться адаптером
данных.
Таким образом, изменим для адаптера команду выборки данных из БД и добавим к форме необходимый для этого пользовательский интерфейс.
Добавьте в обработчик
события Load формы код перенастройки объекта oleDbSelectCommand1private void Form1_Load(object sender, EventArgs e)
{
// Настраиваем объект команд
oleDbSelectCommand1.CommandText = "SELECT CustomerID, CompanyName FROM Customers\r\n"
+ "WHERE (CompanyName LIKE ? + '%')";
oleDbSelectCommand1.Parameters.Add(new System.Data.OleDb.OleDbParameter());
oleDbSelectCommand1.Parameters[0].Value = "A";
// Задаем строку соединения
oleDbConnection1.ConnectionString = ConnectionString("Northwind");
// Заполняем набор данных настроенным адаптером данных
oleDbDataAdapter1.Fill(dataSet11);
// Устанавливаем курсор списка в конец
listBox.SelectedIndex = listBox.Items.Count - 1;
}
В данном коде пояснения требует только SQL-команда, написанная на диалекте SQL для формата файловых баз данных, принятого в Microsoft Office Access 2003
SELECT CustomerID, CompanyName FROM Customers\r\n"
+ "WHERE (CompanyName LIKE ? + '%')
\r\n - управляющие символы "возврат каретки - перевод строки"WHERE условие - условие отбора данных из источникаLIKE шаблон - проверка на соответствие заданному шаблону,
в котором могут использоваться специальные символы, означающие подстановки
значений параметров команды. В качестве шаблона поиска могут быть использованы
два подстановочных знака% - любое количество символов в текущей позицииВ нашем случае выражение после ключевого слова LIKE означает в своей
совокупности, что поиск данных в источнике будет проведен по любому значению,
которое введет пользователь в текстовое поле формы. Следуеть помнить, что если
в команде присутствует LIKE шаблон, то даже если для поля
поиска существует индекс, то он не работает, а проверка условия выполняется
последовательным перебором всех записей таблицы от начала до конца. Это очень
замедляет выборку данных на таблицах с большим количеством записей.
Измените
свойство Dock экземпляра класса ListBox с Fill на Bottom и
освободите место сверху клиентской области формы
Поместите
на форму, как ее дочерние элементы, экземпляры компонентов Label, TextBox, Button в
соответствии со снимком и настройте их как показано в таблице свойств ниже
| Тип | Свойство | Значение |
|---|---|---|
ListBox |
Dock |
Bottom |
Label |
Text |
Customer |
TextBox |
(Name) |
txtCustLimit |
Text |
A (латинское) |
|
Button |
(Name) |
btnLoadList |
Text |
Load |
Создайте обработчик
события Click кнопки btnLoadList и заполните его кодом переназначения
содержимого параметра SQL-запросаprivate void btnLoadList_Click(object sender, EventArgs e)
{
NewLoad();
}
void NewLoad()
{
// Извлекаем значение текстового поля без пробелов
String text = txtCustLimit.Text.Trim();
txtCustLimit.Text = text; // Сохраняем без пробелов для удобства
oleDbSelectCommand1.Parameters[0].Value = text;// Устанавливаем значение параметра
// Очищаем набор данных
dataSet11.Clear();
// Заполняем набор данных командой с новым значением параметра
oleDbDataAdapter1.Fill(dataSet11);
// Устанавливаем курсор списка в конец
listBox.SelectedIndex = listBox.Items.Count - 1;
}
Создайте обработчик
события KeyDown для текстового поля txtCustLimit с тем,
чтобы набор данных обновлялся и по нажатию пользователем клавиши Enter при
завершении ввода очередного значения параметра, и заполните его следующим
кодомprivate void txtCustLimit_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
NewLoad();
}
Запустите приложение
и поэкспериментируйте с его функциональностью, заложенной намиВ данном разделе мы продолжим развитие проекта и добавим
еще по одному дополнительному экземпляру классов OleDbDataAdapter и DataSet.
Для них мы сформируем SQL-команду, которая будет выбирать из источника одну
запись таблицы Customers (клиенты), соответствующую выделенному в списке ListBox конкретному клиенту по его идентификатору. Значения полей этой
записи будем отображать в текстовых полях формы, маркерованных именами столбцов
таблицы. Структура таблицы Customers файловой БД Northwind.mdb,
открытая в Microsoft
Office Access 2003, приведена на снимке

Перейдите в режим View Designer и поместите на форму еще один компонент OleDbDataAdapter с автоматически
сгенерированным именем oleDbDataAdapter2
В мастере настроек
адаптера Data Adapter Configuration Wizard перейдите
на вкладку Generate the SQL statements, щелкните
на кнопке Advanced Options и сбросьте все флажки вкладки Advanced Options,
поскольку поставщик будет только читать данные
Щелкните на кнопке Query Builder построителя SQL-запроса, в появившемся окне выберите
таблицу Customers и щелкните один раз на кнопке Add, а затем - Close
В появившемся списке
полей таблицы Customers отметьте флажки против каждого поля, из которых
должен читать данные БД настраиваемый адаптер данных
В поле Filter строки CustomerID второй секции построителя команд введите =?, после чего
в третьей секции будет сгенерирована командаSELECT CustomerID, CompanyName, ContactName, ContactTitle, Address, City, Region, PostalCode, Country, Phone, Fax FROM Customers WHERE (CustomerID = ?)
показанная на снимке

Завершите построение
запроса кнопкой OK и указанная команда окажется на текущей вкладке
мастера настройки поставщика данных, который по ней будет заполнять будущий
набор данных класса DataSet
Дойдите до последней
вкладки мастера настройки поставщика и убедитесь, что включен только режим Select (но не Update, Insert, Delete )
Щелчком на кнопке Finish завершите настройку поставщика данных и обратите внимание, что в нижней части
появился объект строки соединения для текущего адаптераТеперь нужно создать связанный с поставщиком объект набора данных DataSet,
который будет хранить данные таблицы Customers. Этот набор данных должен
быть согласован по структуре с настройками поставщика, поэтому создадим его
с помощью мастера. В панели Solution
Explorer обратите внимание, что пока в приложении присутствует только
один узел DataSet1.xsd, который был создан мастером на предыдущих
этапах выполнения работы и который описывает класс набора данных, соответствующий
хранению значений только двух полей таблицы Customers. Сейчас создадим
еще один такой узел.
Щелкните правой кнопкой
мыши на объекте oleDbDataAdapter2, выполните команду Generate DataSet контекстного
меню (или в Data главного меню проекта) и настройте одноименное окно мастера
как показано на снимке
После щелчка на кнопке OK будет создан объект набора данных, согласованный
с объектом oleDbDataAdapter2 второго поставщика данных, в обязанность
которого входит поставка данных в соответствии со строкой запроса.
Сейчас нужно изменить
пользовательский интерфейс формы, разместив в клиентской области справа от
списка элементы Label и TextBox с целью просмотра значений
полей таблицы Customers для
выделенной в списке записи.
Измените свойство Dock списка на значение None и оформите интерфейс формы в соответствии с рисунком
На форму было помещено 11 элементов Label и 11 элементов TextBox.
У текстовых меток Label в свойство текст были скопированы названия полей
таблицы Customers из SQL-команды Select, приведенной выше. Свойство AutoSize элементов Label было установлено в false, а свойство TextAlign -
в значение MiddleRight. После выравнивания элементов на форме с помощью
панели инструментов Layout они были выделены и зафиксированы командой Format/Lock Controls,
чтобы случайно не нарушить пользовательский интерфейс формы. Такого же самого
эффекта можно достигнуть, если установить свойство Locked всех элементов
в значение true.
Здесь необязательно, но для ручного программирования было бы удобнее, присвоить объектам текстовых полей осмысленные имена, поэтому, если хотите, выполните следующее действие:
Выделите последовательно
все добавленные элементы TextBox и присвойте им значение свойства (Name) равное именам столбцов таблицы Customers с префиксом txt, например, первый
элемент будет txtCustomerID, второй - txtCompanyName, и т.д.Обратите внимание, что при выполнении предыдущих разделов мы заполняли набор
данных из двух полей таблицы Customers в соответствии с командой Select поставщика oleDbDataAdapter1: поля CustomerID и CompanyName, а использовали
только данные поля CompanyName, настроив свойство DisplayMember объекта
списка listBox согласно приведенной ранее таблице
| Свойство | Значение |
|---|---|
(Name) |
listBox |
DataSource |
dataSet11 |
DisplayMember |
Customers.CompanyName |
Пришла пора присоединить к объекту listBox и поле CustomerID,
значения которого будут заполняться в коллекцию Items списка. Эта коллекция
не будет видна пользователю, но
значение ее любого элемента всегда можно получить для выделенной
строки списка и в дальнейшем использовать как параметр в команде второго поставщика.
Выделите на форме объект listBox и
установите его свойство ValueMember в значение Customers.CustomerID через
раскрывающийся список
Выделите объект listBox и
через панель Properties в режиме Events создайте для события SelectedIndexChanged обработчик,
который заполните такprivate void listBox_SelectedIndexChanged(object sender, EventArgs e)
{
// Очистить второй набор данных
dataSet21.Clear();
// Если выбран элемент списка, заполнить второй набор
if (listBox.SelectedIndex != -1)
{
oleDbSelectCommand2.Parameters[0].Value = listBox.SelectedValue;
oleDbDataAdapter2.Fill(dataSet21);
}
}
Запустите приложение
- вы увидите пустые значения текстовых полейЭто происходит потому, что хоть второй набор данных и заполняется в обработчике
записью, соответствующей переданному в качестве параметра SQL-команды значению
поля CustomerID,
но эти данные никак не связаны с элементами текстовых полей.
Исправим это:
Выделите на форме
элемент txtCustomerID текстового поля, в панели Properties раскройте
его свойство (DataBindings).Text и присоедините к нему поле CustomerID из
второго набора данных, как показано на снимке
Проделайте то же самое
для остальных элементов TextBox формы, связывая их с соответствующими
полями второго набора данных
Запустите приложение
- теперь все в порядке, для выбранной в списке строки в элементах TextBox отображаются значения всех полей текущей записи таблицы Customers
Здесь есть одна несогласованность, которую желательно сразу же устранить.
Дело в том, что текстовые поля мы используем только для просмотра данных, а
они еще и допускают видимость редактирования, которая на данном этапе никак
не отразится на самих данных в БД. Поэтому, чтобы
не вводить пользователя в заблуждение, редактирование в них нужно запретить.
Это можно сделать установкой их свойства ReadOnly в значение true,
но тогда они станут "тускло некрасивыми". Но есть другой способ их
'стерилизовать':
Поместите на форму
над текстовыми полями компонент CheckBox и настройте его так| Свойство | Значение |
|---|---|
(Name) |
checkEdit |
CheckAlign |
MiddleRight |
CheckState |
Unchecked |
Text |
Edit |
Выделите на форме
все расположенные справа элементы TextBox и в панели Properties в режиме Events создайте для них общий обработчик события KeyPress, который заполните
такprivate void txtCustomerID_KeyPress(object sender, KeyPressEventArgs e)
{
if(!checkEdit.Checked)
e.Handled = true;
}
Если сейчас запустить приложение, то можно убедиться (при сброшенном флажке), что хоть теперь текстовые поля не реагируют на ввод с клавиатуры символов, но осталась функциональность управляющих редактированием клавиш Delete, Home, End и стрелок. Чтобы окончательно 'добить' текстовые поля, выполните следующее:
Обведите блоком курсора
все текстовые поля, чтобы сразу опять выделить их, и создайте для них общий
обработчик события KeyDown, который заполните такprivate void txtFax_KeyDown(object sender, KeyEventArgs e)
{
if(!checkEdit.Checked)
e.Handled = true;
}
Несмотря на то, что в двух последних обработчиках использован одинаковый код,
не пытайтесь их объединить, поскольку они возбуждаются разными событиями и
передаваемый в них объект e имеет разный тип.
Запустите приложение
- теперь (при сброшенном флажке) поля не реагируют и на управляющие редактированием
клавиши, но в то же время остаются светлыми и даже лучезарнымиФорма, развитая в предыдущем разделе, пригодна только для просмотра данных БД. Даже если пользователь будет пытаться редактировать текстовые поля, это никак не отразится на хранящихся в БД данных. Однако более важной является возможность редактирование данных в текстовых полях и обновление их в БД. В этом разделе мы введем такую функциональность в приложение.
Поместите на форму
(перед этим выделите форму) над текстовыми полями две кнопки Button,
которые настройте в соответствии с таблицей свойств| Свойство | Значение |
|---|---|
(Name) |
btnSave |
Text |
Save |
(Name) |
btnCancel |
Text |
Cancel |
Кнопка Save будет сохранять содержимое полей редактирования в БД, а
кнопка Cancel - восстанавливать поля повторным чтением соответствующей записи
из БД. Вначале запрограммируем кнопку Cancel. Обработчик события кнопки Cancel должен выполнить ту же самую работу по заполнению текстовых полей из
базы, что у нас уже делает обработчик SelectedIndexChanged списка ListBox,
поэтому
Выделите на форме кнопку Cancel, зайдите в панель Properties в режиме Events и
для события Click через
раскрывающийся список его поля присоедините созданный ранее обработчик listBox_SelectedIndexChanged
Запустите приложение
и убедитесь в правильном функционировании кнопки CancelДля сбора редактируемой в текстовых полях информации нужно с них ее собрать
и обновить кэш набора данных, после чего выполнить метод Update() поставщика
данных, чтобы измененния набора данных сохранились в БД. Для сбора данных
с редактируемых полей, связанных с данными, мы применим экземпляр библиотечного
класса System.Windows.Forms.BindingContext.
Объект BindingContext наследуется как общедоступное виртуальное свойство
класса Control, следовательно содержится в любом элементе управления, в том
числе и в самой форме. Этот объект позволяет управлять процессом редактирования
и обновления данных, а также их передачей в набор данных. Он обеспечивает синхронизацию
наборов данных со связанными элементами управления. Воспользуемся объектом BindingContext как
Двойным щелчком на
кнопке Save создайте для нее обработчик события Click, который заполните
такprivate void btnSave_Click(object sender, EventArgs e)
{
SaveRecord();
}
private void SaveRecord()
{
// Применить свойство формы типа BindingContext
// и вытолкнуть изменения со всех дочерних элементов формы,
// связанных с данными, в кэш набора данных для заданной таблицы
this.BindingContext[dataSet21, "Customers"].EndCurrentEdit();
// Обновить данные в БД из кэша набора данных dataSet21
oleDbDataAdapter2.Update(dataSet21, "Customers");
// Принять изменения
// (лишнее!!! - вызывается автоматически методом Update)
//dataSet21.AcceptChanges();
// Обновить первый набор данных и список listBox
// с сохранением позиции выделенного элемента списка
// на случай, если вдруг редактировалось поле CompanyName,
// отображаемое в списке
int index = listBox.SelectedIndex;
NewLoad();
int count = listBox.Items.Count - 1;
index = index < count ? index : count; // Однострочный условный оператор
listBox.SelectedIndex = index;
}
Запустите приложение
- будет выброшено исключение, что адаптер данных не поддерживает команду Update()Да, действительно, наш второй поставщик данных был ранее настроен только на чтение данных из базы (как и первый), поэтому нужно его перенастроить, чтобы добавить функциональность обновлять данные в БД.
В режиме View Designer формы
вызовите контекстное меню для объекта oleDbDataAdapter2 и выполните
команду Configure Data Adapter
Дойдите до вкладки Generate the SQL statements мастера,
щелкните на кнопке Advanced Options и установите все флажки вкладки Advanced Options
Дойдите до последней
вкладки мастера настройки поставщика, убедитесь, что включены все режимы Select, Insert, Update, Delete и
щелкните на кнопке Finish, чтобы завершить настройку
В обработчике кнопки Save мы использовали обновление данных
в базе в два этапа: вначале вытолкнули изменения из связанных элементов управления
в набор данных, и лишь потом дали команду на окончательное внесение изменений
из набора в саму базу. Такой подход реализован в среде ADO.NET для обеспечения отсоединенного
подключения к
базе данных.
Механизм работы таков: поставщик данных при выполнении запроса SELECT на выборку
данных кратковременно подключается к базе данных, загружает в кэш найденные
данные и сразу же отключается от базы. Это дает возможность работать с базой
данных максимальному количеству пользователей. Поставщик данных после разъединения
имитирует теперь собой базу данных в кэше оперативной памяти и пользователь
работает с данными через набор данных не замечая, что фактически работает с
копией данных.
Пользователь в отсоединенном режиме может сохранять данные якобы в базе, но при каждом сохранении поставщик данных фактически создает у себя по несколько версий изменений, которые можно всегда отменить (наподобие уровней откатов). При окончательном сохранении изменений в базе данных разработчик через программирование поставщика данных может реализовать необходимую стратегию обновления и разрешения конфликтных ситуаций.
Механизм ADO.NET работы в отсоединенном режиме опирается на принцип оптимистического параллелизма обработки данных. Данные в самой базе не блокируются и могут потребляться сразу несколькими пользователями. Поставщики данных каждого пользователя при попытке сохранить проверяют изменения подведомственных данных в базе и позволят сохранить изменения только тех данных, с которыми не работают в данный момент другие пользователи.
Для многопользовательского режима работы с БД наиболее распространены следующие типы проверки нарушения параллелизма и стратегий обновления данных
Функционирование среды ADO.NET в отсоединенном режиме основано на создании
временных данных в файле на языке XML. Чтобы убедиться в этом, достаточно заглянуть
в панель Solution Explorer и увидеть файлы с расширением .xsd и
с именами классов, созданных оболочкой для наших наборов данных. Если открыть,
например, файл DataSet2.xsd через команду контекстного меню Open With/Source
Code (
Для нас из этой скромной теории важно то, что значение ключевого поля существующей записи редактировать нельзя.
Ранее мы запрограммировали кнопку Cancel немного нерационально, посадив
ее на обработчик события SelectedIndexChanged списка. Это ведет к лишним
затратам ресурсов, заставляя приложение при откатах заново считывать данные
во второй набор данных dataSet21. Более правильный способ - заставить
набор данных вытолкнуть свое неизменившееся содержимое в элементы редактирования.
В режиме View Designer формы
выделите кнопку Cancel и через панель Properties в режиме Events очистите
поле события Click кнопки
Двойным щелчком на
кнопке Cancel создайте для нее обработчик, который заполните следующим кодомprivate void btnCancel_Click(object sender, EventArgs e)
{
// Вытолкнуть первоначальные данные из
// набора данных в поля редактирования
this.BindingContext[dataSet21, "Customers"].CancelCurrentEdit();
}
Запустите приложение
- теперь восстановление текстовых полей редактирования из набора данных происходит
мгновенноВ этом разделе мы добавим функциональность удаления или добавления по одной
записи в редактируемую таблицу через второй поставщик данных. Как и при редактировании,
будем использовать возможности объекта BidingContext формы и действовать через
него на связанные с данными элементы управления.
Добавьте на форму две
кнопки и настройти их в соответствии с таблицей свойств| Элемент | Свойство | Значение |
|---|---|---|
Button |
(Name) |
btnAdd |
Text |
Add |
|
Button |
(Name) |
btnDelete |
Text |
Delete |
Создайте обработчики
события Click для обеих кнопок, которые заполните такprivate void btnAdd_Click(object sender, EventArgs e)
{
// Добавить пустую запись в набор и
// показать ее в элементах редактирования
this.BindingContext[dataSet21, "Customers"].AddNew();
}
private void btnDelete_Click(object sender, EventArgs e)
{
if (listBox.Items.Count <= 0)// Список пустой, удалять нечего
return;
// Отметить в наборе текущую запись как удаленную
int index = this.BindingContext[dataSet21, "Customers"].Position;
this.BindingContext[dataSet21, "Customers"].RemoveAt(index);
SaveRecord();// Записать изменения в БД
}
Теперь, когда мы ввели функциональность по изменению записей, начальное состояние флажка редактирования можно изменить для удобства
Выделите флажок редактирования
и установите его свойство checkEdit.Checked=trueВнешне окно запущенного приложения на данном этапе должно быть примерно таким

Если пользователь редактировал данные и забыл их сохранить, то при закрытии формы сохраним изменения сами.
Выделите форму и создайте
обработчик для ее события FormClosed, которое возбуждается при закрытии
формы. В обработчик вставьте вызов функции сохранения данныхprivate void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
SaveRecord();
}
Работа с БД подвержена внешним ошибкам, которые могут возникнуть при обращении
к БД. Программист должен заготовить код на такой случай, чтобы необработанные
исключения не поставили пользователя в тупик или повредили данные. Попробуйте
в нашем приложении удалить какую нибудь родную запись БД Northwind,
- будет выброшено необработанное исключение и приложение завершится аварийно.
Стандартным способом перехвата исключений и их обработки является применение
механизма try... catch... finally. Последовательность блоков catch организует
цепочку фильтров для отлавливания исключений в сторону увеличения
их общего характера. Исключения средой исполнения выдаются в виде объектов-экземпляров
классов, порожденных в конечном итоге от класса Exception. Мы воспользуемся
перехватом специализированных объектов исключений типа OleDbException,
поскольку именно они соответствуют типу возможных ошибок. Другие возможные
исключения общего характера будем отлавливать самыми последними в цепочке фильтров.
В нашем проекте уязвимыми с точки зрения ошибок являются операции чтения данных
из БД, сохранение и удаление записей. Ниже приведен полный листинг файла Form1.cs с
кодом обработки ошибок
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Data.OleDb;
namespace FormsADO
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
String ConnectionString(String fileNameNotExt)
{
string JetEngineString = @"Provider=Microsoft.Jet.OLEDB.4.0;Jet OLEDB:Engine Type=5;Data Source=";
string pathToFile = Application.StartupPath.ToString() + "\\Data\\";
return JetEngineString + pathToFile + fileNameNotExt.Trim() + ".mdb";
}
private void Form1_Load(object sender, EventArgs e)
{
// Настраиваем объект команд
oleDbSelectCommand1.CommandText = "SELECT CustomerID, CompanyName FROM Customers\r\n"
+ "WHERE (CompanyName LIKE ? + '%')";
oleDbSelectCommand1.Parameters.Add(new System.Data.OleDb.OleDbParameter());
oleDbSelectCommand1.Parameters[0].Value = "A";
// Задаем строку соединения
oleDbConnection1.ConnectionString = ConnectionString("Northwind");
// Заполняем набор данных настроенным адаптером данных
//oleDbDataAdapter1.Fill(dataSet11);
SafeFill_1();
// Устанавливаем курсор списка в конец
listBox.SelectedIndex = listBox.Items.Count - 1;
}
private void btnLoadList_Click(object sender, EventArgs e)
{
NewLoad();
}
void NewLoad()
{
// Извлекаем значение текстового поля без пробелов
String text = txtCustLimit.Text.Trim();
txtCustLimit.Text = text; // Сохраняем без пробелов для удобства
oleDbSelectCommand1.Parameters[0].Value = text;// Устанавливаем значение параметра
// Очищаем набор данных
dataSet11.Clear();
// Заполняем набор данных командой с новым значением параметра
//oleDbDataAdapter1.Fill(dataSet11);
SafeFill_1();
// Устанавливаем курсор списка в конец
listBox.SelectedIndex = listBox.Items.Count - 1;
}
private void txtCustLimit_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
NewLoad();
}
private void listBox_SelectedIndexChanged(object sender, EventArgs e)
{
// Очистить второй набор данных
dataSet21.Clear();
// Если выбран элемент списка, заполнить второй набор
if (listBox.SelectedIndex != -1)
{
oleDbSelectCommand2.Parameters[0].Value = listBox.SelectedValue;
//oleDbDataAdapter2.Fill(dataSet21);
SafeFill_2();
}
}
private void txtCustomerID_KeyPress(object sender, KeyPressEventArgs e)
{
if(!checkEdit.Checked)
e.Handled = true;
}
private void txtFax_KeyDown(object sender, KeyEventArgs e)
{
if(!checkEdit.Checked)
e.Handled = true;
}
private void btnSave_Click(object sender, EventArgs e)
{
SaveRecord();
}
private void SaveRecord()
{
// Применить свойство формы типа BindingContext
// и вытолкнуть изменения со всех дочерних элементов формы,
// связанных с данными, в кэш набора данных для заданной таблицы
this.BindingContext[dataSet21, "Customers"].EndCurrentEdit();
// Обновить данные в БД из кэша набора данных dataSet21
//oleDbDataAdapter2.Update(dataSet21, "Customers");
SafeUpdate_2();
// Принять изменения
// (лишнее!!! - вызывается автоматически методом Update)
//dataSet21.AcceptChanges();
// Обновить первый набор данных и список listBox
// с сохранением позиции выделенного элемента списка
// на случай, если вдруг редактировалось поле CompanyName,
// отображаемое в списке
int index = listBox.SelectedIndex;
NewLoad();
int count = listBox.Items.Count - 1;
index = index < count ? index : count; // Однострочный условный оператор
listBox.SelectedIndex = index;
}
private void btnCancel_Click(object sender, EventArgs e)
{
// Вытолкнуть первоначальные данные из
// набора данных в поля редактирования
this.BindingContext[dataSet21, "Customers"].CancelCurrentEdit();
}
private void btnAdd_Click(object sender, EventArgs e)
{
// Добавить пустую запись в набор и
// показать ее в элементах редактирования
this.BindingContext[dataSet21, "Customers"].AddNew();
}
private void btnDelete_Click(object sender, EventArgs e)
{
if (listBox.Items.Count <= 0)// Список пустой, удалять нечего
return;
// Отметить в наборе текущую запись как удаленную
int index = this.BindingContext[dataSet21, "Customers"].Position;
this.BindingContext[dataSet21, "Customers"].RemoveAt(index);
SaveRecord();// Записать изменения в БД
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
SaveRecord();
}
private void SafeFill_1()
{
try // Обработка ощибок
{
// Заполняем набор данных настроенным адаптером данных
oleDbDataAdapter1.Fill(dataSet11);
}
catch (OleDbException ext)
{
string message = "Ошибка чтения данных из БД\n";
string caption = "Ошибка обращения к БД";
MessageBox.Show(String.Format(message + "Подробности: {0}", ext.Message),
caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
oleDbConnection1.Close();
}
}
private void SafeFill_2()
{
try // Обработка ощибок
{
// Заполняем набор данных настроенным адаптером данных
oleDbDataAdapter2.Fill(dataSet21);
}
catch (OleDbException ext)
{
string message = "Ошибка чтения данных из БД\n";
string caption = "Ошибка обращения к БД";
MessageBox.Show(String.Format(message + "Подробности: {0}", ext.Message),
caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
oleDbConnection2.Close();
}
}
private void SafeUpdate_2()
{
try // Обработка ощибок
{
// Обновить данные в БД из кэша набора данных dataSet21
oleDbDataAdapter2.Update(dataSet21, "Customers");
}
catch (OleDbException ext)
{
string message = "Ошибка записи данных в БД\n";
string caption = "Ошибка обращения к БД";
MessageBox.Show(String.Format(message + "Подробности: {0}", ext.Message),
caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
oleDbConnection2.Close();
}
}
}
}
Ниже приведены примеры окна сообщений для неверной строки соединения и для несоответствия данных типу поля:
![]() |
![]() |
Конечно, нужно было бы еще защитить методы объекта BindingContext,
но это уже механическая работа и ее можно сделать самостоятельно.
Продолжим работу на примере готовой БД Northwind. В этом упражнении
создадим проект, в котором содержимое поля CompanyName таблицы Customers (заказчики)
будем загружать в раскрывающийся список ComboBox, а при выборе определенного
элемента списка связанные с соответствующей записью данные другой таблицы ( Orders -
заказы) будем отображать пользователю в элементе управления DataGrid.
Для двух разных таблиц нам понадобятся два поставщика OleDbDataAdapter и
два набора данных DataSet. Элементы управления ComboBox и DataGrid свяжем
с наборами данных через их свойство DataSource. Для ComboBox нужно
дополнительно еще связать свойства DisplayMember и ValueMember с
соответствующими полями ( CompanyName и CustomerID ) подключенной
таблицы Customers, а для DataGrid указать таблицу Orders в
свойстве DataMember.
При выборе пользователем элемента списка значение CustomerID будет
передаваться как параметр в команду поставщика данных, связанного с таблицей Orders,
который загрузит из БД в набор нужные записи для отображения их в DataGrid.
Если пользователь редактировал данные в DataGrid, то при смене записи в ComboBox
эти данные нужно сохранить в БД. Для этого нужно создать соответствующую SQL-команду
для поставщика. Итак, приступим.
Добавьте к решению
командой Add/New Project новый проект типа Windows Forms Application с
именем FormsADO2, а прежний проект FormsADO исключите
из решения командой Remove
Поместите в рабочую
область оболочки компонент OleDbDataAdapter, соедините его с файлом Northwind.mdb БД
из прилагаемого каталога Source (на предложение оболочки скопировать
файл БД в проект ответьте Да ) и настройте его только на чтение полей CustomerID и CompanyName таблицы Customers (заказчики),
используя построитель Query Builder, как это мы делали ранее
в предыдущем упражнении

Выделите поставщик oleDbDataAdapter1 и
командой меню Data/Generate Dataset создайте для него класс DataSet1 и
объект набора данных с автоматически сгенерированным именем dataSet11
Поместите на форму
новый поставщик OleDbDataAdapter с именем oleDbDataAdapter2 для
таблицы Orders (заказы),
в его настройках учтите все возможности взаимодействия с данными, а в команде SELECT предусмотрите передачу значения параметра по полю CustomerID и
сортировку по дате выбранных из БД данных. Для правильного формирования команды
используйте окно Query
Builder

Обратите внимание, как в каком порядке мы вызуально формируем в мастере Query Builder условие
выборки по параметру CustomerID: вначале отмечаем поле CustomerID в
верхней вкладке Orders окна мастера, чтобы это поле появилось во второй
вкладке, далее прописываем фильтр =?, а затем в этой же второй вкладке
снимаем флажок Output со столбца CustomerID, чтобы не включать
его данные поставщиком.


Выделите поставщик oleDbDataAdapter2 и
командой меню Data/Generate Dataset создайте для него класс DataSet2 и
объект набора данных с автоматически сгенерированным именем dataSet21
Поместите на форму
компоненты Label, ComboBox и DataGrid (если их нет в
палитре Toolbox, добавьте к соответствующей вкладке командой контекстного
меню Choose Items ), разместите их в соответствии с рисунком и настройте
согласно приведенной таблице свойств
| Компонент | Свойство | Значение |
|---|---|---|
Label |
Text |
Заказчики |
ComboBox |
(Name) |
cbCustomers |
DataSource |
dataSet11 |
|
DisplayMember |
Customers.CompanyName |
|
| ValueMember | Customers.CustomerID |
|
DataGrid |
(Name) |
dgOrders |
DataSource |
dataSet21 |
|
DataMember |
Orders |
|
CaptionText |
Заказы для выбранного заказчика |
Создайте для формы Form1 обработчик
события Load, а для ComboBox - обработчик события SelectedIndexChanged,
которые заполните такusing System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace FormsADO2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// Заполнить dataSet11 и связанный с ним ComboBox
// данными таблицы Customers - Заказчики
oleDbDataAdapter1.Fill(dataSet11);
// Заполнить dataSet21 и связанный с ним DataGrid данными
// таблицы Orders - Заказы для выбранного Заказчика
RefreshOrders();
}
private void RefreshOrders()
{
// Заполнить dataSet21 и связанный с ним DataGrid данными
// таблицы Orders - Заказы для выбранного Заказчика
dataSet21.Clear();
// Если выбран Заказчик в ComboBox
if (cbCustomers.SelectedIndex != -1)
{
oleDbDataAdapter2.SelectCommand.Parameters[0].Value =
cbCustomers.SelectedValue;
oleDbDataAdapter2.Fill(dataSet21);
}
}
private void cbCustomers_SelectedIndexChanged(object sender, EventArgs e)
{
// Заполнить dataSet21 и связанный с ним DataGrid данными
// таблицы Orders - Заказы для выбранного Заказчика
RefreshOrders();
}
}
}
Запустите проект FormsADO2 и
испытайте работу приложения
В предыдущем разделе мы просматривали отфильтрованные записи дочерней таблицы Orders (заказы)
по записи, выбранной в таблице Customers (заказчики). В этом разделе
мы разовьем проект и дадим возможность пользователю отметить в DataGrid конкретный
заказ, после чего в дополнительной форме появятся подробности о выбранном заказе.
Командой меню Project/Add Windows Form добавьте
к проекту новую форму
Установите для нее
свойства , чтобы размеры формы нельзя было менять и при запуске
она бы не отображалась в панели задач, а также убрали системные кнопки свертывания
и развертывания окна
Из палитры Toolbox перенесите
на форму невизуальные пользовательские элементы, указанные в таблице, которые
обеспечать только чтение данных
из БД, и настройте их через мастера как ранее ( только
для Select )Мы будем связывать поставщики с тремя таблицами БД Northwind: Orders, Employees, Order Details и
для них в значениях свойств объектов приведены уже готовые SQL-команды, которые
попытайтесь получить с помощью мастера Query Builder.
| Компонент | Свойство | Значение |
|---|---|---|
OleDbDataAdapter |
(Name) |
oleOrders |
CommandText |
SELECT OrderID, CustomerID, EmployeeID, OrderDate, RequiredDate,
ShippedDate, ShipVia, Freight, ShipName, ShipAddress, ShipCity, ShipRegion,
ShipPostalCode, ShipCountry
FROM Orders
WHERE (OrderID = ?)
|
|
OleDbDataAdapter |
(Name) |
oleEmployees |
CommandText |
SELECT EmployeeID, RTRIM(LastName) + ', ' + RTRIM(FirstName) AS FullName
FROM Employees
WHERE (EmployeeID = ?)
|
|
OleDbDataAdapter |
(Name) |
oleOrderDetails |
CommandText |
SELECT OrderID, ProductID, UnitPrice, Quantity, Discount
FROM [Order Details]
WHERE (OrderID = ?)
|
Для всех поставщиков создадим общий класс набора данных с именем DataSetAll и
один экземпляр этого класса dataSetAll, где будут храниться все три
таблицы БД одновременно.
Вызовите контекстное
меню для поставщика oleOrders, выполните команду Generate DataSet и
заполните окно мастера в соответствии с рисунком
Переименуйте созданный
объект набора данных в dataSetAll
Вызовите контекстное
меню для поставщика oleEmployees, выполните команду Generate DataSet и
заполните окно мастера в соответствии с рисунком
Вызовите контекстное
меню для поставщика oleOrderDetails, выполните команду Generate
DataSet и заполните окно мастера в соответствии с рисунком
Что мы сделали? Мы связали каждый поставщик с общим набором данных, где будут храниться три таблицы одновременно, а постащики будут заполнять свои таблицы по команде из кода. Теперь нужно создать пользовательский интерфейс дополнительной формы.
Добавьте на форму Form2 и
настройте компоненты в соответствии с таблицей свойств, позиционируйте их
в соответствии с рисунком| Компонент | Свойство | Значение |
|---|---|---|
Label |
Text |
Номер заказа: |
TextBox |
BorderStyle |
FixedSingle |
(DataBindings).Text |
dataSetAll - Orders.OrderID |
|
Label |
Text |
Дата заказа: |
DateTimePicker |
(DataBindings).Value |
dataSetAll - Orders.OrderDate |
Format |
Long |
|
Label |
Text |
Принял заказ: |
TextBox |
BorderStyle |
FixedSingle |
(DataBindings).Text |
dataSetAll - Employees.FullName |
|
DataGrid |
(Name) |
dgOrderDetails |
CaptionText |
Подробности заказов | |
DataSource |
dataSetAll |
|
DataMember |
Order Details |

В конструктор класса Form2 файла Form2.cs введите
дополнительный параметр для передачи из Form1 номера выбранного заказаpublic partial class Form2 : Form
{
long orderID;// Поле для идентификатора заказа
// Идентификатор заказа передается
// через конструктор вызывающей формы
public Form2(long orderID)
{
InitializeComponent();
this.orderID = orderID;
}
}
Для формы Form2 создайте
обработчик события Load, который заполните такprivate void Form2_Load(object sender, EventArgs e)
{
// Найти одну запись по номеру orderID ключевого поля
oleOrders.SelectCommand.Parameters[0].Value = orderID;
oleOrders.Fill(dataSetAll, "Orders");
// Найти закрепленного за заказом служащего
oleEmployees.SelectCommand.Parameters[0].Value =
dataSetAll.Orders[0].EmployeeID;
oleEmployees.Fill(dataSetAll, "Employees");
// Найти записи с деталями, соответствующие выбранному заказу
oleOrderDetails.SelectCommand.Parameters[0].Value = orderID;
oleOrderDetails.Fill(dataSetAll, "Order Details");
}
Перейдите в режим View Designer формы Form1,
выделите объект dgOrders и через панель Properties создайте
обработчик события Click, который заполните такprivate void dgOrders_Click(object sender, EventArgs e)
{
// В первом столбце находится OrderID
long orderID = Convert.ToInt64(dataSet21.Orders.Rows
[dgOrders.CurrentCell.RowNumber].ItemArray[0]);
Form frm2 = new Form2(orderID);// Передаем параметр
frm2.ShowDialog();
}
Вторичное окно мы показываем
в модальном режиме, который перехватывает на себя цикл сообщений приложения,
пока не будет закрыто модальное окно
Испытайте работу приложенияСпроектированное приложение читает всех заказчиков в элемент ComboBox и
заполняет элемент DataGrid всеми заказами для выбранного заказчика.
Затем по выделенному заказу во вторую форму передается номер заказа и по нему
заполняется подробностями заказа объект DataGrid и другие элементы представления
данных. Данные, выбранные из разных таблиц, хранятся в одном наборе данных
с именем dataSetAll.
В данном упражнении мы познакомимся с простыми, но мощными возможностями среды проектирования по созданию СУБД. Нам не придется писать ни строчки кода, все через настройки среда сделает за нас.
Добавьте к решению
командой File/Add/New Project новый проект с именем FormsADO3 и
назначьте его стартовым командой Project/Set as StartUp Project (или
первые два проекта исключите из решения командой Remove и единственный
проект автоматически станет стартовым)
Командой Data/Show Data Sources откройте
панель Data Sources
Щелкните на ссылке Add New Data Source,
чтобы создать новый источник (поставщик + набор) данных, и настройте его
в соответствии с приведенной на рисунках последовательностью вкладок мастера:
Source

Categories
После щелчка на кнопке Finish в
панели Data Sources в раскрывающемся списке с именем таблицы Categories выберите
элемент DataGridView, с помощью которого будет отображаться таблица Categories на
форме, а после этого схватите в панели Data Sources курсором
пиктограмму таблицы и перенесите ее на форму
Выделите на форме объект DataGridView и
в панели Properties настройте его так| Компонент | Свойство | Значение | |
|---|---|---|---|
DataGridView |
AutoSizeColumnsMode |
Fill |
|
Dock |
Fill |
||
Columns |
CategoryID.HeaderText |
Номер | |
CategoryName.HeaderText |
Название | ||
Description.HeaderText |
Пояснения | ||
Picture.HeaderText |
Рисунки |
Запустите приложение
- получится примерно такой результат
Сравните таблицу в
нашем приложении с той же таблицей, открытой в Microsoft Office Access -
разницы нет
Откройте в текущем
проекте файлы Form1.cs, Form1.Designer.cs, NorthwindDataSet.Designer.cs и
поудивляйтесь, сколько оболочка нагенерировала за нас кода, а мы сами не
написали ни строчки!!! Вот как раз такое программирование называется визуальным (для
ленивых)
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.