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

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

.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
1 Мб
Скачать
☆
первую строчку (work.exe [AppExpert]) и выберите в контекстном меню пункт Add node.
В вызванном диалоге Add to Project List откройте файл staff.cpp.
В окне проекта появится соответствующий узел. Можно также доба­вить к узлу проекта staff.cpp узел staff.h, хотя это не обязательно:
41
Откройте модуль workapp.h и вместо строк кода с описанием
структуры staff и массива crew:
struct staff{ // состав сотрудников, штат
char name[20]; // имя
char job[20]; // должность
int pay; // оклад };
staff crew[100]; // массив записей int iCur; // номер текущей записи int iMax; // количество записей в
базе данных
поместите директиву препроцессора #include "staff.h". Если бы сре­ди полей класса не было скрытых (в нашем случае – поле pay), то больше никаких проверки этого утверждения достаточно перенести (или добавить) объявление «public:» перед строчкой:
int pay; // оклад
в модуле staff.h. Более того, будет работать без дополнительных из­менений и полное приложение, созданное в предыдущей главе.
Если же оставить поле «pay» скрытым, то необходимо внести
незначительные изменения в созданный проект. В ции класса диалога (wrkdlgcl.cpp) в коде функции обработчика кнопки «Добавить» (BNClickedAdd) вместо инструкции:
crew[iMax].pay=atoi(buff);
написать:
crew[iMax].SetPay(atoi(buff));
и в методе ShowRecord (показать запись) заменить
wsprintf(buff,"%i",crew[iCur]. pay );
изменений в приложении можно и не делать. Для
модуле реализа-
42
на
wsprintf(buff,"%i",crew[iCur].GetPay());
Запустите приложение. Обратите внимание, что пользователю
не удастся задать оклад меньше минимального значения 850, опре­деляемого константой:
const int MinPay =850;
заданной в файле wrkdlgcl.cpp.
Более полный проект, реализованный в предыдущей главе, так-
же будет функционировать после внесения аналогичных изменений.
С
НОВА КОМБИНИРОВАННЫЙ СПИСОК
В предыдущей главе комбинированный список, предназначен-
ный для выбора должности, дополняет соответствующее поле на­шей анкеты. Рассмотрим теперь возможность использования списка вместо редактируемого поля. Выберите в окне проекта файл workapp.rc и запустите редактор ресурсов (двойной щелчок по узлу в окне проекта или контекстное меню узла → View → Edit
Resources). Раскройте пункт DIALOG
(щелкнув по значку +), выбе-
рите IDD_CLIENT (2000) и дважды щелкните мышкой (или контек­стное меню → Edit…). Удалите поле, предназначенное для задания должности, затем выберите на панели инструментов элемент Combo Box (комбинированный список) и поместите его в окне диалога на место удаленного поля. Задайте имя объекта «список» (ClassExpert
→ идентификатор списка → Add instance variable
→ и введите имя
ComboJob). Как и в первой главе найдите через Class Expert вирту­альную функцию SetupWindow класса диалога TWorkDlgClient и перегрузите её – контекстное меню → Add handler. Код функции аналогичен рассмотренному ранее:
void TWorkDlgClient::SetupWindow() {
TDialog::SetupWindow();
// INSERT>> Your code here.
43
ComboJob -> AddString("дворник");
ComboJob -> AddString("сторож");
ComboJob -> AddString("сантехник");
ComboJob -> SetSelIndex(0); }
Далее отредактируйте модуль wrkdlgcl.cpp – в функции-
обработчике BNClickedAdd для кнопки «добавить» замените инст­рукцию:
EditJob -> GetText(buff,20);
на:
ComboJob -> GetText(buff,20);// копируем текст из поля списка в
массив
и в методе ShowRecord замените инструкцию:
EditJob -> SetText(crew[iCur].job);
на следующие:
int index = ComboJob -> FindString(crew[iCur].job, 0); if (index<0) MessageBox ("Должность не\nсоответствует-\nштат-
ному расписанию",
"Должность", MB_OK | MB_ICONSTOP); ComboJob -> SetText(crew[iCur].job);
Здесь предусмотрена проверка должности
ненной в файле, с имеющимся списком должностей. Метод Find- String позволяет найти индекс строки списка, начинающейся со строки, являющейся первым параметром метода FindString. В случае если в списке искомой строки нет, возвращается значение –1 и вы­дается предупреждение. Подобная проверка желательна, если, на­пример, список имеющихся должностей хранился в отдельном ле инициализации и был изменен со дня последнего сохранения ба­зы данных.
для записи, сохра-
фай-
44
Запустите программу на исполнение. Интерфейс стал более на-
глядным:
ОНТЕЙНЕРЫ. РЕДАКТИРОВАНИЕ
К
До сих пор для отображения наших данных в памяти мы ис-
пользовали массивы. Это удобно когда максимальное количество записей известно заранее, в противном случае мы либо искусственно ограничиваем возможности нашей программы, либо предъявляем завышенные требования к ресурсам, например, если зарезервируем массив размерности 1000000. Динамические массивы в данном слу­чае мало эффективны
, так как в процессе работы требуемый размер базы данных может измениться, что потребует усложнения про­граммы. Кроме того, при удалении записей нам приходится «пере­писывать» большую часть массива (посмотрите в предыдущей главе действия, выполняемые при нажатии кнопки «удалить»). Все эти проблемы легко обойти, если вместо массивов использовать контей­нерные классы,
являющиеся частью стандарта языка С++. Контей­неры и алгоритмы входят в состав STL (Standard Template Library – стандартная библиотека шаблонов) [2-6, 11]. В данной библиотеке также реализованы полезные алгоритмы, например поиска и сорти­ровки, которые позволяют упростить разработку приложения. Для наших целей подходят различные контейнеры. Выбор вектора обу­словлен только тем, что он допускает индексацию, работа с
ним аналогична работе с массивом и потребует наименьших изменений в уже созданных к этому моменту проектах.
45
Итак, приступим. Откройте проект и в заголовочном файле
staff.h замените строку:
extern staff crew[100];
на:
#include <vector.h>
using namespace std; extern vector <staff> crew;
Затем в модуле реализации класса staff.cpp замените объявление
массива:
staff crew[100];
на контейнер:
vector <staff> crew;
Мы объявили вместо массива пустой вектор. Можно создать вектор из ста записей – vector <staff> crew(100) и не менять осталь­ной код, но тогда нам по-прежнему придется следить за тем когда количество записей превысит сотню, программа увеличивала бы размер вектора.
Поскольку наш вектор будет иметь размер, в точности совпа­дающий с количеством записей, то при добавлении новой записи нужно увеличивать вектор. Для этого в файл реализации класса диа­лога (wrkdlgcl.cpp) добавьте в код функции обработчика кнопки
«Добавить» (BNClickedAdd) после строчки:
char buff[20];
инструкцию:
crew.push_back(staff());
то есть мы сначала добавляем в конец вектора запись со значениями полей, заданными по умолчанию: оклад - 0, имя – “Unknown”,
46
, чтобы,
должность - "***", а затем заменяем значения полей данными, счи­танными из соответствующих полей диалогового окна. При этом индекс последней добавленной записи равен количеству записей до этого добавления, то есть iMax. Вместо переменной iMax, значение которой определяет количество записей в базе данных, можно ис­пользовать метод контейнера size, возвращающий размер вектора (то есть iMax = crew.size()). Параметр iMax мизации вносимых изменений в созданный ранее код. Кроме того, обращение к методу требует несколько большего времени, чем счи­тывание значения переменной.
Проверьте работоспособность измененного приложения. Вид диалогового окна остался прежним, так как интерфейс пользователя не менялся.
Теперь реализуем в нашей программе операции удаления, упо­рядочивания и поиска ритмами, предоставляемыми библиотекой STL, что существенно облегчит нашу работу и значительно уменьшит код.
Добавьте в окно диалога кнопку «Удалить» и создайте функ­цию-обработчик BNClickedDel. Можно ограничиться инструкциями:
crew.erase(crew.begin()+iCur);
iMax--;
ShowRecord();
Для удаления текущей записи из контейнера применяется метод
erase, параметром которого является итератор (указатель) на уда­ляемый элемент. контейнера crew. Таким образом, удаляется элемент вектора с ин­дексом iCur (текущая запись). При этом новой текущей записью становится запись, следующая после удаленной. Однако, как и ра­нее, при работе приложения возможны ошибки, обусловленные тем, что база может быть пустой или удаляемая запись была последней по порядку удаления наша база стала пустой, необходимо очистить все поля диалога. С учетом этих обстоятельств код функции может быть та­ким:
// INSERT>> Your code here.
if(crew.empty()){.. // ***
(отсутствует следующая запись). Кроме того, если после
записей. Воспользуемся стандартными алго-
Функция begin указывает на начальный элемент
47
оставлен с целью мини-
MessageBox («База пуста», «Удаление», MB_OK |
MB_ICONERROR); return; } crew.erase(crew.begin()+iCur); // ***
if(iMax == 1){ // если удаляем послед-
нюю – чистим все поля
iCur=0; iMax=0;
EditName -> SetText("");
ComboJob -> SetText(""); // ***
EditPay -> SetText("");
StaticNum -> SetText("");
return; } if(iCur == iMax-1) iCur--; // *** iMax--; ShowRecord();
Сравните этот фрагмент с программой, разработанной нами в
предыдущей главе, комментарием «***» отмечены изменения, свя­занные с применением контейнера вместо массива и комбинирован­ного списка для должности вместо редактируемого поля.
Не сложно реализовать и редактирование записей. Рассмотрим
следующую тель выбрал нужную запись и отредактировал поля диалога, тре­бующие коррекции:
- проверяем, не пуста ли база данных;
- запрашиваем подтверждение на внесение изменений;
- в случае отказа восстанавливаем информацию в окне диалога;
- если подтверждение получено, заменяем значения данными,
Добавьте в окно диалога кнопку «Изменить» и создайте функ-
цию-обработчик BNClickedChange. Введите в созданную с помо­щью ClassExpert заготовку функции инструкции:
// INSERT>> Your code here. char buff[20]; if(crew.empty()){
}
последовательность действий после того, как пользова-
считываемыми из соответствующих элементов
MessageBox ("База пуста", "Коррекция", MB_OK |
MB_ICONERROR); return;
48
управления.
wsprintf (buff,"Изменить запись %i?",iCur+1); if (MessageBox (buff, «Коррекция», MB_YESNO | MB_ICONQUESTION)==IDNO){
ShowRecord(); return; } EditName -> GetText(buff,20); strcpy(crew[iCur].name,buff); ComboJob -> GetText(buff,20); strcpy(crew[iCur].job,buff); EditPay -> GetText(buff,20); crew[iCur].SetPay(atoi(buff)); wsprintf(buff, "Запись %i изменена", iCur+1); MessageBox(buff, "Коррекция", MB_OK | MB_ICONINFORMATION);
Запустите программу, введите несколько записей и проверьте
работоспособность операций коррекции и удаления.
ЛГОРИТМЫ. СОРТИРОВКА И ПОИСК
А
Для упорядочивания и поиска записей можно применить стан-
дартные алгоритмы, имеющиеся в библиотеке STL. В этом случае объем кода существенно сократится. Добавьте кнопку «Сортиро­вать», задайте имя функции обработчика события Click для этой
49
кнопки – BNClickedSort и поместите в тело функции следующие инструкции:
if(iMax == 0){
MessageBox ("База пуста", "Сортировка", MB_OK |
MB_ICONERROR); return; } sort(crew.begin(), crew.end()); iCur=0; ShowRecord();
Не стоит забывать, что алгоритм sort упорядочивает записи,
сравнивая их с помощью перегруженной в классе операции "<" (меньше). В данном случае – по возрастанию оклада. Если операцию меньше определить, например, так:
bool operator<(const staff& sl, const staff& sr){
return strcmp(sl.name,sr.name)<0; }
то будет выполнена сортировка по
На практике часто существует потребность упорядочивания за-
писей по различным полям, по желанию пользователя. Эта задача легко решается, если вспомнить, что в алгоритме сортировки в каче­стве третьего параметра может быть указано имя функции или функционального объекта (класса, в котором определена операция вызова функции [2, 4]). Добавьте (раскомментируйте) в интерфейс класса staff (
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);
а в модуле реализации класса (staff.cpp) раскомментируйте опреде­ления этих функций:
файл staff.h) после объявления:
именам.
50
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]