Элементы управления на форме — КиберПедия 

Индивидуальные очистные сооружения: К классу индивидуальных очистных сооружений относят сооружения, пропускная способность которых...

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

Элементы управления на форме

2022-09-11 26
Элементы управления на форме 0.00 из 5.00 0 оценок
Заказать работу

Потребитель данных

Роль потребителя данных на локальном компьютере играет класс DataSet из пространства System.Data. Он реализует функциональность отсоединенной от источника реляционной структуры данных. Возможности, предоставляемые классом, не зависят от природы источника данных и способа, которым они получены. Наборы данных (Datasets) хранят слепок базы данных в отсоединенном кэше. Структура набора сходна со структурой исходной реляционной базы данных. Она представляет собой иерархическую модель таблиц, связей и ограничений (constraints).

Класс DataSet реализует функциональность реляционной структуры данных в памяти со встроенной поддержкой языка XML (Extensible Markup Language). С объектами класса DataSet весьма удобно работать в отсоединенном режиме, так как они позволяют хранить свои данные на постоянном носителе в формате XML. DataSet содержит в себе коллекцию объектов класса DataTable. Каждый объект класса DataTable содержит либо некий снимок (snapshot) реальной таблицы базы данных, либо данные, полученные из какого-то другого источника (XML-документа, динамической коллекции, массива). На начальной стадии изучения целесообразно генерировать данные DataSet на основе стандартных коллекций, или обычного массива.

Благодаря умению хранить данные в формате XML и передавать их в этом формате между компонентами многослойного (multi-tier), распределенного приложения, DataSet является идеальным решением проблемы передачи данных между программами, работающими на разных платформах.

Однако классы DataSet и DataTable имеют ограниченные возможности. Для выбора и фильтрации данных вы можете использовать метод Select, а для навигации по иерархии связанных таблиц существуют методы: GetChildRows и GetParentRow. Для более сложных манипуляций с данными необходимо писать запросы на языке SQL (команды). Это сильно усложняет процесс программирования и снижает производительность приложений.

Более современный подход состоит в применении технологии LINQ to DataSet, которую разработал Anders Hejlsberg. Она впервые появилась в VS 2008. Аббревиатура LINQ означает Language-Integrated Query. Вот пример оператора LINQ.

var q = from c in db.Customers

from o in c.Orders

where c.City == "London"

select new { c.ContactName, o.OrderDate };

Использование LINQ to DataSet резко повышает производительность труда программиста, так как Visual Studio IDE обеспечивает мгновенную проверку синтаксиса, статическое определение типов и механизм подсказок (IntelliSense). Введение в эту технологию вы можете найти в документе Секреты LINQ.

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

DataSet
Tables
Table
Columns
Column
Rows
Row
Constraints
Constraint
Relations
Relation  
DataViewManager
DataViewSettingsCollection
DataViewSetting  
ExtendedProperties
PropertyCollection
DictionaryEntry

Свойства DataViewManager и ExtendedProperties дают доступ к коллекциям настроек. Они хранятся для каждой таблицы набора данных DataSet.

· Первая коллекция позволяет управлять такими свойствами, как режим сортировки строк таблицы, или фильтр отбора данных, передаваемых из DataSet в DataView. Класс DataView управляет образом таблиц. Например, он может отсортировать строки таблицы, отобрать измененные строки, добавленные, или удаленные.

· Вторая коллекция позволяет добавить к таблице произвольные, заданные вами, свойства.

DataViewSettingsCollection представляет собой типизированную коллекцию, работающую по принципу списка, а PropertyCollection работает по принципу HashTable и хранит пары типа DictionaryEntry.

Поставщики данных

Связь с базой данных поддерживается за счет группы функциональных объектов, называемой поставщиками данных (.NET Data Providers). Их совместная работа позволяет управлять эффективностью процесса обмена данными.

· Connections — классы, осуществляющие соединение с источником данных,

