Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Разработка Windows - приложений в среде Borland. Учебное пособие
.pdf
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. Можно оставить предлагаемое имя функции LVNColumnclick. В нижнем окне ClassExpert появится заготовка нужного
метода:
void TWorkDlgClient::LVNColumnclick(TLwNotify& lwn)
{
// INSERT>> Your code here.
}
У созданной функции имеется один параметр lwn – объект
класса TLwNotify, базового класса сообщений для объектов TListWindow. В поле 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
. Для отобранных в ре-
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
