В лекции №2 вставляю модуль данных. При попытке заменить name на fDM выдает ошибку: "The project already contains a form or module named fDM!". Что делать? |
Таблицы Paradox в ADO
Подключение таблиц Paradox 7 к приложению через ADO
Изучение свойств полей лучше сразу проводить на примере. Для этого создадим небольшую демонстрационную базу данных, всего из двух таблиц, и приложение, работающее с ней. Попутно затронем темы, которых раньше не касались. Цель проекта: создать мини-меню для столовой, кафе или ресторана.
Прежде всего, определимся с таблицами. Таблицы будем создавать в формате Paradox 7, описание типов полей которого подробно рассматривалось на лекции №30 курса "Введение в программирование на Delphi ". Для доступа к данным этих таблиц используем механизм ADO. Создавать таблицы удобней с помощью утилиты Database Desktop, входящей в состав Delphi.
Пусть главная таблица называется Food, ее поля описаны в таблице 5.1:
Подчиненная таблица будет еще проще:
Имя поля: | Тип | Описание |
---|---|---|
TKey | Auto increment (+) | Ключевое поле, служит счетчиком типов. |
TName | Alpha (A) | Текстовое поле размером 20, название типов. |
Итак, начнем. Откройте утилиту Database Desktop. Чтобы облегчить работу и не искать каждый раз нужный каталог, укажем сразу рабочую папку, которую нужно вначале создать средствами Windows:
C:\Menu
Для этого выберите команду меню "File -> Working Directory ". В открывшемся окне нажмите кнопку " Browse " и найдите эту директорию на диске. Когда вы выберите ее, нажмите кнопку " OK ". Теперь эта папка стала папкой "по умолчанию". При попытке открыть или создать таблицу в утилите Database Desktop, эта папка всегда будет текущей (если в дальнейшем вы не смените рабочую папку).
Далее выбираем команду "File -> New -> Table". Оставьте тип Paradox 7, нажмите "ОК".
Далее вам предлагается ввести названия и типы полей. Сделайте это, как в таблице 5.1.
Как только вы ввели названия, типы и размеры (размер есть только у текстового поля), в списке " Table properties " выберите команду " Table Language " и нажмите кнопку " Modify ". В выпадающем списке выберите язык, как на рисунке 5.1:
Если этого не сделать, у вас будут проблемы с отображением русских символов.
Далее нажмите кнопку " Save as " и укажите имя таблицы: Food. Таким же образом сделайте таблицу Tips, руководствуясь таблицей 5.2.
После этого вы можете закрыть утилиту Database Desktop, она больше не нужна.
Пойдем дальше. Поскольку мы собираемся подключаться к таблицам Paradox с помощью механизма доступа к данным ADO, нам потребуется установить на компьютере нужный драйвер ODBC. Для этого откройте Панель управления (Пуск -> Настройка -> Панель управления). Если вы используете Windows 2000, XP или более новую, вам придется еще выбрать команду "Администрирование". Далее открываем " Источники данных ODBC ". Нажимаем кнопку "Добавить", выбираем драйвер " Microsoft Paradox Driver (*.db) " и нажимаем кнопку "Готово". Далее в поле "Имя источника данных" укажите MenuParadox, этот источник мы будем использовать только для этой нашей программы. Затем уберите галочку "Использовать текущий каталог" и нажмите кнопку "Выбор каталога". В открывшемся окне выберите нашу папку C:\Menu:
Нажимаете "ОК", и драйвер готов. Теперь можете закрыть все остальные окна, они больше не нужны.
Загружаете Delphi. Свойству Name главной формы присвойте имя fMain, сохраните модуль формы как Main, а проект в целом как MyMenu. В свойстве Caption формы напишите "Изучение свойств полей". На форму бросьте компонент Panel с вкладки Standard, свойство Align установите в alTop. Ниже с вкладки Data Controls установите компонент DBGrid, в свойстве Align которого выберите alClient, чтобы заполнить оставшееся пространство. Затем на панель установите простую кнопку, в свойстве Caption которой напишите "Типы блюд". У вас должна получиться такая форма:
Раз у нас еще будет форма с типами блюд, следовательно, понадобится и модуль данных, общий для всех форм. Выберите команду File -> New -> Data Module. В свойстве Name модуля укажите fDM и сохраните модуль под именем DM.
Теперь с вкладки ADO устанавливаем компонент ADOConnection. Сразу свойство Name для краткости обращения переименуйте в Con1. Займемся подключением. Дважды щелкните по компоненту, чтобы открыть редактор подключений. Нажмите кнопку " Build ". На вкладке "Поставщик данных" по умолчанию должен быть " Microsoft OLE DB Provider for ODBC Drivers ". Нам нужен именно этот поставщик. Переходим на вкладку "Подключение" (для этого можете просто нажать кнопку "Далее"). В выпадающем списке "Использовать имя источника данных" нам нужно выбрать MenuParadox, то подключение, которое мы создали ранее. К слову сказать, мы могли и не указывать адрес данных, могли оставить галочку "Использовать текущий каталог". В этом случае таблицы нужно было бы расположить там же, где и программа, или создавать подключение при работающей программе. Так удобно делать при использовании локальной базы данных. Способ, которым мы воспользовались сейчас, более удобен для многопользовательских файл-серверных БД. Если бы базы лежали где-то на сетевом диске, тогда мы могли бы в качестве папки указать сетевой путь, например:
\\myserver\Menu
но тогда эта папка должна быть открыта в сети как общий ресурс.
Нажмите кнопку "Проверить подключение". Если вышло сообщение "Проверка подключения выполнена", значит, вы все сделали правильно, и ошибок нет.
Нажимаем кнопку "ОК", чтобы подтвердить подключение, и еще раз "ОК", чтобы закрыть окно подключений. Сразу же свойство LoginPrompt компонента Con1 переводим в False, чтобы каждый раз при подключении программа не запрашивала имя пользователя и пароль. Затем в свойстве Connected устанавливаем True. Подключение произошло.
Далее с вкладки ADO устанавливаем два компонента ADOTable. Выделите оба компонента, и в их свойстве Connection выберите наш Con1. Займемся вначале первой таблицей. В свойстве TableName выберите таблицу Food, свойство Name переименуйте в FoodT, а свойство Active переведите в True. Для второго компонента ADOTable выберите таблицу Tips, а компонент переименуйте в TipsT. Также переведите Active в True. Далее рядом с таблицами установите два компонента DataSource с вкладки Data Access. Первый переименуйте в FoodDS, второй - в TipsDS. В свойстве DataSet каждого выберите соответствующую таблицу. Не забудьте сохранить проект.
Перейдите на главную форму. Командой File -> Use unit подключитесь к созданному модулю данных. В свойстве DataSource сетки DBGrid выберите fDM.FoodDS. На сетке должны появиться столбцы с данными. Нажмите кнопку Run на панели инструментов или горячую клавишу F9. Проект компилируется, запускается, и… выходит ошибка:
В чем дело? Вроде бы, мы все делали правильно, иначе на сетке DBGrid не появились бы нужные столбцы? Просто мы добрались до проблем с полями. Нажмите кнопку "ОК", затем выберите Run -> Program reset, чтобы закрыть повисшую программу. Теперь перейдите на окно модуля данных, щелкните дважды по компоненту FoodT, чтобы вызвать редактор полей. Затем щелкните по окну редактора правой кнопкой и выберите команду Add all fields (Добавить все поля). То же проделайте и со второй таблицей. Снова сохраните проект, скомпилируйте его и запустите - теперь полный порядок, программа запускается и выполняется нормально.
Дело в том, что, используя драйверы ODBC с "неродными" форматами баз данных, такими как dBase или Paradox, приходится точно указывать, какие у нас поля, а не надеяться на авто-определение. Вообще, создавать поля для каждого набора данных считается хорошим тоном в программировании.
Далее создадим еще одну форму, для редактирования типов блюд:
Форму назовите fMyTypes, сохраните модуль как MyTypes. Чтобы убрать из окна лишние кнопки системной строки и не позволять пользователю менять размеры окна, в свойстве BorderStyle формы выберите значение bsDialog. Не забудьте подключить к нему модуль данных DM. На форме две простых кнопки, поле DBEdit с вкладки Data Controls и сетка DBGrid с этой же вкладки. В свойстве DataSource и сетки, и поля выберите fDM.TipsDS. У поля DBEdit, кроме того, в свойстве DataField выберите поле TName. Обратите внимание, что я назвал форму и модуль как MyTypes, а не просто как Types. Слово Types (типы) довольно распространенное в языках программирования и может вызвать конфликт названий. Попробуйте, если не верите!
Дважды нажимаем на верхнюю кнопку, и в обработчике пишем код:
//добавляем запись: fDM.TipsT.Append; //переводим фокус: DBEdit1.SetFocus;
В коде обработки нижней кнопки просто закрываем окно:
Close;
Однако нам нужно убедиться, что если изменения в таблице были, и пользователь желает их сохранить, то они сохраняются. Поскольку мы не знаем точно, каким образом пользователь закроет это окно, придется для проверки сгенерировать событие onClose для формы:
{если изменения есть, спросим что с ними делать. если пользователь не желает их сохранять, отменяем изменения. иначе сохраняем: } if fDM.TipsT.Modified then if Application.MessageBox('Данные изменены! Сохранить?', 'Внимание!', MB_YESNO+MB_ICONQUESTION) <> IDYES then fDM.TipsT.Cancel else fDM.TipsT.Post;
Далее переходим на главную форму, командой File -> Use Unit подключаем модуль MyTypes, дважды щелкаем по кнопке "Типы блюд" и в сгенерированном событии вызываем новый модуль:
fMyTypes.ShowModal;
Сохраните проект, скомпилируйте его и впишите 5-10 типов блюд, например, "Напитки", "Супы", "Салаты" и т.п. Это нам будет нужно для подстановочного поля. Если в поле автоинкремента у вас будут выходить нули, не обращайте внимания - после сохранения таблицы там окажутся правильные цифры. Вы сможете убедиться в этом, закрыв программу и загрузив ее еще раз.
Теперь займемся формой для редактирования основной таблицы. Командой File -> New -> Form или аналогичной кнопкой на панели инструментов создайте новую форму. В свойство Caption этой формы впишите "Редактирование блюда", в свойстве Name укажите fEditor, а модуль сохраните как Editor. Сразу же командой File -> Use Unit подключите к этой форме модуль данных DM. Форма будет выглядеть так:
Как видно из рисунка, на форме присутствуют поясняющие компоненты Label, три компонента DBEdit, один DBLookupComboBox, один DBNavigator и кнопка BitBtn, в свойстве Kind которой выбрано значение bkClose.
Компонент DBLookupComboBox немного сложней остальных. Это подстановочный компонент. Из основной таблицы Food он будет брать целое число - значение поля FType. А из дочерней таблицы Tips этот компонент будет просматривать все значения поля TName. Когда пользователь выберет какой-нибудь тип блюда, целое число, соответствующее ключевому полю TKey, попадет в поле FType главной таблицы. Другими словами, у нас получилась связь один-ко-многим (многие блюда основной таблицы могут иметь одинаковый тип):
Выделите все компоненты, относящиеся к редактированию данных или перемещению по ним (начинающиеся на DB …), и в их свойстве DataSource выберите fDM.FoodDS. Затем с помощью свойства DataField подключите все DBEdit к соответствующему полю таблицы.
У компонента DBLookupComboBox установите следующие значения:
Свойство | Значение |
---|---|
DataSource | fDM.FoodDS |
DataField | FType |
ListSource | fDM.TipsDS |
KeyField | TKey |
ListField | TName |
Как видно из таблицы, компонент DBLookupComboBox имеет такие важные свойства:
- DataSource - свойство содержит ссылку на компонент TDataSource, связанный с основной таблицей.
- DataField - свойство указывает на имя ссылочного поля основной таблицы. В это поле после выбора значения из списка DBLookupComboBox попадает значение ключевого поля подстановочной таблицы.
- ListSource - свойство содержит ссылку на компонент TDataSource, связанный с подстановочной (дочерней) таблицей.
- KeyField - свойство содержит имя ключевого поля подстановочной таблицы. По этому полю ищется нужная подстановочная запись.
- ListField - свойство содержит имя поля подстановочной таблицы, по которому формируется список значений DBLookupComboBox. Эти значения также можно подставлять в основную таблицу в виде lookup (подстановочного) поля.
Если говорить еще проще, то при открытии основного и подстановочного наборов данных, компонент DBLookupComboBox соединяется с подстановочной таблицей, указанной в свойстве ListSource, и формирует список значений из поля, указанного в ListField. Далее, при редактировании основной таблицы, пользователь выбирает из списка DBLookupComboBox одно из значений. При этом DBLookupComboBox смотрит, какое значение выбранной записи в подстановочной таблице имеет ключевое поле KeyField. Как правило, это целое число. Это число DBLookupComboBox и заносит в ссылочное поле основной таблицы DataField. Похожим образом действует и компонент DBLookupListBox, разумеется, учитывая специфику компонента.
Форма fEditor предназначена для редактирования имеющегося блюда или добавления нового, в зависимости от того, каким способом вызвали форму. Поэтому здесь нам нужно лишь создать код для события закрытия формы onClose, куда пропишем:
if fDM.FoodT.Modified then if Application.MessageBox('Данные изменены! Сохранить?', 'Внимание!', MB_YESNO+MB_ICONQUESTION) <> IDYES then fDM.FoodT.Cancel else fDM.FoodT.Post;
Перейдем на главную форму. Командой File -> Use Unit добавим к главной форме новое окно. Пользователь должен иметь возможность редактировать имеющуюся запись, поэтому сгенерируем событие onDblClick для сетки DBGrid1, и пропишем туда следующий код:
fEditor.ShowModal;
Рядом с кнопкой "Типы блюд" добавим еще одну кнопку "Добавить блюдо". Сгенерируйте событие нажатия на эту кнопку и пропишите такой код:
fDM.FoodT.Append; fEditor.ShowModal;
Как видите, отличие кода заключается лишь в том, что при нажатии на кнопку добавляется новая запись, открывается редактор и пользователь редактирует ее. А если он дважды щелкнет по записи на сетке, то откроется тот же редактор, в который будет загружена текущая запись. Впрочем, благодаря навигатору DBNavigator, пользователь и там имеет возможность перемещаться по записям, добавлять или удалять записи. Мы, собственно, получили далекую от идеала, но вполне работоспособную программу. Сохраните проект, скомпилируйте и введите для примера несколько блюд:
Как видите, программа имеет множество недостатков: пользователь видит совершенно ненужные ему ключевые поля, в сетке он видит лишь номер типа блюда, но не видит название этого типа. В поле FVeget ему вручную приходится писать True или False вместо привычных Да/Нет. Еще недостаток: названия полей в сетке соответствуют названиям полей в таблице, а поле " FType " или " FVeget " мало что скажет пользователю. Исправлением этих недостатков займемся в следующей лекции, вместе с изучением свойств полей.