| Стоит Windows 8 Pro, Visual Studio 2010 Express Edition . | 
Самостоятельная работа 15: Создание СУБД средствами BDE (на примере протокола экзамена кандидатов в водители)
Программирование контекстного меню
Теперь продублируем наиболее важные команды управления данными через контекстное меню. Контекстное меню привяжем к компоненту DBGrid1.
- 
 Поместите на форму невизуальный компонент PopupMenu  из вкладки Standard  и определите его свойство Name=PopupRecord. Поместите на форму невизуальный компонент PopupMenu  из вкладки Standard  и определите его свойство Name=PopupRecord.
- 
 Выделите на форме элемент DBGrid1  и присоедините к нему это контекстное меню, задав значение свойства PopupMenu=PopupRecord. Выделите на форме элемент DBGrid1  и присоедините к нему это контекстное меню, задав значение свойства PopupMenu=PopupRecord.
- 
 Двойным щелчком на компоненте с именем PopupRecord  вызовите редактор меню, в котором определите свойства в соответствии с таблицей Двойным щелчком на компоненте с именем PopupRecord  вызовите редактор меню, в котором определите свойства в соответствии с таблицей
Теперь внешне в редакторе контекстное меню должно выглядеть так
Теперь нужно прикрепить к опциям контекстного меню нужные обработчики, чтобы продублировать опции раздела Edit главного меню. Прикреплять обработчики будем по такой же схеме, что и для главного меню: вызовем двойным щелчком на компоненте TPopupMenu редактор меню. Установим инструмент Object Inspector на вкладку Events. В редакторе меню будем выделять соответствующий пункт, а в инспекторе объектов в поле события OnClick в раскрывающемся списке будем выбирать нужный обработчик.
- 
 Выполните закрепление обработчиков за всеми тремя опциями контекстного меню Выполните закрепление обработчиков за всеми тремя опциями контекстного меню
- 
 Запустите приложение и проверьте работоспособность всех меню. Запустите приложение и проверьте работоспособность всех меню.