· DataAdapters — классы, осуществляющие перенос данных из реальных таблиц базы данных в коллекцию таблиц (объектов типа DataTable), поддерживаемых классом DataSet.

· Commands — классы, реализующие выполнение команд (SQL-запросов или хранимых процедур) при обращении к базе данных.

· DataReaders — классы, осуществляющие выборку данных из базы в безопасном режиме (read-only).

Эти объекты работают в случае, когда источником данных является реальная база данных, а не коллекция, массив или XML-файл. Сейчас мы рассмотрим случай, когда источником данных является не база  данных, а XML-файл. Знакомство с другими поставщиками данных пока отложим. Далее в курсе мы покажем, как связать DataSet с реальной таблицей базы данных, созданной в архитектуре OLEDB или SQL Server.

Источник данных — XML-файл

Рассмотрим приемы работы с объектами классов ADO.NET: DataSet, DataTable, DataColumn, DataRow и DataRowView, а также элементом табличного отображения данных — DataGridView, который на сей раз будет связан не с коллекцией типа BindingList<Person>, как было в предыдущем семестре, а с набором данных, управляемым классом DataSet. Рассмотрим ситуацию, в которой необходимо управлять данными двух связанных таблиц Студенты и Экзамены, как показано на рисунке.

Множество студентов будем хранить в объекте класса DataTable. Множество экзаменов будем хранить в другом объекте этого же класса. Класс DataTable определен в пространстве имен System.Data. Он является стержнем дерева классов подсистемы ADO.NET, обладает универсальными возможностями, позволяя оперировать произвольными данными в памяти. Объекты класса DataTable содержат коллекции строк (DataRow) и колонок (DataColumn), они активно используются другими классами, такими как: DataSet (отображение в памяти всей базы данных) и DataView (образ таблицы).

· Создайте новый проект типа Windows Forms c именем StudExam, переименуйте файл и класс (Form1 в FormMain),

· В строке Text свойств формы задайте значение Students DataSet, установите свойство Size=746; 500.

Меню

Наше приложение обойдется без меню (вместо него мы используем ToolStrip), поэтому советую пропустить этот параграф. Но если вы хотите узнать, как работать с классами, реализующими функциональность меню, то следуйте таким путем. Добавьте компонент MenuStrip, идентифицируйте его как mainMenu, в правом верхнем углу отыщите кнопку Smart button (см. на следующем рис. ), нажмите ее и выберите команду Insert standard Items.

Откройте пункт меню Edit и добавьте две команды &Find и ToolStr i pSeparator, как показано ниже.

Как устроены планки меню, статуса и панелей инструментов

Объект MenuStrip имеет вложенную коллекцию Items объектов типа ToolStripItem.

· Класс ToolStripItem — абстрактный, от него происходят классы: ToolStripControlHost, ToolStripDropDownItem, ToolStripLabel, ToolStripStatusLabel, ToolStripButton и ToolStripSeparator.

· ToolStripControlHost является базовым для классов: ToolStripComboBox, ToolStripTextBox, ToolStripProgressBar.

· Класс ToolStripDropDownItem — абстрактный. Он порождает классы: ToolStripDropDownButton, ToolStripMenuItem, ToolStripSplitButton.

Как видите, в обычное меню (а также в контекстное меню—ContextMenuStrip, панель управления—ToolStrip и планку статуса — StatusStrip) можно добавлять кнопки, выпадающие кнопки, текстовые метки, выпадающие списки, поля ввода, индикаторы прогресса. В них можно добавить и любой другой элемент управления (например, календарь), но для этого надо немного потрудиться — разработать код класса, производного от ToolStripControlHost.

Наша планка меню MenuStrip mainMenu содержит коллекцию ссылок на объекты ToolStripMenuItem. Каждый такой объект, в свою очередь, содержит коллекцию ссылок DropDownItems на объекты типа ToolStripMenuItem. Они являются ссылками на реальные или виртуальные команды меню. Каждый объект класса ToolStripMenuItem способен породить дерево вложенных объектов (меню), пока вам не надоест вкладывать меню в меню и вы не остановитесь на каком-то уровне. Объекты этого, последнего уровня и будут рабочими командами меню.

