Visual2000 · Архив статей Колесова & Павловой

Создание персональной адресной книги на VB 5.0

Андрей Колесов, Ольга Павлова

© 1998, Андрей Колесов, Ольга Павлова
Авторский вариант. Статья была опубликована c незначительной литературной правкой в журнале "КомпьютерПресс" № 07/98, c. 187-193.


Часть 2. Создание приложения

В начало статьи

Разработка пользовательского интерфейса

Шаг 6. Создайте новый VB-проект Standard EXE (если он не был еще создан). Введите Contacts в качестве имени, а "Личная записная книжка" - в качестве заголовка новой формы. Разместите на форме элемент управления Data, который содержит интерфейс для перехода к следующей, предыдущей, первой и последней записям в таблице. Свойство Connect можно установить равным любому типу базы данных Jet. В нашем же примере зададим данное свойство равным Access. Далее установим связь элемента управления Data с нашей базой данных, для чего в свойстве DatabaseName укажите полный путь к файлу Contacts.mdb.

Обратите внимание, что значения свойств Connect и DatabaseName задаются в первую очередь - до того, как вы перейдете к определению других свойств. Дело в том, что от них зависят многие другие свойства, поэтому они представляют собой некоторый список вариантов, основанных на выбранной вами базе данных. RecordSource относится к такому типу свойств - в его списке приведены все таблицы базы данных. Выберите таблицу Contacts для свойства RecordSource, а затем измените имя элемента управления - установите значение свойства Name равным dcContacts.

Теперь у вас есть форма с элементом управления Data, который связан с таблицей Contacts. Этот элемент управления не только содержит механизм для перемещения по таблице, но и представляет собой источник данных, к которому "привязаны" остальные компоненты формы. Зависящий от данных (data-aware) или привязанный к данным (data-bound) элемент управления - это такой компонент, который связан с элементом управления Data.

При переходе от одной записи таблицы к другой Data автоматически собирает изменения, произошедшие в привязанных к нему компонентах, и сохраняет их в текущей записи. Затем выполняется переход к новой записи и модификация "привязанных" компонентов в соответствии с ее данными. Таким образом элемент управления Data обеспечивает передачу данных между "привязанным" компонентами и таблицей базы данных.

Шаг 7. Перейдем к размещению "привязанных" элементов управления на форме - по одному для каждого поля базы данных. Поместите Label на форму и установите значение свойства Caption равным "Фамилия:" (включая двоеточие). Рядом с меткой разместите TextBox, который необходимо "привязать" к элементу управления Data. Элементами, влияющими на "привязывание" элемента управления TextBox, являются свойства DataField и DataSource.

Свойство DataSource содержит список всех элементов управления Data в текущей форме, в нашем случае там есть только одно имя - dcContacts. Укажите его. После этого свойство DataField заполняет свой список всеми полями, содержащимися в этом элементе управления Data. Установите имя поля данных равным LastName. Аналогичным образом установите по паре компонентов Label и TextBox для полей FirstName, Company, Address и Email.

Теперь у вас есть полностью функциональная форма, все элементы управления которой "привязаны" к базе данных. Чтобы продемонстрировать ее работу в режиме просмотра (мы еще не включили в нее средства добавления новых записей), в БД должны быть какиенибудь данные. Покажем возможность заполнения базы данных с помощью встроенных средств VB.

В начало статьи

Заполнение базы данных Contacts

Есть несколько вариантов заполнения базы данных в VB. Одним из них, известном и в ранних версиях VB, является использование средства VisData. Для этого нужно открыть соответствующую базу данных и щелкнуть название нужной таблицы, а далее вводить содержание отдельных записей. Однако при работе с VisData у нас возникли проблемы. Во-первых, он плохо вводит буквы русского алфавита (войдя в него с латинской раскладкой клавиатуры, переключить ее уже нельзя), и, во-вторых, записи, сделанные по-русски, невозможно прочесть - используется какой-то нерусифицированный шрифт, который не удалось изменить (рис. 7).

Рис. 7. Ввод русского текста в среде Visual Data Manager не удался — здесь используется какой-то нерусифицированный шрифт

Поэтому мы использовали другой способ выполнения этой же операции средствами, впервые появившимися в VB 5.0.