Программирование строки состояния
В нижней части формы мы разместили ранее строку состояния StatusBar1. В ней должны отображаться длинные подсказки о назначении элементов пользовательского интерфейса, если пользователь наведет курсор на соответствующий элемент управления.
Приложение в C++Builder является объектом Application, определенным по умолчанию. Когда свойство ShowHint объекта Application имеет значение true (значение по умолчанию) и курсор мыши находится над компонентом запущенного приложения, то независимо от того, включено или нет свойство ShowHint этого компонента, генерируется событие Hint родительского объекта Application. При этом значение свойства Hint объекта компонента копируется в значение Hint объекта Application. Если перехватить и обработать событие OnHint объекта Application и в нем вывести поступившее от компонента значение свойства Hint в строку состояния, то получим управление подсказками. Перехватим событие OnHint объекта приложения TApplication, для чего переопределим в своем приложении его обработчик
- 
 Добавьте в описание класса TMainForm  файла UMain.h  объявление  функции-обработчика Добавьте в описание класса TMainForm  файла UMain.h  объявление  функции-обработчикаОбъявление функции-обработчика события OnHint class TMainForm : public TForm { ............................................ private: // User declarations int countRecord; public: // User declarations __fastcall TMainForm(TComponent* Owner); private: void __fastcall OnHint(TObject *Sender); };
Модификатор __fastcall нужно указывать обязательно для согласования с библиотечным форматом метода OnHint.
- 
 Добавьте в конец файла UMain.cpp  определение  функции-обработчика Добавьте в конец файла UMain.cpp  определение  функции-обработчикаОпределение функции-обработчика события OnHint void __fastcall TMainForm::OnHint(TObject *Sender) { StatusBar1->SimpleText = Application->Hint; }
- 
 Включите панель View/ClassExplorer, найдите в ней функцию FormCreate  (мы ее создавали ранее) и двойным щелчком быстро адресуйтесь к ней в текстовом редакторе. Включите панель View/ClassExplorer, найдите в ней функцию FormCreate  (мы ее создавали ранее) и двойным щелчком быстро адресуйтесь к ней в текстовом редакторе.
- 
 Другой способ быстрой адресации : Другой способ быстрой адресации :Выберите главную форму MainForm в селекторе объектов Object TreeView. Переключитесь на вкладку Events в инспекторе объектов, найдите созданный нами ранее обработчик события OnCreate для главной формы. Двойной щелчок на поле значений обработчика приведет к быстрой адресации к его коду в текстовом редакторе. 
- 
 Введите переадресацию события OnHint  приложения на нашу функцию-обработчик (ключевое слово this  в данном коде указывать необязательно) Введите переадресацию события OnHint  приложения на нашу функцию-обработчик (ключевое слово this  в данном коде указывать необязательно)Переадресация на наш обработчик void TMainForm::FormCreate(TObject *Sender) { Table1->Open(); countRecord = Table1->RecordCount; // Настройка подсказок строки состояния Application->OnHint = &this->OnHint; }
Теперь все готово для отображения длинных подсказок объектом приложения. Но нужно ввести в каждый компонент интерфейса (кнопки, пункты меню) сами подсказки. Для этого нужно последовательно выделять каждый компонент приложения, включать свойство ShowHint, если мы хотим отображения всплывающих (коротких) подсказок, и заполнять нужным текстом свойство Hint по формату
всплывающая_подсказка | текст_строки_состояния
- 
 Выделите в режиме Design  кнопку btnBof  на панели инструментов. Проверьте, что свойство ShowHint  для отображения всплывающей подсказки включено, а в свойство Hint  добавьте текст Выделите в режиме Design  кнопку btnBof  на панели инструментов. Проверьте, что свойство ShowHint  для отображения всплывающей подсказки включено, а в свойство Hint  добавьте текстВ начало|Перейти на первую запись таблицы 
- 
 Повторите эти шаги для других элементов в соответствии с таблицей. Для выделения компонентов меню пользуйтесь редактором меню или инструментом Object TreeView. Следите за тем, чтобы для кнопок и соответствующих пунктов меню текст подсказок был одинаков, иначе можно ввести в заблуждение, утомить и огорчить бедного пользователя. Обратите внимание, что для объектов-пунктов меню свойство ShowHint  отсутствует, поскольку всплывающие подсказки  для них не предусмотрены, чтобы не создавать избыточности информации. Поэтому в свойстве Hint  опций меню можно короткую подсказку не указывать (а можно и указать, только она не будет отображаться). Повторите эти шаги для других элементов в соответствии с таблицей. Для выделения компонентов меню пользуйтесь редактором меню или инструментом Object TreeView. Следите за тем, чтобы для кнопок и соответствующих пунктов меню текст подсказок был одинаков, иначе можно ввести в заблуждение, утомить и огорчить бедного пользователя. Обратите внимание, что для объектов-пунктов меню свойство ShowHint  отсутствует, поскольку всплывающие подсказки  для них не предусмотрены, чтобы не создавать избыточности информации. Поэтому в свойстве Hint  опций меню можно короткую подсказку не указывать (а можно и указать, только она не будет отображаться).
- 
 Запустите проект и проверьте функционирование длинных подсказок. Запустите проект и проверьте функционирование длинных подсказок.
Программирование выхода
При завершении работы приложения нам нужно проверить, сохранялись ли на диске последние изменения данных, выполненные пользователем. Несохраненные изменения можно обнаружить по цвету панели Panel1->Color == clRed. Но это будет промежуточный признак, на который лучше не операться, поскольку мы можем его изменить и забыть, что от него зависит дальнейшая логика кода. Проверять нужно специально введенный флаг, который будем поднимать при любых изменениях в данных.
- 
 Введите в класс TMainForm  файла UMain.h  объявление логической переменной flagIsModified Введите в класс TMainForm  файла UMain.h  объявление логической переменной flagIsModifiedОбъявление флага изменения данных в классе TMainForm class TMainForm : public TForm { .............................................. private: // User declarations int countRecord; bool flagIsModified; public: // User declarations __fastcall TMainForm(TComponent* Owner); private: void __fastcall OnHint(TObject *Sender); };
- 
 Инициализируйте флаг в функции-обработчике события OnCreate  формы Инициализируйте флаг в функции-обработчике события OnCreate  формыИнициализация флага изменения данных void __fastcall TMainForm::FormCreate(TObject *Sender) { Table1->Open(); countRecord = Table1->RecordCount; flagIsModified = false; // Настройка подсказок строки состояния Application->nHint = &this->OnHint; }Для установки признака модификации данных используем события компонента Table1 
- 
 Выделите компонент Table1  и через вкладку Events  инспектора объектов создайте (вначале заполните поле с именем, затем нажмите клавишу Enter ) обработчик для события AfterEdit  с именем DataModified Выделите компонент Table1  и через вкладку Events  инспектора объектов создайте (вначале заполните поле с именем, затем нажмите клавишу Enter ) обработчик для события AfterEdit  с именем DataModified
- 
 Заполните обработчик следующим кодом поднятия флага модификации данных Заполните обработчик следующим кодом поднятия флага модификации данныхКод обработчика поднятия флага модификации данных void __fastcall TMainForm::DataModified(TDataSet *DataSet) { flagIsModified = true; Panel1->Color = clRed; }Обратите внимание, что в обработчике мы предусмотрели изменение цвета панели Panel1 в одном месте, а ранее мы вынуждены были размещать его в нескольких обработчиках. Найдите такой код в обработчиках btnInsertClick, btnAppendClick, btnDeleteClick и удалите его, после чего обработчики должны выглядеть так Удаляем излишний код в обработчиках void __fastcall TMainForm::btnInsertClick(TObject *Sender) { Table1->Insert(); // Panel1->Color = clRed; } //--------------------------------------------------------------------------- void __fastcall TMainForm::btnAppendClick(TObject *Sender) { DBGrid1->SelectedIndex = 0; // Выделить первое поле Table1->Append(); // Panel1->Color = clRed; } //--------------------------------------------------------------------------- void __fastcall TMainForm::btnDeleteClick(TObject *Sender) { Table1->Delete(); // Panel1->Color = clRed; }
- 
 Этот же обработчик DataModified  прикрепите к событиям AfterDelete  и AfterInsert Этот же обработчик DataModified  прикрепите к событиям AfterDelete  и AfterInsert
- 
 В обработчике btnSaveClick  поместите код сброса флага В обработчике btnSaveClick  поместите код сброса флагаКод обработчика сброса флага модификации данных void __fastcall TMainForm::btnSaveClick(TObject *Sender) { Panel1->Color = clGreen; if(!Table1->Modified && !flagIsModified) return; Table1->Edit(); Table1->Post(); Table1->Close(); Application->ProcessMessages(); //Прокачка сообщений Table1->Open(); flagIsModified = false; // countRecord = Table1->RecordCount; }
Обратите внимание, что прежний признак countRecord нам теперь не нужен, поскольку мы ввели более универсальный флаг модификации.
- 
 Удалите из кода все, что связано с переменной countRecord, включая ее объявление в файле UMain.h Удалите из кода все, что связано с переменной countRecord, включая ее объявление в файле UMain.h
Теперь, когда мы имеем флаг flagIsModified, поддерживаемый в актуальном состоянии, можно приступать к коду завершения приложения. Приложение можно завершить щелчком по кнопке системного меню (кнопка-крестик) или программным путем с помощью выполнения метода Close() главной формы (а у нас пока всего одна форма и есть).
- 
 Создайте обработчик для элемента меню FileExit  опции Файл/Выход  завершения приложения программным путем и заполните его так Создайте обработчик для элемента меню FileExit  опции Файл/Выход  завершения приложения программным путем и заполните его такОбработчик опции FileExit завершения приложения void __fastcall TMainForm::FileExitClick(TObject *Sender) { this->Close(); }
Для того, чтобы проверить, сохранялись изменения перед завершением или нет, воспользуемся событием OnCloseQuery формы, в обработчике которого предупредим пользователя о несохраненных изменениях, если таковые будут. В обработчик события OnCloseQuery передается ссылка на булеву переменную CanClose, которая является флагом продолжения закрытия и изначально равна true. Если эту переменную сбросить, то закрытие формы прекратится.
- 
 Через инструмент Object TreeView  выделите экземпляр MainForm, откройте вкладку Events  и двойным щелчком на событии OnCloseQuery  создайте обработчик с со следующим кодом Через инструмент Object TreeView  выделите экземпляр MainForm, откройте вкладку Events  и двойным щелчком на событии OnCloseQuery  создайте обработчик с со следующим кодомОбработчик события закрытия формы void __fastcall TMainForm::FormCloseQuery(TObject *Sender, bool &CanClose) { if(flagIsModified) { int result = Application->MessageBox( "Сохранить изменения в данных?", "Завершение работы...", MB_YESNOCANCEL | MB_ICONWARNING ); switch(result) { case IDYES: btnSaveClick(Sender); // Вызвали наш обработчик break; case IDCANCEL: CanClose = false; return; } Table1->Close(); } }
- 
 Подобным же образом предупредите пользователя при удалении записи в обработчике btnDeleteClick Подобным же образом предупредите пользователя при удалении записи в обработчике btnDeleteClickДополнение обработчика btnDeleteClick void __fastcall TMainForm::btnDeleteClick(TObject *Sender) { int result = Application->MessageBox( "Удалить запись?", "Удаление", MB_YESNO | MB_ICONWARNING ); if(result == IDYES) Table1->Delete(); }
- 
 Испытайте новые возможности. Теперь при удалении записи или попытке выхода при несохраненных изменениях будут выдаваться следующие окна сообщений Испытайте новые возможности. Теперь при удалении записи или попытке выхода при несохраненных изменениях будут выдаваться следующие окна сообщений
Создание СУБД средствами BDE (на примере протокола экзамена кандидатов в водители)
Передача результатов экзамена
Кандидат в водители, сдававший теоретический экзамен на другой программе, ответил на двадцать вопросов. Его ответы экзаменационная программа сохранила в промежуточном файле tmp_base.dbf, структура которого показана в таблице
| Структура таблицы tmp_base.dbf | ||||
|---|---|---|---|---|
| Field Name | Type | Size | Dec | Пояснения | 
| Name | Numeric | 3 | 0 | Регистрационный номер экзаменуемого (уникальный идентификатор результатов, равный значению поля Number в таблице Base0.dbf) | 
| Question | Numeric | 3 | 0 | № ответа (№ записи в файле Exam.dbf, в котором хранятся номера вопросов) | 
| Answer | Numeric | 1 | 0 | № правильного ответа | 
| Result | Logical | 1 | Да/Нет (Верно/Неверно) | |
| Time | Numeric | 2 | 0 | Время в секундах, затраченное на ответ | 
Сама промежуточная таблица может выглядеть так
 
                             