Планка инструментов (ToolStrip)

Вместо элемента Toolbar, который работал в.NET Framework 1.1, используем элемент типа ToolStrip. Добавьте такой элемент с идентификатором toolStrip.

В свою внутреннюю коллекцию Items типа ToolStripItemCollection ToolStrip позволяет добавлять кнопки и некоторые другие элементы управления. Вы уже догадались, что кнопка с подсказкой Add ToolStripButton, автоматически появляющаяся на поверхности панели в режиме дизайна, служит для создания и добавления в коллекцию новых элементов. Используйте ее для вставки кнопок, меток, выпадающего списка и полей ввода. Вы можете, как и в случае с меню, воспользоваться Smart button () и добавить набор стандартных кнопок, а затем удалить ненужные и добавить недостающие элементы. Все элементы управления должны быть идентифицироваы в поле (Name) окна свойств выделенного элемента.

· Присвойте кнопкам (см. рис. выше) имена: btnOpen, btnSave, btnFindNext, btnFindList, btnFindView и btnAverage.

· Добавьте в ToolStrip текстовые метки (элементы типа ToolStripLabel см. рис. выше). Установите для них значения свойств Name (lblFind, lblAvgLo, lblAvgHi), и Text (Find:, Average low:, hi:).

· Добавьте выпадающий список (ToolStripComboBox) с именем comboFind. Он будет помнить искомые строки текста.

· Добавьте два элемента типа ToolStrip Text Box с именами: t xt AvLo и t xt AvgHi. Они будет использоваться для задания верхней и нижней границ поиска студентов по среднему баллу.

Навигатор связей

Навигатор связей (элемент типа BindingNavigator) нам не нужен, поэтому советую пропустить этот параграф. Но если вы хотите знать, как работать с такими элементами управления, то добавьте его на форму. Замените длинное имя навигатора на bn. Так как по умолчанию навигатор улегся на ToolStrip и закрыл ее, то измените значение его свойства Dock на константу перечисления DockStyle.Bottom (см. нижнюю часть формы), или манипулируйте настройками элементов формы так, чтобы и ToolStrip и BindingNavigator были видимы (не закрывали друг друга). Вы можете также переместить все элементы управления из ToolStrip в BindingNavigator и удалить ToolStrip, так как BindingNavigator способен его полностью заменить.

Пояснения

· В методе AddStyle мы управляем множеством колонок таблицы, которые отображает grid, а также компонентами его стиля. Здесь опять главную роль играет механизм DataBinding (привязка одного свойства к другому), он включается оператором col.DataPropertyName = dc.ColumnName;.

· Индекс таблицы DataSet, которая отображается текущим grid передается параметром. Мы используем его для выбора нужной таблицы, а также для смены цвета фона перемежающихся строк (AlternatingRows).

· Заметьте, что для колонки Credit (зачет) вместо объекта DataGridViewTextBoxColumn, мы используем объект класса DataGridViewCheckBoxColumn. Найдите в MSDN справку по всем классам, производным от DataGridViewColumn, и проанализируйте их функциональность. Среди них нет класса CalendarColumn (мы добавили его вручную).

Довольно часто некоторые поля данных надо скрыть, не отображать их на форме (например, поля первичных автоинкрементируемых ключей). Для этого просто не включайте эти поля в множество колонок (объектов DataGridViewColumn), которое добавляется в DataGridView (см. код grid.Columns.Add(col);). В данный момент вы можете добавить код, генерирующий данные таблицы экзаменов, и привязать к ней второй grid (gridExam).

Запись и чтение данных

Класс DataSet значительно упрощает операции записи и чтения данных. Он имеет множество встроенных свойств и методов, поддерживающих управление данными в формате XML. Начнем с метода записи данных WriteXml.

void Save(string fn)

