Опубликован: 17.08.2010 | Доступ: свободный | Студентов: 999 / 59 | Оценка: 4.11 / 3.89 | Длительность: 29:38:00

Самостоятельная работа 15: Создание СУБД средствами BDE (на примере протокола экзамена кандидатов в водители)

Программирование контекстного меню

Теперь продублируем наиболее важные команды управления данными через контекстное меню. Контекстное меню привяжем к компоненту DBGrid1.

  • Поместите на форму невизуальный компонент PopupMenu из вкладки Standard и определите его свойство Name=PopupRecord.
  • Выделите на форме элемент DBGrid1 и присоедините к нему это контекстное меню, задав значение свойства PopupMenu=PopupRecord.
  • Двойным щелчком на компоненте с именем PopupRecord вызовите редактор меню, в котором определите свойства в соответствии с таблицей
    Свойства контекстного меню PopupRecord для компонента DBGrid1 проекта
    Name Caption
    PopupInsert Вставить
    PopupAppend Добавить
    PopupDelete Удалить

Теперь внешне в редакторе контекстное меню должно выглядеть так


Теперь нужно прикрепить к опциям контекстного меню нужные обработчики, чтобы продублировать опции раздела 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 объявление функции-обработчика
    Объявление функции-обработчика события 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 определение функции-обработчика
    Определение функции-обработчика события OnHint
    
    void __fastcall TMainForm::OnHint(TObject *Sender)
    {
      StatusBar1->SimpleText = Application->Hint;  
    }
  • Включите панель View/ClassExplorer, найдите в ней функцию FormCreate (мы ее создавали ранее) и двойным щелчком быстро адресуйтесь к ней в текстовом редакторе.

  • Другой способ быстрой адресации :

    Выберите главную форму MainForm в селекторе объектов Object TreeView. Переключитесь на вкладку Events в инспекторе объектов, найдите созданный нами ранее обработчик события OnCreate для главной формы. Двойной щелчок на поле значений обработчика приведет к быстрой адресации к его коду в текстовом редакторе.

  • Введите переадресацию события OnHint приложения на нашу функцию-обработчик (ключевое слово this в данном коде указывать необязательно)
    Переадресация на наш обработчик
    void TMainForm::FormCreate(TObject *Sender)
    {
      Table1->Open();
      countRecord = Table1->RecordCount;
    	
      // Настройка подсказок строки состояния
      Application->OnHint = &this->OnHint;
    }

Теперь все готово для отображения длинных подсказок объектом приложения. Но нужно ввести в каждый компонент интерфейса (кнопки, пункты меню) сами подсказки. Для этого нужно последовательно выделять каждый компонент приложения, включать свойство ShowHint, если мы хотим отображения всплывающих (коротких) подсказок, и заполнять нужным текстом свойство Hint по формату

всплывающая_подсказка | текст_строки_состояния
  • Выделите в режиме Design кнопку btnBof на панели инструментов. Проверьте, что свойство ShowHint для отображения всплывающей подсказки включено, а в свойство Hint добавьте текст

    В начало|Перейти на первую запись таблицы

  • Повторите эти шаги для других элементов в соответствии с таблицей. Для выделения компонентов меню пользуйтесь редактором меню или инструментом Object TreeView. Следите за тем, чтобы для кнопок и соответствующих пунктов меню текст подсказок был одинаков, иначе можно ввести в заблуждение, утомить и огорчить бедного пользователя. Обратите внимание, что для объектов-пунктов меню свойство ShowHint отсутствует, поскольку всплывающие подсказки для них не предусмотрены, чтобы не создавать избыточности информации. Поэтому в свойстве Hint опций меню можно короткую подсказку не указывать (а можно и указать, только она не будет отображаться).
    Подсказки для элементов интерфейса приложения
    Тип Элемент Hint ShowHint
    Быстрые кнопки btnBof В начало | Перейти на первую запись true
    btnEof В конец | Перейти на последнюю запись true
    btnPrior Предыдущий | Перейти на одну запись вверх true
    btnNext Следующий | Перейти на одну запись вниз true
    btnInsert Вставить | Вставить одну запись перед текущей true
    btnAppend Добавить | Добавить одну новую запись в конец таблицы true
    btnDelete Удалить | Удалить текущую запись из таблицы true
    btnSave Сохранить | Записать внесенные изменения на жесткий диск true
    Главное меню FileSave Записать внесенные изменения на жесткий диск нет
    FileExit Завершение работы приложения нет
    EditBof Перейти на первую запись таблицы нет
    EditEof Перейти на последнюю запись нет
    EditPrior Перейти на одну запись вверх нет
    EditNext Перейти на одну запись вниз нет
    EditInsert Вставить одну запись перед текущей нет
    EditAppend Добавить одну новую запись в конец таблицы нет
    EditDelete Удалить текущую запись из таблицы нет
    Контекстное меню PopupInsert Вставить одну запись перед текущей нет
    PopupAppend Добавить одну новую запись в конец таблицы нет
    PopupDelete Удалить текущую запись из таблицы нет
    StatusBar1 Место для отображения длинных подсказок false
  • Запустите проект и проверьте функционирование длинных подсказок.

Программирование выхода

При завершении работы приложения нам нужно проверить, сохранялись ли на диске последние изменения данных, выполненные пользователем. Несохраненные изменения можно обнаружить по цвету панели Panel1->Color == clRed. Но это будет промежуточный признак, на который лучше не операться, поскольку мы можем его изменить и забыть, что от него зависит дальнейшая логика кода. Проверять нужно специально введенный флаг, который будем поднимать при любых изменениях в данных.

  • Введите в класс 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 формы

    Инициализация флага изменения данных
    void __fastcall TMainForm::FormCreate(TObject *Sender)
    {
      Table1->Open();
      countRecord = Table1->RecordCount;
      flagIsModified = false;
    	
      // Настройка подсказок строки состояния
      Application->nHint = &this->OnHint;
    }

    Для установки признака модификации данных используем события компонента Table1

    События компонента Table1
    AfterDelete После вызова Delete
    AfterEdit После вызова Edit
    AfterInsert После вызова Insert или Append
  • Выделите компонент 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

  • В обработчике 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

Теперь, когда мы имеем флаг flagIsModified, поддерживаемый в актуальном состоянии, можно приступать к коду завершения приложения. Приложение можно завершить щелчком по кнопке системного меню (кнопка-крестик) или программным путем с помощью выполнения метода Close() главной формы (а у нас пока всего одна форма и есть).

  • Создайте обработчик для элемента меню FileExit опции Файл/Выход завершения приложения программным путем и заполните его так
    Обработчик опции FileExit завершения приложения
    void __fastcall TMainForm::FileExitClick(TObject *Sender)
    {
      this->Close();
    }

Для того, чтобы проверить, сохранялись изменения перед завершением или нет, воспользуемся событием OnCloseQuery формы, в обработчике которого предупредим пользователя о несохраненных изменениях, если таковые будут. В обработчик события OnCloseQuery передается ссылка на булеву переменную CanClose, которая является флагом продолжения закрытия и изначально равна true. Если эту переменную сбросить, то закрытие формы прекратится.

  • Через инструмент 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
    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 Время в секундах, затраченное на ответ

Сама промежуточная таблица может выглядеть так


Александр Даниленко
Александр Даниленко
Стоит Windows 8 Pro, Visual Studio 2010 Express Edition .