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

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

.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
1 Мб
Скачать
☆
MessageBox("Что редактировать-то?",
"Редактирование",MB_OK | MB_ICONQUESTION);
return;
}
if(MessageBox("Изменить отмеченную запись?", "Редактирова-
ние",
MB_YESNO | MB_ICONQUESTION) == IDNO) return;
browser->GetSelIndexes(&i, 1);
browser->DeleteAnItem(i);
EditName->GetText(buff,20);
TListWindItem item(buff,0,20);
item.SetIndex(i);
browser->InsertItem(item);
item.SetSubItem(1);
EditJob->GetText(buff,20);
item.SetText(buff);
browser->SetItem(item);
item.SetSubItem(2);
EditPay->GetText(buff,20);
item.SetText(buff);
browser->SetItem(item);
browser->SetSel(i, true);
Прежде всего, проверяем, сколько записей выбрано в списке. Это значение возвращает метод GetSelCount класса TListWindow. Если выбрано несколько записей, или не выбрано ни одной, выво­дим сообщение: «Что редактировать-то?» и отменяем редактирова­ние. Если помечена на изменение. Далее, получаем индекс выбранной записи с помо­щью метода GetSelIndexes класса TListWindow. Из названия мето­да следует, что он позволяет получить индексы записей при множе­ственном выборе. Первый параметр метода – адрес массива индек­сов выбранных записей, второй параметр – размерность массива (количество выбранных записей). У и ее индекс записывается в переменную i. Далее, отмеченная запись
ровно одна запись, запрашиваем подтверждение
нас выбрана ровно одна запись,
171
удаляется с помощью метода DeleteAnItem класса TListWindow и добавляется новая, с тем же индексом. Процедура создания и добав­ления новой записи абсолютно аналогична рассмотренному выше коду функции обработчика кнопки «Добавить» и подробно обсуж­далась выше. В конце вызывается функция SetSel класса TListWin- dow, чтобы сохранить выделение отредактированной записи.
Вообще говоря, такой подход ( сто нее новой) выбран для простоты изложения и годится только в том случае, если записи не содержат дополнительной информации, например, данных, ассоциированных с записью, или текущее со­стояние записи. В противном случае вместо удаления записи требу­ется получить ее с помощью метода GetItem класса TListWindow и изменить только текст колонок методом SetText.
Запустите проект на исполнение. Введите несколько записей. Выберите, например, вторую запись и внесите необходимые изме­нения:
удаление записи и создание вме-
После подтверждения на редактирование данные во второй строке элемента «просмотр списка» будут изменены на информа­цию, введенную в соответствующие поля диалога.
Обратите внимание на заголовки столбцов:
Это кнопки! И так же, как и для других кнопок, можно создать функцию обработчик для щелчка мышкой по заголовку. Кроме того, поместив указатель мыши на границу между заголовками, можно поменять ширину колонок во время работы программы. Указатель
172
мыши при этом принимает вид двусторонней стрелки . Можно передвигать границу столбца или двойным щелчком правой кнопки мыши установить ширину равной ширине максимальной записи в колонке.
Используем заголовки колонок в качестве кнопок для сортиров­ки записей. В классе TListWindow имеется метод SortItems, пред­назначенный для упорядочивания списка. Второй, не обязательный, параметр этой функции является 32-битовым целым числом, а вый параметр должен быть объектом класса TLwComparator, пред- назначенного для сортировки. В данном классе должна быть пере­гружена функция Compare, возвращающая целое значение при сравнении двух записей. Первые два параметра этой функции – это данные, ассоциированные с записью, а третий параметр – копия вто­рого параметра функции SortItems. До сих пор мы не данных, ассоциированных с записью, теперь придется.
Чтобы не усложнять изложение, поступим просто. Скопируем информацию из выбранной колонки в контейнер, содержащий стро­ки. В качестве данных, ассоциированных с записью, воспользуемся копией индекса записи в списке. В этом случае индексы объектов (строк) в контейнере будут соответствовать параметрам, получен­ным функцией Compare при нечно, использовать массив обычных строк языка С, но количество записей, введенных пользователем во время сеанса, неизвестно… Кроме того, поскольку при сравнении требуется учесть, является ли информация тестовой или числовой (оклад), воспользуемся вторым параметром функции SortItems (копируется в третий параметр фун­кции Compare) для передачи номера колонки, по ляется сортировка.
Итак, прежде всего добавьте в конец модуля wrkdlgcl.h объяв­ления нужного буферного контейнера myParam и класса myComp ­наследника класса TLwComparator:
#include <vector.h>
using namespace std;
vector <string> myParam;
class myComp:public TLwComparator{
public:
сравнении двух записей. Можно, ко-
использовали
которой осуществ-
пер-
173
myComp(): TLwComparator(){} int Compare(uint32 item1, uint32 item2, uint32 iSubItem) const;
};
а в конец модуля wrkdlgcl.cpp реализацию метода Compare:
int myComp::Compare(uint32 item1, uint32 item2, uint32 lParam)
const{
if(lParam != 2)
return myParam[item1].compare(myParam[item2]); //текст
return atoi(myParam[item1].c_str()) - atoi(myParam[item2].c_str());
//числа
}
Далее, определите функцию, обрабатывающую щелчок мышью по заголовкам столбцов. Вызовите ClassExpert, в панели Classes выберите класс диалога TWorkDlgClient, затем в панели событий Events найдите идентификатор элемента управления «просмотр спи- ска», раскройте список событий, выберите событие LVN_COLUM-
NCLICK (щелчок по Add handler. Можно оставить предлагаемое имя функции LVN­Columnclick. В нижнем окне ClassExpert появится заготовка нужного метода:
void TWorkDlgClient::LVNColumnclick(TLwNotify& lwn)
{
// INSERT>> Your code here.
}
У созданной функции имеется один параметр lwn – объект класса TLwNotify, базового класса сообщений для объектов TList­Window. В поле iSubItem параметра lwn содержится номер столбца, по заголовку которого щелкнул пользователь.
Добавьте после
char buff[20];
int count = browser->GetItemCount();
myParam.resize(count);
заголовку) и создайте функцию обработчик →
коментария строчки:
174
for (int i = count-1; i >=0; i--){
TListWindItem item; item.SetIndex(i); browser->GetItem(item); item.lParam=i; browser->SetItem(item); browser->GetItemText( i, lwn.iSubItem, buff, 20);
myParam[i]=buff; } browser->SortItems(myComp(), lwn.iSubItem);
Сначала, определяется количество записей – метод GetItem-
Count и резервируется контейнер myParam требуемого размера. Да-
лее, в цикле запись считывается в объект класса TListWindItem и задается индекс в качестве данных, ассоциированных с записью (просто записывается в поле lParam объекта item). После чего вне­сенные изменения записываются в элемент «просмотр списка» –
SetItem. Информация из Item) считывается в буферный массив – GetItemText и сохраняется в
элементе контейнера myParam. После описанной выше подготовки осуществляется упорядочивание записей – SortItems. Первый пара­метр – это объект созданного нами класса myComp, второй параметр задает номер столбца. Запустите программу на исполнение, введите несколько записей и щелкните по заголовку любого столбца – запи­си будут отсортированы.
Несколько усложнив описание класса myComp и код перегру-
женной функции Compare, можно обойтись без копирования данных в дополнительный контейнер. Если данные, ассоциированные с за­писью уникальны, то есть, среди них нет одинаковых значений, то по ним можно найти соответствующую запись в списке и затем счи­тать информацию из нужного столбца. Указатель на список (browser) можно передать через второй параметр функции SortItems, а номер столбца через глобальную переменную или дополнительное поле класса myComp.
В модуле wrkdlgcl.h уберите объявление буферного контейнера
myParam и отредактируйте класс myComp:
class myComp:public TLwComparator{ public:
выбранного столбца (его индекс lwn.iSub-
175
int SortColumn; myComp(int sc=0):TLwComparator(),SortColumn(sc){} int Compare(uint32 item1, uint32 item2, uint32 iSubItem) const; };
измените код метода Compare в модуле wrkdlgcl.cpp:
int i; char buff1[20], buff2[20]; LV_FINDINFO lf; //TListWindow* browser=(TListWindow*)lParam; //или ­TListWindow* browser=reinterpret_cast<TListWindow*>(lParam); lf.flags=LVFI_PARAM; lf.lParam=item1; i=browser->FindItem(-1,&TLwFindInfo(lf)); browser->GetItemText( i, SortColumn, buff1, 20); lf.lParam=item2; i=browser->FindItem(-1,&TLwFindInfo(lf)); browser->GetItemText( i, SortColumn, buff2, 20); if(SortColumn != 2) return strcmp(buff1,buff2); //текст return atoi(buff1)-atoi(buff2); //числа
В параметре lParam хранится целое число – адрес объекта «про-
смотр списка». Операция reinterpret_cast меняет точку зрения транслятора на значение – теперь это указатель на объект класса
TListWindow, который мы запоминаем в локальной переменной browser. Метод FindItem класса TListWindow возвращает индекс записи,
TLwFindInfo. В конструктор этого объекта передается экземпляр lf структуры LV_FINDINFO. Поле flags структуры LV_FINDINFO определяет, в каких полях заданы искомые значения. Значение, рав­ное LVFI_PARAM, указывает, что данные заданы в поле lParam структуры LV_FINDINFO, куда и копируются значения параметров item, то есть данные, ассоциированные с записью. Следовательно метод FindItem будет искать запись по ассоциированным данным. Первый параметр функции FindItem задает индекс начальной записи при поиске. Если требуется искать с самого начала, задается значе­ние, равное –1. Наконец, с помощью уже знакомой нам функции
соответствующей данным, заданным в объекте класса
,
176
GetItemText информация из столбца SortColumn запоминается в бу-
ферном массиве. Аналогично обрабатывается вторая сравниваемая запись, определяемая параметром item2.
Осталось в методе LVNColumnclick убрать три строчки, связан-
ные с заполнением ставшего не нужным контейнера:
myParam.resize(count);
и ниже:
browser->GetItemText( i, lwn.iSubItem, buff, 20); myParam[i]=buff;
затем заменить инструкцию:
browser->SortItems(myComp(), lwn.iSubItem);
на:
//browser->SortItems(myComp(lwn.iSubItem), (uint32)browser); //или ­browser->SortItems(myComp(lwn.iSubItem), reinterpret_cast < uint 32>(browser));
Проверьте, все должно работать. Процедура поиска в списке может основываться
нии рассмотренной выше функции FindItem. Данный метод позво­ляет осуществлять поиск либо по данным, ассоциированным с запи­сью, как это делалось ранее, либо по значению основного поля запи­си, то есть информации, введенной в первый столбец. Добавьте в окно диалога кнопку «Поиск» и создайте функцию-обработчик для этой
кнопки. Введите следующие инструкции в тело функции:
int first; char name[20]; LV_FINDINFO lf; lf.flags = LVFI_STRING | LVFI_PARTIAL; EditName -> GetText(name, 20); lf.psz = name;
177
на использова-
browser -> SetSelItemRange(false, 0, browser->GetItemCount() - 1); first = browser->FindItem(-1,&TLwFindInfo(lf)); for(int i = first; i != -1; i=browser->FindItem(i,&TLwFindInfo(lf))){
browser->SetSel(i, true); } if(first>=0) browser->EnsureVisible(first, false);
Поскольку поиск предполагается осуществлять по информации,
содержащейся в первой колонке, устанавливается флаг LVFI_ST- RING. Установка дополнительного значения LVFI_PARTIAL по­зволяет найти строчку, не совпадающую, но начинающуюся с по­следовательности символов, на которую указывает параметр psz структуры lf. В качестве искомого значения используется информа­ция, введенная в редактируемое поле EditName диалога SelItemRange класса TListWindow позволяет выделить записи в за­данном диапазоне индексов, если первый параметр равен значению true или снять выделение (значение false). Нужный диапазон индек­сов задается вторым и третьим параметрами функции. Выделение всех ранее выбранных записей здесь предварительно отменяется. Далее запоминаем индекс первой найденной записи, поскольку воз­можно потребется си, удовлетворяющие условиям поиска, выделяются с помощью ме­тода SetSel. После того как найдены все подходящие строчки, осу­ществляется скроллинг списка, так, чтобы первая найденная запись была видна (вызовом метода EnsureVisible). Естественно, в рассмот­ренном алгоритме предполагается, что в свойствах элемента управ­ления «просмотр списка» на Single Selection, то есть, разрешен множественный выбор.
Как уже отмечалось, метод FindItem не позволяет осуществлять
поиск непосредственно по значениям всех колонок, за исключением первой. Однако несложно уточнить условия поиска, используя дан­ные этих столбцов. Замените строчку:
browser->SetSel(i, true);
на следующие инструкции:
char buff[20], job[20]; browser->GetItemText( i, 1, buff, 20);
осуществить скроллинга списка. Затем все запи-
вкладке ListView не устанавлен флажок
178
. Метод Set-
EditJob -> GetText(job, 20); if( !strcmp(job, buff) || job[0]=='\0') browser->SetSel(i, true);
Теперь будут найдены (отмечены) только те записи, в которых
значения в первом столбце начинаются с последовательности сим­волов, заданной в поле диалога EditName, и кроме того, текст второ­го столбца совпадает с заданным в поле EditJob (должность). При этом, если поле EditJob пустое, поиск по-прежнему будет осуществ­ляться только по первой колонке.
!strcmp(job, buff) || job[0]=='\0'
на:
strstr(buff, job) == buff || job[0]=='\0'
то будут найдены сотрудники, должности которых, как и имена, на­чинаются на искомые последовательности символов.
Если заменить условие:
Считать информацию из нужного столбца записи объекта «про-
смотр списка» можно с помощью метода GetItemText класса TListWindow. Первый параметр метода определяет индекс записи, второй параметр – колонку, третий параметр указывает адрес сим­вольного массива, в который копируется информация, а четвертый параметр задает максимальное количество копируемых в массив символов. Добавьте в окно
диалога кнопку «Сохранить» и создайте
179
функцию-обработчик для этой кнопки. Введите следующие инст­рукции в тело функции:
const char* FileName = "database.txt"; char buff[20]; ofstream out(FileName); if( !out) {
MessageBox("Не могу \n открыть файл", "Сохранение",
MB_OK | MB_ICONSTOP);
return; } for(int i=0;i<browser->GetItemCount();i++) {
for(int column=0;column<3;column++) {
browser->GetItemText( i, column, buff, 20);
out<<buff<<'\t'; } out<<endl; } out.close();
Запустите программу. Введите несколько записей и нажмите
кнопку сохранить. В папке проекта будет создан текстовый файл:
Рассмотрим еще один проект, в котором информация в памяти
организована в виде массива (или контейнера), а элемент «просмотр списка» применяется для фильтрации и просмотра данных. В по­следний раз скопируйте проект «Заготовка», созданный в первой главе данного пособия. В этом проекте данные организованы с по­мощью массива crew фиксированной длины зультате фильтрации записей нам потребуется дополнительный мас-
180
. Для отобранных в ре-
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]