{

if (fn!= null && ds.Tables[0].Rows.Count!= 0)

{

ds.WriteXml(fn);

SetPath(fn);

}

}

Метод SetPath корректирует значения переменных dirName и fileName.

void SetPath(string fn)

{

Text = fn;

dirName = Path.GetDirectoryName(fn);

fileName = Path.GetFileName(fn);

}

Имя файла fn получим от пользователя с помощью метода FileDlg.

string FileDlg(bool bOpen)

{

FileDialog dlg = bOpen? (FileDialog)new OpenFileDialog(): (FileDialog)new SaveFileDialog();

dlg.InitialDirectory = dirName;

dlg.Filter = "XML files (*.xml)|*.xml";

dlg.FileName = fileName;

return dlg.ShowDialog() == DialogResult.OK? dlg.FileName: null;

}

Для обеспечения видимости классов попросите студию вставить недостающие директивы using. Чтение набора данных ds из XML-файла выполним в методе Open.

void Open(string fn)

{

if (fn!= null)

{

ds.Clear();

ds.ReadXml(fn);

SetPath(fn);

ds.AcceptChanges();

}

}

Отметьте, что при чтении данных из файла DataSet рождается заново, поэтому старые данные необходимо уничтожить (см. вызов Clear). Создайте папку Data, запустите приложение и сохраните данные в файле Studs.xml. Теперь можно закомментировать строку вызова InitTables и проверить поведение приложения при открытии файла с данными.

Добавляем ограничение

Если мы хотим не только просматривать данные, но и вносить изменения, то для сохранения целостности по ссылкам, необходимо добавить код, создающий объект класса ForeignKeyConstraint. Он задает ограничение, накладываемое на данные обеих таблиц. Ограничение (constraint) типа FK-PK (ForeignKey-PrimaryKey) следит за тем, чтобы не нарушалась целостность данных по ссылкам. Следующий фрагмент надо добавить в метод RelateAndBind (до создания DataRelation).

ForeignKeyConstraint cs = new ForeignKeyConstraint("SE", ds.Tables[0].Columns[0], ds.Tables[1].Columns[1]);

cs.DeleteRule = Rule.Cascade;

cs.UpdateRule = Rule.Cascade;

ds.Tables[1].Constraints.Add(cs);

ds.EnforceConstraints = true;

Здесь мы создаем объект класса ForeignKeyConstraint, настраиваем его и вставляем в коллекцию Constraints подчиненной таблицы. Последний оператор включает ограничения. Иногда их надо выключать, например, при начальном заполнении таблицы, или при чтении данных из файла. В эти моменты целостность данных по ссылкам (временно) может отсутствовать. По завершении этих операций ограничения следует вновь включить.

Вторая, подчиненная таблица

В класс формы уже имеется массив с именами курсов лекций:

static string[] courses = { "Math", "Mechanics", "Physics", "English", "Oracle", "OpenGL", "History" };

Объект Random rand уже объявлен нами, но еще не создан. Он позволит сгенерировать случайные параметры для строк таблицы экзаменов.

· Создайте объект rand в подходящий момент жизни приложения.

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

Подсказка. Добавьте код внутрь метода InitTables.

foreach (object[] stud in studs)

{

int num = 0;

foreach (string c in courses)

{

if (num++!= rand.Next(courses.Length))

    ds.Tables[1].Rows.Add(CreateRandomExam((int)stud[0], c));

}

}

Доработайте код метода CreateRandomExam.

DataRow CreateRandomExam(int studID, string course)

{

     // Такой способ создания строки надежен, так как новая строка уже имеет определенный нами ранее набор колонок

DataRow row = ds.Tables[1].NewRow();

row[1] = studID;

row[2] = course;

row[3] = // Напишите логическое выражение (сдал-ли студент зачет). Успех должен быть в 90% случаев.

row[4] = 2;

if ((bool)row[3])

{

row[4] = // Используйте rand

row[5] = // Новая дата, отстающаяся от DateTime.Now на случайное число лет (не более 2), случайный месяц и день

}

return row;

}

