Практическая работа по теме 3. "Наборы данных" Передача данных из источника данных в объект DataSet с помощью объекта DataAdapter. Обновление данных в связанных таблицах. Отображение данных: объект DataView. Привязка данных в формах Windows. Оглавление Задание Задание Задание Задание 1. 2. 3. 4. Передача данных из источника данных в DataSet Обновление данных в связанных таблицах Отображение таблиц и полей: объект DataView Привязка данных в формах Windows Задание 1. Определение метаданных для объекта DataTable Цель Научиться управлять процессом передачи данных от источника данных к DataSet. Решение Изучить объект модели ADO.NET DataAdapter, его свойства и методы. Обсуждение Объект DataAdapter является промежуточным звеном между объектом DataSet и физическим источником данных. Он играет роль связующего звена между двумя компонентами ADO.NET: подключенным к источнику данных компонентом провайдеров данных .NET и неподключенным к источнику данных компонентом объектов DataSet. В структуре провайдеров данных .NET объект DataAdapter занимает центральное место. Каждый провайдер имеет собственную реализацию объекта DataAdapter, например, SqlDataAdapter, OledbDataAdapter, OdbcDataAdapter. На рис. 3.1. приведена схема использования объекта DataAdapter в модели ADO.NET, его свойств и методов. Основным назначением объекта DataAdapter является управление процессом передачи данных от источника данных к объекту DataSet и от объекта DataSet к источнику данных. Это выполняется с помощью небольшого набора свойств и методов, которые мы должны изучить. Основными методами объекта DataAdapter являются Fill и Update. Метод Fill наполняет объект DataAdapter данными, полученными от источника данных, а метод Update обновляет источник данных в соответствии с изменениями данных в таблицах объекта DataSet. Объект DataAdapter содержит набор из четырех свойств: SelectCommand, InsertCommand, UpdateCommand и DeleteCommand, которые являются объектами ADO.NET типа Command, сконфигурированными для выполнения одноименных операций. Например, объект SelectCommand выполняется при вызове метода Fill объекта DataAdapter, а остальные объекты выполняются для каждой измененной записи при вызове метода Update объекта DataAdapter. Рис. 3.1. Схема работы объекта DataAdapter как связующего звена между источником данных и объектом DataSet Таким образом, разработчики базы данных обладают полным контролем над этими командами, которые позволяют настраивать команды, используемые для операций вставки, обновления и удаления. Наконец, коллекция TableMappings объектов DataTableMapping позволяет указать соответствие между таблицей источника данных и объектом DataTable. Для использования объекта DataAdapter требуется подключение к базе данных и команда Select (это минимальный набор средств и методов). Для этого можно использовать объекты Connection и Command, то есть создать их, сконфигурировать и присвоить их свойства Connection и SelectCommand объекту DataAdapter. Private daEmployees As New SqlDataAdapter Dim cnn As New SqlConnection("server=Persist Security Info=False;Integrated Security=SSPI; database=HumanResources; server=abrzh") daEmployees.SelectCommand = New SqlCommand daEmployees.SelectCommand.Connection = cnn daEmployees.SelectCommand.CommandText = "SELECT EmployeeID, FirstName" Можно воспользоваться другим приемом - использовать конструктор объекта DataAdapter с двумя строковыми параметрами: один для команды SELECT, а второй для строки подключения, как показано ниже. Dim daDep As SqlDataAdapter = New SqlDataAdapter ("select * from Departments", "server=Persist Security Info=False;Integrated Security=SSPI; database=EmployeeInfo; server=abrzh") Попробуем теперь вызвать метод Fill для извлечения данных из базы данных EmployeeInfo и загрузки их в объект DataSet. 1. Создайте базу данных EmployeeInfo средствами Visual Studio.Net в SQL Server. Модель базы данных представлена на рис. 3.2. Рис. 3.2. Модель базы данных EmployeeInfo 2. Установите связи и заполните таблицы необходимой информацией. 3. Откройте форму "Data Set" проекта к практикуму по теме "Отсоединенная модель программирования". 4. Создайте новую кнопку DataAdapterFill. Вставьте в процедуру обработки щелчка по командной кнопке код, приведенный ниже. Не забудьте импортировать необходимые пространства имен: Imports System.Data, System.Data.SqlClient Private Sub Button7_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button7.Click ReadData() End Sub Код процедуры общего назначения для чтения данных из источника данных ReadData(): Private Sub ReadData() Dim rows As Integer Dim daDep As SqlDataAdapter = New SqlDataAdapter("select * from Departments", "server=Persist Security Info=False; Integrated database=EmployeeInfo; dsE = New rows = daDep.Fill(dsE, DisplayDataset(dsE) End Sub Security=SSPI; server=abrzh") DataSet "Departments") После создания объекта daDep с двумя аргументами, которые содержат команду Select и строку подключения, вызывается метод Fill для наполнения данными таблицы Departments набора данных dsE. Метод Fill также возвращает количество записей, которые включены (или обновлены) в набор данных dsE. При выполнении метода Fill провайдером данных неявно выполняются следующие действия: Для объекта SelectCommand открывается подключение к источнику данных, если оно еще не открыто. Выполняется команда, указанная в свойстве CommandText объекта SelectCommand (вместе с параметрами, если таковые имеются). Создается объект DataReader для возвращения имен полей и типов, использованных для создания нового объекта DataTable в указанном наборе данных DataSet, если этого объекта еще не существует. Объект DataReader используется для извлечения данных и вставки их в таблицу. Объект DataReader закрывается. Подключение к источнику данных закрывается, если оно было открыто объектом DataReader, в противном случае оно остается открытым. При выполнении одной команды по отношению к источнику данных (наш с вами случай) обычно проще и эффективнее создавать объекты Command и Connection неявно при создании объекта DataAdapter (именно так мы с вами и поступили). Однако при выполнении нескольких команд по отношению к одному источнику данных эффективнее создавать объект Connection явно и затем присвоить его объекту DataAdapter. Это позволяет поддерживать подключение постоянно открытым, без часто повторяющихся операций его открытия и закрытия, что снижает производительность. Эквивалентный код представлен ниже. Private Sub ReadData1() Dim rows As Integer Dim daDep As New SqlDataAdapter Dim cnn As New SqlConnection("server=Persist Security Info=False; Integrated Security=SSPI; database=EmployeeInfo; server=abrzh") Dim cmdSelect As New SqlCommand("select * from Departments") dsE = New DataSet cmdSelect.Connection = cnn daDep.SelectCommand = cmdSelect cnn.Open() rows = daDep.Fill(dsE, "Departments") DisplayDataset(dsE) cnn.Close() End Sub Конечно, для эффективного использования явно созданных объектов cnn и cmdSelect желательно, чтобы количество операций с базой данных было достаточно большим. Еще одно дополнение к написанному коду. Методу Fill мы передаем ссылку на набор данных dsE и имя таблицы Departments, в которую вставляются данные: rows = daDep.Fill(dsE, "Departments") Возможны другие варианты вызова метода Fill. Вместо имени таблицы можно было бы передать ссылку на объект DataTable или передать только ссылку на объект DataSet, а метод Fill в таком случае по умолчанию загрузит данные в объект DataTable по имени Table. rows = daDep.Fill(dsE) Для указания информативных имен таблиц Table, Table1 и так далее, используется коллекция объектов DataTableMapping и метод Add: daDep.TableMappings.Add("Table", "Отделы") daDep.TableMappings.Add("Table1", "Сотрудники") Самостоятельно Добавьте в процедуру ReadData() описание еще одно адаптера с другой командой Select для загрузки данных из таблицы Employees. Скомпонуйте проект, щелкните по кнопке DataAdapterFill, в поле со списком, как и прежде, будет отображена информация о содержании набора данных dsE, но теперь она извлекается из базы данных под управлением SQL Server, а не генерируется локально кодом приложения (рис. 3.3). Рис. 3.3. Данные из таблиц базы данных, управляемой SQL Server (не из локальной версии, как ранее) Для установления родительско-дочерних связей между записями в этих таблицах можно создать объект DataRelation, который служит отношением между ними. dsE.Relations.Add("D_E", dsE.Tables("Departments").Columns("ID"), dsE.Tables("Employees").Columns("DepartmentID")) Задание 2. Обновление данных в связанных таблицах Цель Сохранить все изменения в источнике данных в соответствии с изменениями в таблицах набора данных DataSet. Решение Использовать метод Update объекта DataAdapter,. который анализирует изменения в указанной таблице набора данных (или сразу во всех таблицах, если ни одна из них не указана явно). Обсуждение Для каждой измененной записи по отношению к источнику данных выполняется команда вставки, обновления или удаления с помощью соответствующего объекта InsertCommand, UpdateCommand или DeleteCommand. Каждая измененная запись обновляется отдельно, а не как часть транзакции или пакетной операции. Причем порядок обновления записей определяется порядком их расположения в объекте DataTable. Для явного управления порядком выполнения операций для заданной таблицы можно использовать метод GetChanges, который доступен как на уровне объекта DataSet, так и на уровне объекта DataTable. Этот метод используются для извлечения отдельных наборов записей с разными состояниями записей. Предположим, мы обновляем нашу базу данных EmployeesInfo данными из объекта ds, передавая их в базу данных с помощью объекта da. Причем сначала требуется выполнить все удаления, затем все обновления, а потом все вставки записей. Это можно сделать, трижды вызывая метод GetChanges с указанием соответствующих разных состояний записей. После каждого вызова метода GetChanges вызывается метод Update объекта DataAdapter с передачей объекта DataTable, возвращенного методом GetChanges. Dim dt = ds.Tables("Table") Dim tab As New DataTable tab = dt.GetChanges(DataRowState.Deleted) If Not tab Is Nothing Then da.Update(tab) End If tab = dt.GetChanges(DataRowState.Modified) If Not da.Update(tab) End tab = If Not da.Update(tab) End If tab Is Nothing Then If dt.GetChanges(DataRowState.Added) tab Is Nothing Then Объект DataAdapter не создает автоматически команды INSERT, UPDATE и DELETE для обновления источника данных в соответствии с изменениями данных в объекте DataSet. Эти команды можно указать одним из следующих способов: Использовать объект CommandBuilder для автоматической генерации команд во время выполнения приложения; Явно запрограммировать эти команды; Использовать компонент DataAdapter Design-Time Component и мастер конфигурирования объекта DataAdapter Configuration Wizard. Рассмотрим использование объекта CommandBuilder - это самый простой способ выполнения команд INSERT, UPDATE и DELETE. Следует иметь в виду, что объект CommandBuilder имеет некоторые ограничения в использовании, так, например, он не подлежит конфигурированию (настройке), предназначен только для одной таблицы, не учитывает связи таблицы с другими таблицами. 1. Создайте в проекте новую форму, разместите на ней элемент DataGrid для отображения записей таблиц и две командные кнопки Fill и Update (рис. 3.4). 2. В верхней части кода выполните импортирование следующих пространств имен: Imports Imports System.Data.SqlClient System.Data 3. Для каждой из кнопок введите соответствующий код, представленный ниже. Dim cnn As New SqlConnection("Persist Security Info=False;Integrated Security=SSPI;database=EmployeeInfo;server=abrzh") Dim ds As DataSet 'Код обработки щелчка по кнопке Fill Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim da As New SqlDataAdapter("select * from Departments", cnn) DataGrid1.DataSource = Nothing ds = New DataSet da.Fill(ds, "Departments") da = New SqlDataAdapter("select * from Employees", cnn) da.Fill(ds, "Employees") 4. 'Установление родительско-дочерних отношений между таблицами. ds.Relations.Add("Departments_Employees", ds.Tables("Departments").Columns("ID"), ds.Tables("Employees").Columns("DepartmentID")) DataGrid1.DataSource = ds End Sub Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click 'Код обработки щелчка по кнопке Update 5. 'Создание адаптеров данных Dim daDep As New SqlDataAdapter("select * from Departments", cnn) Dim daEmp As New SqlDataAdapter("select * from Employees", cnn) Dim cbDep As New SqlCommandBuilder(daDep) Dim cbEmp As New SqlCommandBuilder(daEmp) 6. 'Внесение изменений в таблицы в "правильном" порядке Dim tab As New DataTable 'Удаление записей в дочерней таблице tab = ds.Tables("Employees").GetChanges(DataRowState.Deleted) If Not tab Is Nothing Then daEmp.Update(tab) End If 7. 'Все измененные записи в родительской таблице. tab = ds.Tables("Departments").GetChanges If Not tab Is Nothing Then daDep.Update(tab) End If 8. 'Новые или измененные записи в дочерней таблице. tab = ds.Tables("Employees").GetChanges(DataRowState.Added Or DataRowState.Modified) If Not tab Is Nothing Then daEmp.Update(tab) End If End Sub Рис. 3.4. Отображение связанных таблиц с помощью элемента управления DataGrid Самостоятельно Скомпонуйте проект Departments and Employees и проверьте полученное приложение, выполнив перечисленные ниже действия: Запустите полученное приложение и щелкните на кнопке Fill. Это должно привести к вставке данных в объект DataSet из базы данных EmployeeInfo. Однако строка кода DataGrid1.DataSource = ds связывает с сеткой весь объект DataSet, а не какую-то одну таблицу DataTable. Поэтому сетка содержит раскрывающийся список (рис. 3.4). Щелкните на пиктограмме "плюс" и раскройте список ссылок на две таблицы объекта DataSet. Раскройте таблицу Departments, введите новые данные, удалите или измените имеющиеся строки. Проверьте изменения в источнике, для этого нажмите кнопку Update, затем снова нажмите кнопку Fill. Проделайте аналогичные изменения с записями таблицы Employees. Успешное удаление записи из родительской таблицы Departments вместе с ее дочерними записями в таблице Employees возможно благодаря заданному по умолчанию ограничению ForeignConstraint для отношения Department_Employees. Ограничение заключается в каскадном обновлении (удалении) данных в родительской и дочерней таблицах. Задание 3. Отображение таблиц и полей: объект DataView Цель При проектировании интерфейса пользователя научиться изменять способ отображения данных из таблиц набора данных DataSet. Решение Использовать объект DataView, позволяющий создавать разные представления данных для объекта DataTable. Обсуждение Объект DataView обладает следующими свойствами, которые настраивать способ отображения данных из объекта DataTable: позволяют Порядок сортировки (нисходящий или восходящий) по одному или нескольким полям. Выражение для фильтрации записей, которое указывает критерии отображения записей на основе значений полей. Фильтр состояния записей, который указывает критерии отображения записей на основе состояния записи. Объект DataView - полностью динамическое представление данных, то есть все изменения в таблице-источнике немедленно отображаются в объекте DataView. Каждый объект DataView является представлением только одной таблицы и не может быть объединением нескольких таблиц. Не смотря на то, что объект DataView очень похож на таблицу, он не может использоваться как таблица, в нем нельзя исключать поля, которые присутствуют в таблице-источнике, не возможно включать дополнительные поля, например, вычисляемые поля, которых нет в таблице-источнике. Другими словами, объект DataView является представлением таблицы, но не самой таблицей. У каждой таблицы может быть сколько угодно представлений и для всех этих представлений таблица является источником полей и строк. Объекты DataView могут быть созданы двумя способами. Первый способ представляет собой свойство DefaultView непосредственно таблицы-источника. Предположим, нам нужно создать представление для таблицы Employees базы данных EmployeeInfo, причем сотрудники в этом представлении должны быть упорядочены по номеру отдела и получать заработную плату более 10000 рублей. Для этого нужно использовать следующий код: dsEmployees.Tables("Employees").DefaultView.RowFilter="Salary > 10000" dsEmployees.Tables("Employees").DefaultView.Sort="DepartmentID" Если необходимо отобразить в представлении текущие значения только тех записей, которые были изменены (но еще не сохранены), то можно изменить настройки представления: dsEmployees.Tables("Employees").DefaultView.RowFilter=" " dsEmployees.Tables("Employees").DefaultView.RowStateFilter= DefaultView.RowState.ModifiedCurrent Второй способ создания представления для таблицы представляет собой явное создание объекта класса DataView для конкретной таблицы с последующей настройкой свойств (RowFilter, Sort и RowStateFilter) объекта. dvView2 = DataView(dsEmployees.Tables("Employees"), "FirstName", DataViewRowState.CurrentRows) New "", Этой кодовой строкой было создано представление для таблицы Employees, в котором нет фильтра, сортировка по полю FirstName и изображены текущие записи таблицы. Это представление можно изменять, например, установить фильтрацию и сортировку по убыванию: dvView2.RowFilter="FirstName > 'Г'" dvView2.Sort= "FirstName DESC" dvView2.RowStateFilter=DataViewRowState.Current Объект DataView имеет модель редактирования, аналогичную модели редактирования объекта DataTable,. то есть в представлении можно удалять, добавлять, редактировать данные. Рассмотрим теперь, как описанные выше идеи применяются на практике. 1. Создайте в вашем проекте еще одну форму DataView (рис. 3.5). 2. Добавьте в форме сетку данных DataGrid, текстовой поле с надписью Filter, поле со списком Sort by Column, поле со списком Row State, флажок Descending и командную кнопку Apply. 3. Выберите все установленные элементы и скопируйте их в форму еще раз. Обратите внимание на номера элементов управления. 4. Скопируйте код, приведенный ниже в ваш проект. Внесите в него изменения в соответствии с названиями полей таблицы и элементов управления из вашего проекта. 5. Окончательный вид формы DataView показан на рис. 3.5. Imports System.Data Imports System.Data.SqlClient Private dsEmployees As New DataSet Private dvView2 As DataView Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load Dim i As Integer, col As DataColumn Dim daEmployees As SqlDataAdapter = New SqlDataAdapter("SELECT * FROM Employees", "Persist Security Info=False; Integrated Security=SSPI; database=EmployeeInfo; server=abrzh") 'Инициализация объекта DataAdapter daEmployees.Fill(dsEmployees, "Employees") 'Вставка данных только в одну таблицу dvView2 = New DataView(dsEmployees.Tables("Employees"), "", "FirstName", DataViewRowState.CurrentRows) 'Создание объекта DataView 'Вставка списка имен полей в элементы ComboBox For Each col In dsEmployees.Tables("Employees").Columns ComboBox1.Items.Add(col.ColumnName) ComboBox4.Items.Add(col.ColumnName) Next 'Вставка всех возможных состояний объекта DataViewRowState Dim names As String() names = DataViewRowState.None.GetNames(DataViewRowState.None.GetType) For i = 0 To names.GetUpperBound(0) ComboBox2.Items.Add(names(i)) ComboBox3.Items.Add(names(i)) Next 'Указание значений по умолчанию TextBox1.Text = "" TextBox2.Text = "" ComboBox1.SelectedItem = "FirstName" ComboBox4.SelectedItem = "FirstName" CheckBox1.Checked = False CheckBox2.Checked = False ComboBox2.SelectedItem = "CurrentRows" ComboBox3.SelectedItem = "CurrentRows" dsEmployees.Tables("Employees").DefaultView.Sort = "FirstName" dvView2.Sort = "FirstName" 'Связывание сеток данных с таблицей DataGrid1.DataSource = dsEmployees.Tables("Employees").DefaultView DataGrid2.DataSource = dvView2 End Sub Обработка нажатия верхней кнопки Apply Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim sort As String Dim rowState As DataViewRowState 'Указание фильтра dsEmployees.Tables("Employees").DefaultView.RowFilter = TextBox1.Text 'Указание сортировки sort = ComboBox1.SelectedItem If CheckBox1.Checked Then sort = sort & " DESC" End If dsEmployees.Tables("Employees").DefaultView.Sort = sort 'Указание состояния записи dsEmployees.Tables("Employees").DefaultView.RowStateFilter = rowState.Parse(rowState.GetType, ComboBox2.SelectedItem) End Sub Обработка нажатия нижней кнопки Apply Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click Dim sort As String Dim rowState As DataViewRowState 'Указание фильтра dvView2.RowFilter = TextBox2.Text 'Указание сортировки sort = ComboBox4.SelectedItem If CheckBox2.Checked Then sort = sort & " DESC" End If dvView2.Sort = sort 'Указание состояния записи dvView2.RowStateFilter ComboBox3.SelectedItem) End Sub = rowState.Parse(rowState.GetType, Рис. 3.5. Интерфейс, построенный с использованием объектов DataView Самостоятельно 1. Скомпонуйте проект DataView и запустите полученное приложение. Внесите необходимые изменения в критерии сетки и щелкните на кнопке Apply для внесения этих изменений в соответствующую сетку. 2. Попробуйте выбрать разные поля сортировки и состояния записей для организации разных представлений. 3. Попробуйте использовать разные фильтры, например сложные выражения Salary>10000 AND Salary<20000 или FirstName Like А. 4. Сетки автоматически поддерживают операции вставки, изменения и удаления записей, поэтому попробуйте отредактировать записи и выбрать соответствующее состояние для отображения только измененных записей. Обратите внимание, как разные представления одной таблицы данных отображаются в сетках данных. Если вставить, изменить или удалить запись в одном представлении (и применить изменение за счет перехода к следующей записи), то внесенные изменения будут автоматически отображены в другом представлении (если, конечно, эта запись соответствует заданному фильтру). Задание 4. Привязка данных в формах Windows Цель Использовать элементы управления Windows-формы для отображения данных из таблиц набора данных DataSet. Решение Использовать свойства элементов управления Windows-форм для связывания с элементами данных объекта DataSet. Обсуждение В проектах, построенных на основе Windows-форм, используют простую (simple data binding) и составную (complex data binding) привязки данных. Простой привязкой данных (simple data binding) называется связывание элемента управления с единственным элементом данных. Примером такой привязки данных могут послужить элементы управления TextBox либо Label, связанные с единственным значением, извлеченным из объекта DataSet либо источников данных. Для демонстрации техники простой привязки данных к элементам управления формы рассмотрим связывание текстовых полей TextBox с элементами данных объекта DataSet. Для примера воспользуемся информацией из таблицы Employees базы данных EmployeeInfo, с которой мы работаем в текущем практикуме. Создайте в проекте еще одну Windows-форму. Разместите в ней 4 надписи, 4 текстовых поля и 2 командные кнопки. Пример такой формы показан на рис. 3.6. Рис. 3.6. Простая привязка данных к элементам формы 1. Добавьте модуль класса в этот же проект. Класс будем использовать для разработки общего метода подключения к базе данных и заполнения DataSet. Класс позволит нам повторное использование метода подключения из других форм приложения. Код класса приведен ниже. Imports System.Data Imports System.Data.SqlClient Public Class clsCommon Public Function PopulateDataSet(ByVal strSQL As String) As DataSet 'Заполним объект DataSet на основе переданного оператора SQL Dim dsResults As New DataSet Dim strConnection As String = "Persist Security Info=False;Integrated Security=SSPI;database=EmployeeInfo;server=abrzh" Dim cnn As New SqlConnection(strConnection) Dim da As New SqlDataAdapter(strSQL, cnn) cnn.Open() da.Fill(dsResults) cnn.Close() PopulateDataSet = dsResults End Function End Class 2. Добавьте следующий программный код в обработчик события Form1_Load: Imports System.Data Imports System.Data.SqlClient Dim dsEmployees As New DataSet Private Sub Form7_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load 3. 'Объявим новый экземпляр класса Dim cCommon As New clsCommon 'вызовем функцию PopulateDataSet класса clsCommon и передадим ей в качестве параметра оператор SQL, 'предназначенный для извлечения данных dsEmployees = cCommon.PopulateDataSet("SELECT FirstName, LastName, DepartmentId, Salary FROM Employees") 'свяжем свойство Text текстового поля для FirstName с полем FirstName таблицы Employees TextBox1.DataBindings.Add("Text", dsEmployees.Tables(0), "FirstName") 'свяжем свойство Text текстового поля для LastName с полем LastName таблицы Employees TextBox2.DataBindings.Add("Text", dsEmployees.Tables(0), "LastName") 'свяжем свойство Text текстового поля для DepartmentID с полем DepartmentID таблицы Employees TextBox3.DataBindings.Add("Text", dsEmployees.Tables(0), "DepartmentID") 4. Свяжем свойство Text текстового поля для Salary с полем Salary таблицы Employees TextBox4.DataBindings.Add("Text", dsEmployees.Tables(0), "Salary") End Sub 5. Опишите обработчики щелчков по кнопкам, следующим программным кодом: Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Me.BindingContext(dsEmployees.Tables(0)).Position -= 1 End Sub Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click Me.BindingContext(dsEmployees.Tables(0)).Position += 1 End Sub Составная привязка данных (complex data binding) используется при связывании элемента управления сразу с несколькими элементами данных, к примеру, это касается связывания таких элементов управления, как DataGrid с объектом DataSet в целом. Другим примером составной привязки данных может послужить связывание элемента управления "комбинированный список" (ComboBox) с несколькими полями на источнике данных, например, с полем, отображаемым для пользователя, и со скрытым значением, используемым при операции обновления записи в самой базе данных. 6. Добавьте в приложение еще одну форму, расположите на ней элемент ComboBox, сетку DataGrid и командную кнопку (рис. 3.7). Добавьте следующий фрагмент программного кода для обработки события Form_Load, в рамках которого будет осуществляться загрузка DataSet и привязка его таблиц к элементам ComboBox и DataGrid. Imports System.Data, System.Data.SqlClient Dim dsDepartment As New DataSet Dim dvEmployees As New DataView Private Sub Form8_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load 7. 'Зададим оператор SQL для извлечения информации из базы данных Dim strSQL As String = "SELECT ID, DepartmentName FROM Departments" 8. 'Объявим новый экземпляр класса clsCommon Dim cCommon As New clsCommon 'Заполним DataSet dsDepartment = cCommon.PopulateDataSet(strSQL) 9. 'Зададим для ComboBox1 в качестве источника данных таблицу из DataSet ComboBox1.DataSource = dsDepartment.Tables(0) 10. 'Установим для свойства ValueMember объекта ComboBox1 значение ID ComboBox1.ValueMember dsDepartment.Tables(0).Columns("ID").ToString = 11. 'Установим для свойства DisplayMember объекта ComboBox1 значение DepartmentName 'Эта информация будет выводиться для пользователя ComboBox1.DisplayMember = dsDepartment.Tables(0).Columns("DepartmentName").ToString 12. 'Зададим оператор SQL для извлечения информации из базы данных strSQL = "SELECT FirstName, DepartmentID, Salary FROM cCommon = New clsCommon lastName, Employees" 13. 'Заполним DataSet dsDepartment = cCommon.PopulateDataSet(strSQL) 'Создадим представление для таблицы Employees из DataSet dvEmployees.Table = dsDepartment.Tables("Table 14. 'Настроим представление на изображение только тех записей таблицы, которые совпадают с ключом выбранной записи в списке ComboBox1 dvEmployees.RowFilter = "DepartmentID=" & ComboBox1.SelectedValue 'Выведем отфильтрованное представление в сетку DataGrid1.DataSource = dvEmployees End Sub 15. Добавим программный код для кнопки, позволяющий просматривать записи только о тех сотрудниках, которые относятся к выбранному отделу. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click dvEmployees.RowFilter = "DepartmentID=" & ComboBox1.SelectedValue End Sub Рис. 3.7. Составная привязка данных к элементам формы Самостоятельно Добавьте в форму Complex data binding кнопку для внесения записей в базу данных. В класс clsCommom добавьте метод, позволяющий фиксировать изменения из Dataset в базе данных. Напишите программный код для кнопки "Внести изменения в базу данных". Метод класса clsCommon для внесения изменений в базу данных может выглядеть следующим образом: 'строку strConnection вынесите выше Dim strConnection As String = "Persist Security Info=False;Integrated Security=SSPI;database=EmployeeInfo;server=abrzh" Public Function UpdateDBFromDataSet(ByVal dsChanges As DataSet, ByVal strSQL As String) As Boolean Dim cnn As New SqlConnection(strConnection) Dim da As New SqlDataAdapter(strSQL, cnn) Dim cb As New SqlCommandBuilder(da) cnn.Open() da.Update(dsChanges) UpdateDBFromDataSet = True cnn.Close() End Function Вызов метода по щелчку на кнопке может иметь следующий вид: Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click If Not dsDepartment.HasChanges(DataRowState.Modified) Then Exit Sub Dim changesDataSet As DataSet changesDataSet = dsDepartment.GetChanges(DataRowState.Modified) Dim strSQL As String = "SELECT FirstName, lastName, DepartmentID, Salary FROM Employees" Dim cCommon As New clsCommon cCommon.UpdateDBFromDataSet(changesDataSet, strSQL) End Sub Скомпонуйте проект и протестируйте его в различных режимах: добавления, изменения и удаления записей.