Шаг 8. Из меню Add-Ins в Visual Basic выберите Visual Database Tools.

Примечание.Данная утилита в виде автономного средства входит в состав Visual Studio 97 (в том числе и в состав VB 5.0 Professonal). Она находится на диске дистрибутива VB5 в подкаталоге Tools/Datatool и установливается с помощью автономной Setup. В составе VB 6.0 такой утилиты нет: под этим назчанием в этой версии понимается набор новых средств работы с базами данных.

Если такой команды в меню нет, то выберите Add-In Manager и установите флажок рядом с Microsoft Data Tools (предварительно установив это средство на свой компьютер). После того как вы выбрали команду Visual Database Tools, Visual Basic запустит копию Developer Studio - Visual InterDev, которая представляет собой интегрированную среду разработки Visual J++ и Visual C++.

По каким-то причинам Microsoft решила не включать Visual Database Tools непосредственно в среду разработки VB. Поэтому при первом его запуске необходимо определить имя источника данных (Data Source Name - DSN) для работы с базой данных. Щелкните вкладку Machine Data Source, а затем - кнопку New, чтобы создать новый источник данных. Выберите источник данных User, предлагаемый по умолчанию, и щелкните кнопку Next. После этого выделите в качестве драйвера Microsoft Access и снова щелкните кнопку Next.

Наконец, щелкните кнопку Finish, и драйвер Microsoft Access выведет диалоговое окно, где задается имя источника данных и название базы данных, с которой вы хотите работать. Введите Contacts в качестве имени источника данных (рис. 8). Щелкните кнопку Select и выберите созданную вами базу данных Contacts.mdb. Источник данных должен показаться в списке источников данных. Выделите Contacts и щелкните OK.

Рис. 8. Определение источника данных в среде Visual Database Tools

Теперь вы находитесь в оболочке Developer Studio. Окно в левой части экрана обычно показывает связь с базой данных. В случае необходимости можно включить несколько баз данных. Открыв узел Tables, вы увидите две таблицы, созданные ранее.

Шаг 9. Откройте узел Contacts и вы увидите поля, входящие в состав этой таблицы. Если вы дважды щелкните ее название, то в окне, расположенном в правой части экрана, выведется сетка с содержимым таблицы (рис. 9). Последняя строка сетки, помеченная звездочкой (*), используется для ввода новых данных. Введите две или три записи с помощью сетки. Ничего не вводите в столбец ContactID - он будет заполняться автоматически при вставке записи в таблицу. Если вы хотите увидеть значение, которое таблица присвоила полю ContactID, щелкните сетку правой кнопкой мыши и выберите Run. Тогда сетка отправит повторный запрос к базе данных и вернет значения, присвоенные полю ContactID.

Рис. 9. Заполнение таблицы Contacts в Visual Database Tools. В левом окне видна структура созданной нами базы данных

В начало статьи

Просмотр записей из базы данных

Шаг 10. Закройте Developer Studio и вернитесь в Visual Basic. Запустите свой проект на выполнение и с помощью элемента управления Data просмотрите данные, которые вы только что ввели. Однако для просмотра данных было бы желательно иметь информацию о номере текущей записи. Для этого можно использовать окно белого цвета компонента Data, которое заполняется с помощью его свойства Caption. Мы будем использовать данное окно для вывода индикатора типа "Запись 1 из 3" (см. рис.1).

Элемент управления Data содержит объект RecordSet, включающий в себя два свойства, которые можно использовать для обновления заголовка. Свойство RecordSet.AbsolutePosition показывает запись, которую вы просматриваете в данный момент, а свойство RecordSet.RecordCount - сколько записей хранится в данном наборе. Мы хотим обновлять строку состояния каждый раз, как только элемент управления Data переходит к другой записи. В этом случае инициируется событие Reposition, куда мы и поместим код для обновления строки состояния. Дважды щелкните элемент управления Data, выберите событие Reposition из списка Procedure и напишите туда следующий код:

dcContacts.Caption = "Запись " + CStr(dcContacts.Recordset. _
  AbsolutePosition + 1) + " из " +  _
  CStr(dcContacts.Recordset.RecordCount)