Привяжите gridExam к данным таблицы Exams. Вы можете выбрать ее из DataSet (ds.Tables[1]). Отладьте приложение так, чтобы оно позволяло работать с данными двух таблиц. Для начала пусть оба компонента DataGridView отображают все данные таблиц, к которым они привязаны.

Результат поиска

Напомним, что поиск производился в DataTable (в памяти), а его результат надо отобразить в DataGridView (на экране). Если поиск (вызов метода Select класса DataTable) был удачным, то показать результат можно путем маркировки в DataGridView той его ячейки, которая соответствует первой (rows[0]) строке из массива (rows) всех строк, найденных в DataTable. Это делается с помощью свойства CurrentCell компонента DataGridView. Заметим, что установка этого свойства провоцирует событие CellEnter — входа фокуса в ячейку DataGridView. Ячейка grid выделяется цветом, управляемым свойством SelectionBackColor класса DataGridViewCellStyle. Доступ к объекту DataGridViewCellStyle дает свойство DefaultCellStyle класса DataGridView.

Чтобы все прошло гладко, нам осталось вычислить целочисленный индекс той строки в DataGridView, которая соответствует первому элементу в массиве строк, найденных в DataTable. Простое действие — определение индекса, казалось бы не должно вызвать затруднений. Но именно оно вынуждает напряженно искать решение.

Дело в том, что результатом отбора (метода Select) является массив ссылок на объекты класса DataRow, содержащиеся во внутренней коллекции объекта DataTable. Порядок элементов в этом массиве никак не связан с порядком отображения строк таблицы в DataGridView.

Вы заметили, что отображаемые строки можно сортировать самыми разными способами, если последовательно нажимать мышью заголовки всех столбцов элемента DataGridView? Эта внешняя сортировка никак не влияет на то значение, которое дает выражение rows[0][0]. Оно всего лишь выбирает значение, расположенное в нулевом столбце той строки DataTable, индекс которой записан в элементе rows[0]. Нулевой столбец соответствует полю ID и мы узнаем ключ первой из найденных записей. Если строки DataGridView в данный момент отсортированы по этому столбцу, то результат поиска будет показан (маркирован) правильно, если по другому — нет.

В процессе отбора строк из DataTable не учитывается факт внешней сортировки, так как эта структура расположена в памяти и не сортируется при нажатии на заголовки DataGridView. Напомним, что компонент DataGridView лишь отображает объекты.

Поиск в DataGridView

Анализ поведения приложения в режиме поиска, наводит на мысль о том, что целесообразно производить поиск не в таблице, поддерживаемой классом DataTable, а непосредственно в компоненте DataGridView. Хотелось бы сделать поиск более удобным.

· Продолжать поиск со строки, следующей за той, которая в данный момент содержит фокус.

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

· Пользователь должен иметь возможность искать в любой колонке как gridStud, так и gridExam.

· Измените алгоритм метода Find.

void Find()

{

string colName = // Определите имя колонки (используйте индексы: searchTableID и searchColumnID

try

{

string text = Trim(comboFind.Text); // Искомый текст

if (findWhat!= text)

findWhat = text;

 

if (bFindNext)

searchRowID++;

if (searchRowID < 0 || grids[searchTableID].Rows.Count <= searchRowID)

searchRowID = 0;

if (FindNext())

{

if (!comboFind.Items.Contains(comboFind.Text))

    comboFind.Items.Add(comboFind.Text);

bFindNext = true;

return;

}

else

throw new Exception();

}

catch { }

SetErrorMsg(findWhat, colName);

}

· Доверьте IntelliSense создание заготовки метода SetErrorMsg, затем введите код, как показано ниже.

void SetErrorMsg(string criteria, string colName)

{

string msg = "Could not find: '" + criteria + "'";

msg += colName!= null? " in column: " + colName: ". Select a column to search";

new FormMsg(msg, 2000);

}

· Добавьте метод FindNext.

