Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Разработка Windows - приложений в среде Borland. Учебное пособие

.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
1 Мб
Скачать
☆
ComboJob -> AddString("дворник");
ComboJob -> AddString("сторож");
ComboJob -> AddString("сантехник");
ComboJob -> SetSelIndex(0);
Метод AddString класса TComboBox добавляет строчку в спи­сок, а функция SetSelIndex позволяет выделить первую строчку в списке (строчки списка индексируются с нуля). Далее создайте заго­товку функции обработчика CBNSelchangeJob для события списка «выбор значения в списке» – SELCHANGE. Вызовите ClassExpert
→ найдите идентификатор списка → раскройте
"+" → найдите со-
бытие CBN_SELCHANGE → вызовите пункт контекстного меню Add handler → задайте имя метода CBNSelchangeJob.
В теле функции CBNSelchangeJob наберите код:
char buff[20];
ComboJob -> GetSelString( buff, 20);
EditJob -> SetText(buff);
Метод GetSelString позволяет считать в символьный массив из выбранной строки списка количество символов, ограниченное вто­рым параметром. Затем записанная в массив buff строка выводится в поле EditJob диалога. Запустите проект
на исполнение:
Теперь должность сотрудника можно задать, только выбрав её из списка штатного расписания. Выбранная должность отображается в соответствующем поле анкеты. Можно поместить комбинирован-
31
ный список не рядом с редактируемым полем EditJob, а вместо не­го, такой подход реализован в следующей главе. Подробнее списки и комбинированные списки рассматриваются в четвертой главе.
Реализуем возможность удаления выбранной записи. Добавьте в диалог кнопку «Удалить» и создайте функцию - обработчик BN- ClickedDel. Удаление текущей записи (iCur) приводит к уменьше­нию на
единицу номеров следующих записей (если удаляемая за­пись не последняя) и общего количества записей в базе данных (iMax). Кроме того, нужно проверить, не пуста ли база. Соответст­венно код функции может быть таким:
// INSERT>> Your code here. if(iMax == 0){ MessageBox ("База пуста", "Удаление", MB_OK | MB_ICONERROR); return;} if(iMax == 1){
iCur=0; iMax=0; EditName -> SetText(""); EditJob -> SetText(""); EditPay -> SetText(""); StaticNum -> SetText(""); return;
} if(iCur == iMax-1){ // выбранная запись – последняя
по порядку
iCur--; } else{
for(int i=iCur; i<iMax-1; i++) crew[i]=crew[i+1]; } iMax--; ShowRecord();
В приведенном коде при попытке удаления записей из пустой
базы данных выводится предупреждение в окно сообщений Mes-
sageBox:
Первым параметром этого метода является само сообщение,
второй и третий параметр, которые могут быть опущены, описыва-
32
ют заголовок окна MessageBox и позволяют выбрать набор команд­ных кнопок и дополнительный значок. Подробнее параметры функ­ции и возвращаемое ей значение рассматриваются в третьей главе.
Если в базе данных была всего одна запись, то база после ее
удаления станет пустой. В этом случае поля диалога чистятся. Если удаляемая запись была последней
по порядку в базе данных, умень­шается индекс текущей записи, то есть будет показана предыдущая запись. В противном случае все записи с индексом больше, чем у удаляемой записи, копируются в элементы массива crew с меньшим на единицу индексом. В этом случае текущей записью станет запись, следующая после удаляемой. Далее уменьшается параметр
iMax –
оставшееся количество записей и выводится новая текущая запись.
Сохранить наши данные можно как в текстовый, так и бинар­ный файл. Поскольку в нашем массиве crew все записи одного раз­мера (в байтах), удобнее работать с бинарным файлом прямого дос­тупа. Добавьте в диалог кнопку «Сохранить», создайте функцию – обработчик BNClickedSave и
поместите в нее инструкции:
// INSERT>> Your code here.
FILE *fp;
fp = fopen("work.zzz", "wb");
if( !fp) {
MessageBox("Не вышло... " , "Сохранение", MB_OK | MB_ICONWARNING); return;
}
fwrite( crew, sizeof(staff), iMax, fp); // записываем iMax записей
fclose(fp);
MessageBox("Получилось...", "Сохранение", MB_OK |
MB_ICO-NINFORMATION);
Имя файла work.zzz здесь фиксировано. В том случае, если файл создать не удалось, выводится предупреждение с помощью диалога MessageBox. В файл записывается iMax записей – элемен­тов массива crew. Осталось не забыть добавить директиву препро­цессора #include <stdio.h>!
Теперь добавьте в окно диалога кнопку «Открыть» и создайте функцию-обработчик для нее BNClickedOpen. Возможный код
этой
функции выглядит так:
// INSERT>> Your code here.
33
FILE *fp;
fp = fopen ("work.zzz", "rb");
if( !fp) {
MessageBox ("Не вышло... ", "Чтение", MB_OK |
MB_ICONWARNING); return; } iMax = fread ( crew, sizeof(staff), 100, fp); fclose(fp); MessageBox ("Получилось…", "Чтение", MB_OK | MB_ICONINFORMATION); if(iMax>0) { iCur = 0; ShowRecord(); }
Поскольку в файле проекта workapp.h размер массива crew ог-
раничен ста элементами, то пытаемся прочитать из файла не более 100 записей. Функция fread возвращает число реально считанных записей, которое мы запоминаем в переменной iMax. Если из файла считана хотя бы одна запись, программа сделает текущей первую
(индекс ноль) запись и выведет ее
в окно диалога.
В приведенных функциях имя файла базы данных фиксировано.
Предоставить пользователю возможность выбора имени файла в операциях чтения и записи можно, добавив еще одно редактируемое поле для задания нужного имени файла. Допустим, такое поле соз­дано и названо EditFile. Тогда достаточно добавить в начало функ­ций, работающих с
файлами, следующий фрагмент кода:
char buff[20]; EditFile -> GetText(buff, 20);
считываем из поля EditFile имя файла и используем считанное имя при открытии файла:
fp = fopen (buff , "wb"); // при записи или fp = fopen (buff , "rb"); // при чтении
Удобнее для задания имени файла использовать стандартный
диалог TInputDialog. Для этого отредактируйте файл ресурсов как текст: закройте редактор ресурсов, выберите workapp.rc в окне про­екта → контекстное меню → View → TextEdit. Добавьте после ди­рективы препроцессора:
34
#include "workapp.rh"
директиву, подключающую ресурс диалога TInputDialog:
#include <owl/inputdia.rc>
сохраните и закройте файл.
Откройте файл wrkdlgcl.cpp. Добавьте после директивы пре-
процессора:
#include "wrkdlgcl.h"
директиву:
#include <owl/inputdia.h>
Затем добавьте в начало функций, работающих с файлами, следую­щий фрагмент кода:
char buff[20]={0}; TInputDialog(this, "Введите имя файла", "Имя файла",
buff, sizeof( buff)).Execute();
и используйте считанное в массив buff имя при открытии файла. Окно
созданного модального диалога будет иметь вид:
Текст, введенный в редактируемое поле диалога TIinputDialog,
копируется в массив buff только в том случае, если будет нажата кнопка “OK”. Если пользователь нажмет кнопку “Cancel”, в массиве buff останется пустая строка и файл не будет открыт. Подробнее па­раметры диалога и возвращаемое значение рассматриваются в третьей главе.
35
Далее реализуем операцию сортировки записей по именам со-
трудников. Поместите кнопку «Сортировать», задайте имя функции обработчика события Click для этой кнопки – BNClickedSort и до­бавьте в заготовку созданной функции инструкции:
// INSERT>> Your code here. int last, n = iMax-1; staff temp; if(iMax == 0){ MessageBox ("База пуста", "Сортировка", MB_OK | MB_ICONERROR); return;} do{
last=0;
for(int i=0; i<n; i++)
if(strcmp(crew[i].name , crew[i+1]. name) > 0){
temp=crew[i]; crew[i]=crew[i+1]; crew[i+1]=temp; last=i;
}
n=last; }while(last != 0); iCur = 0; ShowRecord();
здесь применяется обменная сортировка методом «пузырька». Более эффективные алгоритмы сортировки можно найти в щей литературе, например в [11].
Наконец добавьте кнопку «Поиск», задайте имя функции
BNClickedSearch и добавьте в заготовку созданной функции инст­рукции:
// INSERT>> Your code here.
int left=0, right = iMax, middle = (left+right)/2; char buff[20] = {0}; TInputDialog (this, "Поиск", "Введите искомое имя",
buff, sizeof(buff)).Execute(); if( ! buff[0]) {
MessageBox ("Кого ищем-то? ", "Поиск", MB_OK |
MB_ICONERROR); return; } BNClickedSort(); // записи необходимо упорядочить по именам do{
36
соответствую-
if(strcmp(crew[middle].name,buff) > 0 ) right=middle;
else left = middle;
middle = (left+right)/2; }while(left != middle); if( strcmp(crew[middle].name , buff) ){
MessageBox ("Клиент не найден", "Поиск", MB_OK |
MB_ICONWARNING); return; } MessageBox ("Клиент найден", "Поиск", MB_OK | MB_ICONINFORMATION); iCur = middle; ShowRecord();
В этой функции применяется алгоритм бинарного поиска [11].
Необходимо учитывать, что алгоритм бинарного поиска применим только для массивов, упорядоченных по значениям искомого поля – в данном случае по именам.
Алгоритмы, реализованные для операций поиска и сортировки,
являются стандартными, и, безусловно, хорошо известны читателю. Поэтому, в целях экономии места при изложении детали реализации обсуждаться
здесь не будут. Впрочем, как уже отмечалось, многие
алгоритмы подробно рассматриваются, например в [11].
Наш проект приобрел следующий вид:
37
ГЛАВА II. КЛАССЫ, КОНТЕЙНЕРЫ И АЛГОРИТМЫ
ЛАСС STAFF
К
Так же как и структура, класс является пользовательским типом
данных. Однако в отличие от структуры языка C класс объединяет данные и методы работы с данными. Заметим, что в языке C++ клас­сы и структуры весьма похожи: структуры С++, как и классы, могут включать в себя методы, конструкторы и т.п. Основные отличия классов и структур проявляются лишь при использовании механиз­мов наследования. Так как мы не собираемся строить иерархию классов, в нашем приложении, строго говоря, можно описать струк­туру, используя ключевое слово struct вместо class.
Создайте два новых модуля: файл staff.h, содержащий интер-
фейс класса staff:
#ifndef __STAFF_H #define __STAFF_H
#define MaxChar 20 #include "string.h"
class staff{ // состав сотрудников, штат
int pay; // оклад public:
char name[MaxChar]; // имя
char job[MaxChar]; // должность
staff(int p=0, char* n= "Unknown", char* j="***");
staff(const staff& s);
staff& operator=(const staff& s);
~staff(); // методы доступа к скрытому полю pay
int GetPay();
void SetPay(int p); // методы, используемые при упорядочивании записей
friend bool operator<(const staff& sl, const staff& sr);
// friend bool CompName(const staff& sl, const staff& sr); // friend bool CompJob(const staff& sl, const staff& sr);
38
// методы, используемые при поиске записей
friend bool operator==(const staff& sl, const staff& sr);
// friend bool SearchName(staff sl, staff sr); // friend bool SearchJob(staff sl, staff sr);
};
extern staff crew[100]; extern int iMax; extern int iCur;
#endif
и файл staff.cpp, описывающий реализацию класса:
#include "staff.h" const int MinPay =850; // минимальный оклад staff::staff(int p, char* n, char* j){
pay=p; strcpy(name,n); strcpy(job,j); } staff::staff(const staff& s){ operator=(s);} staff& staff::operator=(const staff& s){
if( this==&s) return *this;
pay=s.pay; strcpy(name,s.name); strcpy(job,s.job);
return *this; } staff::~staff(){} // методы доступа к скрытому полю pay int staff::GetPay(){ return pay;} void staff::SetPay(int p){
if( p< MinPay) pay=MinPay;
else pay=p; } // методы, используемые при упорядочивании записей bool operator<(const staff& sl, const staff& sr){
return sl.pay<sr.pay; }
// bool CompName(const staff& sl, const staff& sr){ // return strcmp(sl.name,sr.name)<0; // }
39
// bool CompJob(const staff& sl, const staff& sr){ // return strcmp(sl.job,sr.job)<0; // }
// методы, используемые при поиске записей bool operator==(const staff& sl, const staff& sr){
return sl.pay==sr.pay; }
// bool SearchName(staff sl, staff sr){ // return !strcmp(sl.name,sr.name); // } // bool SearchJob(staff sl, staff sr){ // return !strcmp(sl.job,sr.job); // }
staff crew[100]; int iMax=0; int iCur=0;
Можно было не определять в класее:
- конструктор staff(int p=0, char* n= “Unknown”, char* j="***"),
- конструктор копий – staff(const staff& s),
- операцию присваивания – staff& staff::operator=(const staff& s)
- и деструктор – ~staff(){},
так как те, что будут созданы транслятором, вполне подходят в дан­ном случае. Тем не менее, будьте внимательны, так как в мых далее фрагментах кода вызывается именно этот, определенный выше конструктор. Поле pay (оклад сотрудника) в классе staff объ­явлено скрытым для того, чтобы продемонстрировать работу со скрытыми полями.
В модулях staff.h и staff.cpp закомментированы строчки, свя-
занные с объявлением и реализацией дружеских функций Comp- Name, CompJob, SearchName и SearchJob. Эти функции понадо­бятся позднее, при реализации операций поиска и сортировки по имени или должности сотрудника. Задачи поиска и сортировки об­суждаются в разделе «Алгоритмы. Сортировка и поиск».
Теперь можно вернуться к проекту, сохраненному ранее в папке
«Заготовка». Скопируйте эту папку. Работать будем с копией, а к оригиналу мы еще вернемся. Поместите в описанием класса staff, откройте проект, выделите в окне проекта
40
папку с проектом файлы с
приводи-
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]