Нам привычнее вести отсчет с единицы, поэтому мы добавляем 1 к свойству AbsolutePosition, которое выполняет счет с нуля.

Теперь, если вы запустите свой проект, то обнаружите, что строка состояния в первый момент выводит "Запись 1 из 1", независимо от того, сколько записей хранится в таблице. Это происходит из-за ошибки в VB 5.0. Для ее исправления, добавьте код, который сначала переводит к следующей записи в таблице, а затем возвращает в ее начало. В нашем примере вставим следующий код для события Form_Initialize:

If Not dcContacts.Recordset.EOF Then
  dcContacts.Recordset.MoveNext
  dcContacts.Recordset.MoveFirst
End If

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

В начало статьи

Добавление и удаление записей таблицы Contacts

Без таких функций добавления и удаления записей от нашей записной книжки не очень много пользы. Но это также довольно просто сделать с помощью элемента управления Data.

Шаг 11. Выделите Data и установите его свойство EOFAction равным 2 - AddNew, что позволит автоматически добавлять новую запись при достижении конца таблице.

Кроме того, потребуется написать код, который обновляет заголовок Data. Нужно проверить свойство EditMode для набора записей, чтобы определить, выводите ли вы существующую запись или добавляете новую. Если EditMode равно vbAddNew, то добавляется новая запись. Усовершенствуем код события Reposition элемента управления Data:

If dcContacts.Recordset.EditMode = vbAddNew Then
  dcContacts.Caption = "Новая запись"
Else
   dcContacts.Caption = "Запись " + _
     CStr(dcContacts.Recordset. _
     AbsolutePosition + 1) + " из " + _
     CStr(dcContacts.Recordset.RecordCount)
End If

С помощью данного кода в заголовке элемента управления Data выводится "Новая запись" при добавлении новой записи, а во всех остальных случаях - номер записи.

Запустите проект, переместитесь к концу таблицы, а затем перейдите на одну запись вперед. Заполните данными все элементы управления и снова перейдите к следующей записи. При перемещении к новой записи Data сохранит все данные, введенные в "привязанных" компонентах.

Однако такой способ добавления записей не очень очевиден для пользователей-новичков. Для них эту функцию можно представить в более простом варианте. Поместите на форму кнопку с названием "Добавить" и введите следующий код в ее событие Click:

dcContacts.Recordset.AddNew

Шаг 12. Также необходима функция удаления записи. Поместите другую кнопку на форму, установите свойство Caption равным "Удалить" и введите такой код в событие Click:

With dcContacts.Recordset
  .Delete    ' Удаление записи
  .MoveNext     ' Переход к следующей записи
  If .EOF Then .MoveLast    ' Проверка конца файла
End With

В начало статьи

Поиск записей

Какую ценность имеет список контактов, если в нем нет функции поиска? Объект RecordSet содержит несколько методов решения этой задачи. Метод FindFirst находит первую запись, отвечающую некоторому критерию. Его мы и используем для нахождения контактов по фамилии.

Шаг 13. Поместим кнопку на форму, установим свойство Caption равным "Найти" и введем следующий код в событие Click:

Dim szFindString As String
  szFindString = InputBox _
    ("Введите фамилию для поиска:", "Найти контакт", szLastFind)
  If Len(szFindString) > 0 Then
    dcContacts.Recordset.FindFirst _
      "LastName = '" + szFindString + "'"
    If dcContacts.Recordset.NoMatch Then
      MsgBox "Нет такой фамилии", vbOKOnly, "Найти контакт"
    Else
    szLastFind = szFindString
  End If
End If

Переменная szLastFind должна быть объявлена как глобальная на уровне модуля. Она используется для хранения фамилии, заданной пользователем при последнем поиске. Обязательно проверьте длину строки, так как если пользователь щелкнет кнопку Cancel в окне ввода поиска, VB вернет строку нулевой длины.

Критерий поиска формируется следующим образом: имя поля, которое вы ищите, затем знак равенства, одиночная кавычка, символьная переменная и, наконец, еще одна одиночная кавычка. Для числовых полей кавычки не нужны. Метод NoMatch объекта RecordSet проверяет, существуют ли записи, отвечающие заданному критерию.