bool FindNext()

{

bool bFound = false;

DataGridView grid = grids[searchTableID];

for (int i = searchRowID; findWhat.Length > 0 &&!bFound;) // Цикл поиска

{

object o = grid[searchColumnID, i].Value; // ячейка в строке i и столбце searchColumnID

if (o!= null)

{

string val = o.ToString();

if (Если текст в ячейке начинается с искомой строки)

{

    bFound = true;

    grid.CurrentCell = grid[searchColumnID, i];

    // Выделите всю строку grid

    // Запомните индекс строки в переменной searchRowID

    break;

}

}

if (i == grid.RowCount - 1)

i = -1;

i++;

if (i == searchRowID)

break;

}

return bFound;

}

Проверка показывает удовлетворительное поведение приложения в режиме поиска, но здесь, конечно, остались подводные камни. Чтобы увидеть их, надо связать DataGridView с реальной таблицей базы данных, которая может содержать тысячи строк.

Задание

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

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

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

Детали

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

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

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

key.OpenSubKey("StudentsSet", true);

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

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

void DoLoad()

{

SetDesktopLocation(20, 20);

InitDataSet();

 

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

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

Open(fileName);

else

InitTables();

FillSearchBox();

 

RelateAndBind();

//...

}

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

fileName = Settings.Default.LastFile;

if (fileName!= null)

Open(fileName);

else

InitTables();

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

Settings.Default.Save();

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

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

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

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

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

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

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

     // Чтение DataSet

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

view.RowStateFilter = DataViewRowState.Added;

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

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

{

DataRowView rv = view[i];

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

}

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

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

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

{

AllowNew = false,

AllowEdit = false,

AllowDelete = false

};

viewGrid.DataSource = view;

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

Задание

Реализуйте реакцию на нажатие кнопки btnFindView. Создайте образ выбранной таблицы (DataView). Образ должен быть отфильтрован с учетом критерия поиска, заданного в строке comboFind. В процессе обработки события (программным способом) создайте DataGridView grid, поместите его в форму formFound и привяжите его к данным образа таблицы DataView. Для объекта grid добавьте обработку события CellEnter. В реакции на это событие выделяйте цветом те строки таблиц главного окна, которые соответствуют выделенной строке DataGridView вспомогательного окна formFound.

Например, если grid в окне formFound отображает результаты экзаменов и в нем активна строка, соответствующая ID=17 studID=4, то в gridStud и gridExam должны быть выделены строки, соответствующие studID=4 и ID=17, как показано на рисунке. Перемещение фокуса вверх и вниз по строкам grid в форме formFound должно отслеживаться в обеих таблицах главного окна. Таким образом, пользователь видит как данные студента, экзамен которого отбражен в выделенной строке grid (окна поиска), так и информацию о самом экзамене.

Все предыдущие режимы поиска должны работать, как и прежде. Вспомогательная форма должна создаваться вновь при переключении режимов поиска (ListView или DataGridView). Стиль вспомогательной таблицы grid должен изменяться в зависимости от типа активной таблицы в главном окне.

Потребитель данных

Роль потребителя данных на локальном компьютере играет класс DataSet из пространства System.Data. Он реализует функциональность отсоединенной от источника реляционной структуры данных. Возможности, предоставляемые классом, не зависят от природы источника данных и способа, которым они получены. Наборы данных (Datasets) хранят слепок базы данных в отсоединенном кэше. Структура набора сходна со структурой исходной реляционной базы данных. Она представляет собой иерархическую модель таблиц, связей и ограничений (constraints).

Класс DataSet реализует функциональность реляционной структуры данных в памяти со встроенной поддержкой языка XML (Extensible Markup Language). С объектами класса DataSet весьма удобно работать в отсоединенном режиме, так как они позволяют хранить свои данные на постоянном носителе в формате XML. DataSet содержит в себе коллекцию объектов класса DataTable. Каждый объект класса DataTable содержит либо некий снимок (snapshot) реальной таблицы базы данных, либо данные, полученные из какого-то другого источника (XML-документа, динамической коллекции, массива). На начальной стадии изучения целесообразно генерировать данные DataSet на основе стандартных коллекций, или обычного массива.

