Visual2000 · Архив статей А.Колесова & О.Павловой
Андрей Колесов, Ольга Павлова
© 1999, Андрей Колесов, Ольга ПавловаVBP-проекты примеров, приведенных в данной статье, вы можете найти на Web-узле по адресу: www.visual.2000.ru/develop/vb/source/.
Эти элементы управления являются модифицированными вариантами DBCombo, DBList и DBGrid, которые поставлялись в более ранних версиях VB. Как и ADO Data, они реализованы в виде ActiveX-компонентов, входящих в состав всех редакций VB 6.0. Поэтому для их размещения в окне инструментов необходимо воспользоваться командой Project|Components и в списке компонентов диалогового окна Components установить флажки Microsoft DataGrid Control 6.0 и Microsoft DataList Controls 6.0.
Элемент управления DataGrid - это связанная с данными сетка, набор строк и столбцов которой соответствует записям и полям объекта Recordset. С его помощью конечный пользователь может читать и корректировать информацию баз данных. Установка ссылки в свойстве DataSource обеспечивает автоматическое заполнение сетки, в том числе и заголовков ее столбцов, после чего можно работать с этими данными в режиме обычной электронной таблицы.
DBGrid из VB 5.0 программно совместим с элементом управления DataGrid, но обладает одним важным отличием: он имеет свойство DataMode, поддерживающее два режима - "связанный" (bound mode) и "несвязанный" (unbound mode), в то время как DataGrid не поддерживает второй режим.
Связанные с данными окно списка DataList и комбинированное окно Data Combo очень похожи на стандартные элементы управления ListBox и ComboBox, однако в них есть специальные функции для работы с базами данных. Отличительная черта элементов управления DataCombo и DataList - это их способность получать доступ к двум разным таблицам и связывать данные из первой таблицы с полем во второй. Это становится возможным благодаря использованию двух источников данных, например элемента управления ADO Data или Data Environment. Такие возможности делают их незаменимыми при разработке приложений типа "таблиц поиска".
Обычно реляционная база данных состоит из нескольких таблиц. Одна из них обычно считается основной и содержит некоторую достаточно постоянную информацию. Другие таблицы содержат более часто обновляемые данные, объем которых динамично меняется.
Например, база данных Northwind, поставляемая вместе с VB, хранит координаты поставщиков в таблице под именем Suppliers. Данные о поставщике хранятся в отдельной записи, состоящей из фиксированного числа полей, в которых размещается название фирмы (CompanyName), ее адрес (Address) и пр. Кроме того, каждая фирма (каждая запись) имеет уникальный цифровой код, который хранится в поле SupplierID.
Вторая таблица носит имя Products, в которой хранится описание продуктов: название (ProductName), стоимость (UnitPrice) и т.д. Поставщик же продуктов в этой таблице представлен кодом в поле SupplierID. То есть если необходимо узнать подробную информацию о поставщике, вы должны найти соответствующую запись в таблице Suppliers с таким же кодом. Таким образом, параметр SupplierID связывает информацию в двух таблицах, при этом в таблице Suppliers он выступает в качестве идентификатора, а в таблице Products - в качестве ключа.
Вопросы связывания двух таблиц и создания законченного приложения, работающего с такой базой данных, приведены в статье "Создание персональной адресной книги на VB 5.0". Сейчас же мы рассмотрим только небольшой пример приложения, который покажет возможности совместного использования DataGrid и DataCombo при работе с базой данных Northwind.
Шаг 1. В VB создайте новый проект Standard EXE. Поместите на форму элемент управления ADO Data и установите его свойства следующим образом:
Name | AdoDataSource |
ConnectionString | Nwind.udl (указав также путь к источнику данных) |
RecordSource | Select * From Products |
Caption | Таблица Products |
Шаг 2. Разместите на форме элемент управления DataGrid и установите его свойства:
Name |
grdProducts |
DataSource | AdoDataSource |
Caption | Продукты |
Таким образом, мы связали сетку с элементом управления ADO Data, который, в свою очередь, связан с таблицей Products из базы данных Northwind (через созданный ранее источник данных Nwind.udl).
Шаг 3. Запустим приложение и мы увидим в сетке содержимое таблицы Products (рис. 8). Щелкая стрелки элемента управления adoDataSource, мы можем перемещаться по строкам таблицы, изменяя позицию текущего курсора.
Рис. 8
Как мы видим, в каждой записи хранится информация о товаре, но в некоторых полях записаны некоторые коды, мало что говорящие конечному пользователю. Как нам сделать так, чтобы вместо числового идентификатора SupplierID можно было увидеть более полезную информацию о поставщике, например название компании? Воспользуемся для этого элементом управления DataCombo.
Шаг 4. Поместите на форму еще один элемент управления ADO Data и установите его свойства:
Name |
adoRowSource |
ConnectionString | Nwind.udl |
RecordSource | Select CompanyName, SupplierID From Suppliers |
Caption | Suppliers |
Visible | False |
Шаг 5. Разместите на форме элемент управления DataCombo и установите его свойства:
Name |
dcbSuppliers |
DataSource | adoDataSource |
DataField | SupplierID |
RowSource | adoRowSource |
ListField | CompanyName |
BoundColumn | SupplierID |
Шаг 6. Запустим приложение. Теперь, передвигаясь по таблице Products, мы видим в окне элемента dcbSuppliers название компании-поставщика для текущего продукта (рис. 9).
Рис. 9
Более того, чтобы изменить имя поставщика для какого-либо продукта, нужно просто раскрыть список и выбрать другую фирму. При этом автоматически изменяется значение, записанное в поле SupplierID (рис. 10). Обратите внимание, что раньше для продукта Aniseed Syrup был указан поставщик Exotic Liquids (код 1), а теперь мы установили для него другую фирму - Pavlova Ltd. (код 7).
Рис. 10
Конечно, можно было бы исправить код SupplierID непосредственно в таблице, но лучше это делать, имея перед глазами понятные названия компаний. Поэтому введем следующий код в модуль кода формы:
Private Sub Form_Load() ' Сделать поле SupplierID невидимым в ' элементе управления DataGrid grdProducts.Columns("SupplierID").Visible = False End Sub
Теперь столбец с кодом поставщика будет невидимым и недоступным для прямого исправления (да он теперь и не нужен), рис. 11.
Рис. 11
Обратите внимание, что аналогичную операцию можно было бы выполнить для поля CategoryID для вывода более подробной информации о категории продукта (для определения, например, таможенных пошлин). А вот смысл поля ProductID в таблице Products совсем иной. Если SupplierID и CategoryID являются здесь ключами, то ProductID - уникальный идентификатор, который может использоваться для получения информации о продукте, например, из таблицы сведений о сделках. Это, в частности, означает, что мы можем в таблице Products менять значения ключей, но корректировать идентификатор никто не имеет права (он формируется автоматически). Поэтому лучше в процедуре Form_Load добавить еще одну строку, чтобы сделать поле ProductID недоступным:
grdProducts.Columns("ProductID").Visible = False
Как видно из приведенных выше установок свойств, этот элемент управления использует два источника данных. Приведенный ниже рисунок демонстрирует, как выполняется эта связка:
adoDataSource -- Свойства dbcSuppiers --- adoRowSource (Products) | | (Suppliers) ============================================================== | | --- DataSource: adoDataSource | SupplierID ------- DataField: SupplierID | ContactName ProductName RowSource: adoRowSource --- Address ProductID BoundColumn: SupplierID ------ SupplierID UnitPrice ListField: CompanyName ------ CompanyName UnitsInStock
Таким образом свойство ListField определяет, какое поле реально выводится элементом управления на экран (в нашем случае это название поставщика). А свойство BoundColumn определяет, какое поле в таблице Suppliers предоставляет фактическое значение таблице Products. Помните при этом, что поле SupplierID в таблице Suppliers ни в коем случае не следует редактировать - его значение записывается в поле, указанное свойством DataField (в нашем случае это поле SupplierID в таблице Products).
Следующая таблица содержит упомянутые здесь свойства и способы их использования:
DataSource | Элемент управления данными, связанный с DataCombo или DataList |
DataField | Поле в наборе записей элемента управления, заданного в свойстве DataSource. Определяет, какой элемент списка будет выделен. Служит для обновления списка |
RowSource | Элемент управления данными, который будет использоваться для заполнения списка |
BoundColumn | Поле в наборе записей элемента управления, заданного в свойстве RowSource. Должно быть того же типа, что и DataField |
ListField | Поле в наборе записей элемента управления, заданного в свойстве RowSource. Будет использоваться при заполнении списка |
Примечание. DataCombo и DataList можно использовать и с одним элементом управления данными. Для этого необходимо установить оба свойства - DataSource и RowSource - как один и тот же элемент управления данными, а свойства DataField и BoundColumn - как одно и то же поле в наборе записей этого элемента управления. Тогда список будет заполнен значениями ListField из обновленного набора записей. В случае же если определено свойство ListField, но не указано свойство BoundColumn, то последнее будет установлено как поле ListField.
Для доступа к данным, формат которых не поддерживается драйвером ODBC, можно создать собственный источник данных с помощью модуля класса. Мы продемонстрируем это на примере элемента управления DataGrid, который будет связываться с этим модулем при помощи свойства DataSource.
Шаг 1. Создайте новый проект Standard EXE. Поместите на форму элемент управления DataGrid. Установите ссылку на библиотеку Microsoft ActiveX Data Objects 2.0 Library командой Project|References. Измените названия проекта и созданной формы на Example3.
Шаг 2. Добавьте в проект модуль класса командой Project|Add Class Module. В окне Properties созданного модуля класса измените его название на NamesData, а затем щелкните свойство DataSourceBehavior и измените его на vbDataSource. Последняя установка нужна, чтобы в модуле класса появилась дополнительная событийная процедура GetDataMember, через которую и будут обрабатываться запросы потребителя данных.
Шаг 3. В разделе Declarations модуля класса создайте переменную ADODB.Recordset:
Option Explicit Private WithEvents rsNames As ADODB.Recordset
Здесь ключевое слово WithEvents позволяет запрограммировать события объекта Recordset.
Шаг 4. А теперь для события Initialize модуля класса введем код, приведенный в листинге 1. С его помощью создается набор записей Recordset, в котором определяются два поля, а затем формируются десять записей. Для события GetDataMember введите следующий код:
Private Sub Class_GetDataMember(DataMember _ As String, Data As Object) Set Data = rsNames End Sub
Этот код возвращает объект Recordset каждый раз при вызове события.
Шаг 5. Теперь нам нужно связать DataGrid с созданным источником данных. Обратите внимание, что мы не можем указать источник, установив свойство DataSource в окне Properties. Такую установку можно произвести только программным образом. Для этого в модуле кода формы объявите переменную объекта класса:
Option Explicit Private datNames As NamesData
Затем в событии Load формы введите следующий код, чтобы установить свойство DataSource элемента управления DataGrid1 как объект класса:
Private Sub Form_Load() ' Создает новый объект NamesData Set datNames = New NamesData ' Связывает элемент управления DataGrid1 ' с новым источником данных Set DataGrid1.DataSource = datNames End Sub
Шаг 6. Запустите проект на выполнение, и вы получите форму наподобие той, что приведена на рис.12.
Рис. 12
Шаг 7. В событии GetDataMember есть аргумент DataMember, позволяющий выбирать нужный набор записей из некоторого списка, определенного в модуле класса. Например, это можно сделать таким образом:
Private Sub Class_GetDataMember(DataMember _ As String, Data As Object) Select Case DataMember Case "Names" Set Data = rsNames Case "Dates" Set Data = rsDates Case Else ' Набор данных по умолчанию Set Data = rsYears End Select End Sub
Теперь по умолчанию будет выбираться набор записей rsYears (его, конечно, нужно создать и определить в модуле класса), а не rsNames. Чтобы обращение все же выполнялось к набору rsNames, нужно сначала задать свойство DataMember элемента управления DataGrid1, а уже потом установить DataSource:
DataGrid1.DataMember = "Names" Set DataGrid1.DataSource = datNames
Шаг 8. У вас есть также возможность программировать события объекта Recordset. Чтобы увидеть список возможных событий, выберите rsNames в списке Object окна кода модуля класса, а потом откройте список Procedures/Events.
Шаг 9. Вы можете добавить классу свойства. Например, приведенный здесь код обеспечивает создание свойства RecordCount - числа записей в наборе:
Public Property Get RecordCount() As Long RecordCount = rsNames.RecordCount End Property
Чтобы проверить его работоспособность, разместите на форме элемент управления Label и введите в код процедуры Form_Load еще одну строку:
Label.Caption = "Общее число записей = " & _ datNames.RecordCount
Шаг 10. Создание метода для класса выполняется добавлением в него новой процедуры. Например, следующий код обеспечивает циклическое перемещение по записям набора данных:
Public Sub Cycle() ' Циклическое перемещение по записям ' набора данных rsNames.MoveNext If rsNames.EOF Then rsNames.MoveFirst End Sub
Шаг 11. Затем поместите на форму командную кнопку Command1 (назовите ее "Перемещение в цикле") и введите для нее такой код:
Private Sub Command1_Click() datNames.Cycle End Sub
Запустите приложение. Теперь, щелкая кнопку, вы увидите, что курсор текущей записи в DataGrid1 перемещается по сетке.
Шаг 12. Совершенно аналогичным способом можно связать набор записей, определенный в модуле класса, с другими элементами управления, например текстовым окном. Разместите на форме элемент управления Text1 и введите в процедуру Form_Load еще три строки кода:
Text1.DataMember = "Names" Set Text1.DataSource = datNames Text1.DataField = "Номер"
Запустите приложение. Теперь щелкая кнопку "Перемещение в цикле", вы увидите, что в поле Text1 будет выдаваться поле "Номер" текущей записи (рис. 13).
Рис. 13
Шаг 13. Однако более гибким механизмом связывания данных является использование объекта BindingCollection из коллекции Data Binding Collection. (Для этого нужно сначала установить ссылку на библиотеку Microsoft Data Binding Collection). Для работы с ним добавьте в раздел Declarations модуля класса строку:
Private objBindingCollection as BindingCollection
Чтобы протестировать данный режим, разместите на форме еще одно текстовое поле - Text2. Далее в процедуру Form_Load добавьте такой код:
' Связывание данных с помощью объекта ' BindingCollection Set objBindingCollection = New BindingCollection objBindingCollection.DataMember = "Names" Set objBindingCollection.DataSource = datNames ' ' Далее выполняется добавление связей для ' объекта BindingCollection ' ' Установка свойства Text objBindingCollection.Add Text2, "Text", "Название" ' Установка свойств элементов управления ' из столбца "Код" набора записей objBindingCollection.Add Text2, "Appearance", "Код" objBindingCollection.Add Text1, "Visible", "Код"
Здесь создается экземпляр объекта BindingCollection и устанавливается его свойство DataSource. Затем с помощью метода Add определяются отношения, связывающие свойства разных объектов. Методу Add передаются три обязательных параметра: имя объекта потребителя, свойство этого объекта, которое будет привязано к источнику, и поле источника, которое будет привязано к свойству. Казалось бы, этот механизм похож на тот, что мы уже применяли ранее. Однако в шаге 11 мы только устанавливали связь между объектом и источником данных. Заполнение же свойств элемента управления выполнялось автоматически по некоторому предопределенному алгоритму (в данном случае для Text1 производилась установка его свойства Text).
В случае же с BindingCollection выполняется прямая связь поля набора записей с любым свойством элемента управления (а не только свойством Text). В нашем примере мы производим соответствующую установку свойств Visible для Text1 и Appearance для Text2. В результате после запуска примера, щелкая кнопку "Перемещение в цикле", мы видим не только коррекцию содержимого поля Text2, но также изменение формы данного окна (плоское или объемное изображение) и появление/исчезновение изображения окна Text1 (рис. 14).
Рис. 14
Более того, установленная связь работает в обе стороны. Если вы введете новое значение в поле Text2, то увидите, что оно отразится в сетке DataGrid1 через источник данных (модуль класса), рис. 15.
Рис. 15
При работе с объектом BindingCollection потребителем данных может выступать не только элемент управления, но и другие объекты, в частности тот же модуль класса.
Шаг 14. Создайте еще один модуль класса и установите его свойства Name как MyTarget и DataBindingBehavior как vbSimpleBound. В его секции Declarations определите переменную
Private NameField$
Добавьте в этот модуль класса пару процедур Property Get/Property Let для общего свойства MyName:
Public Property Get MyName() As String MyName = NameField$ End Property Public Property Let MyName(MyNameField As String) NameField$ = MyNameField$ ' Вывод полученного значения в окно Immediate Debug.Print NameField$ End Property
Класс MyConsumer не имеет средств отображения данных, поэтому для доказательства связи и правильной передачи данных мы используем вывод обновляемого свойства в окно Immediate.
Шаг 15. Теперь выберите форму и введите в ее раздел Declarations:
Private objConsumer as MyConsumer
а в код процедуры Form_Load следующий код:
Set objConsumer = New MyConsumer ' Связываем поле "Название" со свойством "MyName" ' объекта MyConsumer objBindingCollection.Add objConsumer, "MyName", "Название"
Шаг 16. Запустите проект и сделайте так, чтобы было видно окно Immediate. Теперь, нажимая кнопку "Перемещение в цикле", убедитесь, что одновременно с обновлением информации в окнах формы будет выдаваться значение обновляемого свойства MyName класса MyConsumer (рис. 16).
Рис. 16
Private Sub Class_Initialize() ' Добавляет название нового элемента к ' коллекции DataMembers DataMembers.Add "Names" ' Устанавливает переменную объекта Set rsNames = New ADODB.Recordset With rsNames ' Создает набор записей с тремя полями. ' Первая запись и третья записи - ' целочисленные данные, ' а вторая - строка, имеющая ' максимальную длину 256 символов .Fields.Append "Номер", adInteger .Fields.Append "Название", adBSTR, 255 .Fields.Append "Код", adInteger ' CursorType установлен как ' adOpenStatic - обновляемый ' мгновенный снимок набора записей .CursorType = adOpenStatic ' LockType установлен как ' adLockOptimistic, чтобы иметь ' возможность обновлять набор записей .LockType = adLockOptimistic .Open ' Открывает набор записей End With ' Dim i As Integer For i = 1 To 10 ' Добавляет 10 записей rsNames.AddNew rsNames!Номер = i rsNames!Название = "Запись " & i rsNames!Код = i Mod 2 rsNames.Update Next i ' Переходит к началу набора данных rsNames.MoveFirst End Sub