Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Разработка Windows - приложений в среде Borland. Учебное пособие
.pdf
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
папку с проектом файлы с
приводи-
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