Однако кнопка "Найти" ищет только первую запись, отвечающую заданному критерию. Если у вас несколько записей с одной и той же фамилией, то можно воспользоваться методом FindNext объекта RecordSet, который начинает движение не с первой записи, а с текущей. Поместите на форму кнопку "Найти следующую" и введите такой код:

If szLastFind <> "" Then
  dcContacts.Recordset.FindNext "LastName = '" + szLastFind + "'"
  If dcContacts.Recordset.NoMatch Then
    MsgBox "Такой фамилии больше нет", vbOKOnly, "Найти контакт"
  End If
End If

Первая строка проверяет, было ли что-нибудь найдено с помощью кнопки "Найти". Если было, то для нахождения следующей записи с такой же фамилией используется метод FindNext. Если нужные записи не обнаружены, выдается сообщение об ошибке.

В начало статьи

Интеграция таблиц Contacts и PhoneNumbers

Теперь у вас есть полностью функциональное приложение - менеджер контактов. Осталось только интегрировать его с таблицей PhoneNumbers. Здесь необходимо помнить, что элемент управления Data уже связан с другой таблицей и мы не можем использовать его для списка номеров телефонов. Поэтому создадим новый компонент.

Шаг 14. Поместите на форму еще один элемент управления Data и назовите его dcPhoneNumbers. Установите значение свойства Connect равным Access, а значение свойства DatabaseName - Contacts.mdb. Но ничего не вводите в свойство RecordSource: если указать таблицу PhoneNumbers для свойства RecordSource, то Data будет содержать все телефонные номера.

Чтобы новый элемент управления Data содержал телефонные номера только для текущего контакта, нужно установить значение свойства RecordSource равным динамическому SQLзапросу, с помощью которого определяется, какие поля, отвечающие указанному критерию, и из какой таблицы необходимо вернуть:

SELECT * FROM PhoneNumbers WHERE ContactID = 1

Указав звездочку (*), вы сообщаете элементу управления Data о том, что необходимо вернуть все поля. FROM указывает, из какой таблицы необходимо взять данные. WHERE задает критерий, которому должны отвечать возвращаемые записи.

В данном случае мы говорим, что хотим увидеть только те записи, у которых ContactID равен единице. В нашем коде мы получаем ContactID из текущего контакта и сразу же формируем фразу WHERE. Получающаяся в результате SQL-строка помещается в свойство RecordSource, передавая тем самым только те номера, которые отвечают текущему контакту. Поскольку мы хотим, чтобы список телефонных номеров обновлялся каждый раз при переходе к другой записи, в событие Reposition элемента управления dcContacts необходимо добавить следующий код (он должен располагаться сразу после кода, который устанавливает заголовок текущей записи):

If Not dcContacts.Recordset.EOF Then
  dcPhoneNumbers.RecordSource = _
    "SELECT * FROM PhoneNumbers WHERE ContactID = " + _
    CStr(dcContacts.Recordset!ContactID)
  dcPhoneNumbers.Refresh
End If

Первая строка кода проводит проверку, чтобы убедиться, находитесь ли вы в правильной записи элемента управления dcContacts. Вторая строка осуществляет доступ к полю ContactID объекта dcContacts RecordSet, а затем добавляет его текущее значение в символьном формате в конец SQL-строки, о который мы говорили выше (то есть вместо статического номера мы используем его динамическое значение). Получившаяся строка помещается в свойство RecordSource элемента управления dcPhoneNumbers. Далее вызывается метод Refresh этого же элемента управления для выполнения самого запроса.

Мы собираемся использовать dcPhoneNumbers только для доступа к данным, и нет необходимости показывать его пользователям. Поэтому установите значение его свойства Visible равным False.

Шаг 15. Элемент управления dcPhoneNumbers обеспечивает доступ к нужным записям, но как вывести на экран их содержимое? Для этого воспользуемся элементом управления DataBase Grid - DBGrid. (Если его нет на панели Toolbox, то подключите его с помощью команды Components в меню Project.) Вы можете связать DBGrid с dcPhoneNumbers так же, как это было сделано в случае TextBox. В отличие от последнего элемент управления DBGrid выводит все записи, сформированные элементом управления Data. Таким образом вы получите все номера, связанные с текущим контактом.