Благодаря умению хранить данные в формате XML и передавать их в этом формате между компонентами многослойного (multi-tier), распределенного приложения, DataSet является идеальным решением проблемы передачи данных между программами, работающими на разных платформах.

Однако классы DataSet и DataTable имеют ограниченные возможности. Для выбора и фильтрации данных вы можете использовать метод Select, а для навигации по иерархии связанных таблиц существуют методы: GetChildRows и GetParentRow. Для более сложных манипуляций с данными необходимо писать запросы на языке SQL (команды). Это сильно усложняет процесс программирования и снижает производительность приложений.

Более современный подход состоит в применении технологии LINQ to DataSet, которую разработал Anders Hejlsberg. Она впервые появилась в VS 2008. Аббревиатура LINQ означает Language-Integrated Query. Вот пример оператора LINQ.

var q = from c in db.Customers

from o in c.Orders

where c.City == "London"

select new { c.ContactName, o.OrderDate };

Использование LINQ to DataSet резко повышает производительность труда программиста, так как Visual Studio IDE обеспечивает мгновенную проверку синтаксиса, статическое определение типов и механизм подсказок (IntelliSense). Введение в эту технологию вы можете найти в документе Секреты LINQ.

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

DataSet
Tables
Table
Columns
Column
Rows
Row
Constraints
Constraint
Relations
Relation  
DataViewManager
DataViewSettingsCollection
DataViewSetting  
ExtendedProperties
PropertyCollection
DictionaryEntry

Свойства DataViewManager и ExtendedProperties дают доступ к коллекциям настроек. Они хранятся для каждой таблицы набора данных DataSet.

· Первая коллекция позволяет управлять такими свойствами, как режим сортировки строк таблицы, или фильтр отбора данных, передаваемых из DataSet в DataView. Класс DataView управляет образом таблиц. Например, он может отсортировать строки таблицы, отобрать измененные строки, добавленные, или удаленные.

· Вторая коллекция позволяет добавить к таблице произвольные, заданные вами, свойства.

DataViewSettingsCollection представляет собой типизированную коллекцию, работающую по принципу списка, а PropertyCollection работает по принципу HashTable и хранит пары типа DictionaryEntry.

Поставщики данных

Связь с базой данных поддерживается за счет группы функциональных объектов, называемой поставщиками данных (.NET Data Providers). Их совместная работа позволяет управлять эффективностью процесса обмена данными.

· Connections — классы, осуществляющие соединение с источником данных,

· DataAdapters — классы, осуществляющие перенос данных из реальных таблиц базы данных в коллекцию таблиц (объектов типа DataTable), поддерживаемых классом DataSet.

· Commands — классы, реализующие выполнение команд (SQL-запросов или хранимых процедур) при обращении к базе данных.

· DataReaders — классы, осуществляющие выборку данных из базы в безопасном режиме (read-only).

Эти объекты работают в случае, когда источником данных является реальная база данных, а не коллекция, массив или XML-файл. Сейчас мы рассмотрим случай, когда источником данных является не база  данных, а XML-файл. Знакомство с другими поставщиками данных пока отложим. Далее в курсе мы покажем, как связать DataSet с реальной таблицей базы данных, созданной в архитектуре OLEDB или SQL Server.

Источник данных — XML-файл

Рассмотрим приемы работы с объектами классов ADO.NET: DataSet, Da


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

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

История развития пистолетов-пулеметов: Предпосылкой для возникновения пистолетов-пулеметов послужила давняя тенденция тяготения винтовок...

Биохимия спиртового брожения: Основу технологии получения пива составляет спиртовое брожение, - при котором сахар превращается...

Семя – орган полового размножения и расселения растений: наружи у семян имеется плотный покров – кожура...



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

0.238 с.