Поместите DBGrid на форму и назовите его dbgPhone (рис. 10). Установите свойства AllowAddNew и AllowDelete равными True - это позволит вам добавлять новые телефонные номера и удалять старые. В свойстве DataSource укажите dcPhoneNumbers, а для свойства TabAction выберите 2 - Grid Navigation. Благодаря этому вы сможете перемещаться по ячейкам таблицы с помощью кнопки Tab. Щелкните свойство Custom, чтобы вывести на экран страницу дополнительных свойств DBGrid. Перейдите во вкладку Columns и задайте названия столбцов в DBGrid.

Рис. 10. Вид исходной формы нашего приложения. Обратите внимание, что элемент управления Data1 является невидимым в режиме выполнения программы

Первый столбец будет выводить местонахождение телефонного номера. Убедитесь, что в списке столбцов выделен столбец Column 0. Установите его заголовок равным "Местонахождение", а в строке DataField задайте имя поля Location. Выберите столбец Column 1 в списке столбцов. Задайте его заголовок как "Номер телефона", а в строке DataField задайте имя поля Number. В строке NumberFormat введите (###)###-####. Этот параметр работает по тем же правилам, что и функция Format() в VB. В данном случае мы форматируем число из базы данных как телефонный номер. Щелкните кнопку OK, чтобы убрать диалоговое окно с экрана. Вы почти закончили наш пример.

Шаг 16. Добавление новых записей в базу данных PhoneNumbers. Как мы уже говорили, элемент управления DBGrid должен давать пользователю возможность добавлять новые записи. Проблема состоит в том, что таблица содержит четыре столбца: NumberID, ContactID, Location и Number. Два последних столбца представлены в элементе управления DBGrid, так что пользователь может вводить для них новые значения. Значение же NumberID равно AutoIncrement, которое создается автоматически при сохранении записи в таблице.

Пользователь не может вводить значение ContactID - это поле представляет собой внешний ключ, который связывает запись в таблице PhoneNumbers с соответствующим контактом в таблице Contacts. Во-первых, пользователь не знает значения ContactID для контакта, а, вовторых, если бы он мог видеть его, это могло бы стать причиной ошибок из-за неправильного ввода этого числа. Поэтому поле ContactID должно формироваться автоматически, копируя это значение из текущей записи Contacts.

Каждый раз при добавлении новой записи с помощью сетки инициируется событие OnAddNew. Именно здесь нужно установить соответствующее значение поля ContactID. Введите следующий код в событие dbgPhone_OnAddNew:

dcPhoneNumbers.Recordset.Fields ("ContactID") = _
  dcContacts.Recordset.Fields("ContactID").Value

Данная строка кода получает ContactID из элемента управления dcContacts и устанавливает его для той записи, которую вы добавляете при помощи элемента управления dcPhoneNumbers.

Шаг 17. Запустите свой проект. Добавьте несколько телефонных номеров с помощью элемента управления DBGrid. Когда вы станете перемещаться по контактам, элемент управления DBGrid будет показывать соответствующие им телефонные номера.

Шаг 18 и последний. Совместными усилями у нас получилось отличное приложение, которое, может быть, будет полезным еще для кого-то, кроме его создателей. Но при записи программы на другой компьютер, скорее всего, вы обнаружите, что она не работает, так как не может найти нужную базу данных (хотя вы переписали ее также). Проблема заключается в том, что в нашей программе "жестко зашит" абсолютный адрес (причем два раза для разных элементов управления Data Control). Чтобы избежать этой ситуации, следует выполнять динамическую настройку программы на используемый файл базы данных при ее запуске. Например, если мы будем считать, что этот файл находится в одном каталоге с самим приложением (как это у нас и было), то нужно добавить такой код в процедуру загрузки формы:

Private Sub Form_Load()
  dcContacts.DatabaseName = App.Path + "\Contacts.mdb"
  dcPhoneNumbers.DatabaseName = dcContacts.DatabaseName
End Sub

Очевидно, что используя такой прием, несложно сделать так, чтобы пользователь мог, например, задавать адрес базы данных при запуске приложения, обеспечивая возможность работы с различными "адресными книгами".

Вот и все. Примите наши поздравления - мы сделали довольно полезное законченное приложение!

В начало статьи