Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Основы программирования на Delphi.doc
Скачиваний:
0
Добавлен:
01.01.2020
Размер:
5.09 Mб
Скачать

9.3.10. Список

Компонент ListBox отображает список элементов, которые пользователь может просматривать и выбирать, но не может непосредственно модифицировать. По умолчанию элементами списка являются строки, но могут быть и графические объекты. Элементы могут располагаться в одну или несколько колонок и автоматически сортироваться. При необходимости обеспечивается возможность прокрутки списка. Компонент ListBox находится в палитре компонентов на вкладке Standard (рисунок 9.44).

Рисунок 9.44. Компонент ListBox

Его характерные свойства собраны в таблице 9.14.

Свойство

Описание

Align

Способ выравнивания компонента в пределах содержащего компонента.

AutoComplete

Если равно True, то можно быстро выбрать элемент, если начать набирать его текст на клавиатуре.

BevelEdges

Вложенные свойства beLeft, beTop, beRight и beBottom определяют видимость соответственно левой, верхней, правой и нижней сторон рельефной рамки.

BevelInner

Внутренний скос рельефной рамки: bvNone — скос отсутствует, bvLowered — скос внутрь, bvRaised — скос наружу; bvSpace — скос заменяется отступом.

BevelKind

Вид рельефной рамки: bkNone — рамки нет, bkTile — рамка с четкими скосами, bkSoft — рамка со сглаженными скосами, bkFlat — плоская рамка (без скосов).

BevelOuter

Внешний скос рельефной рамки: bvNone — скос отсутствует, bvLowered — скос внутрь, bvRaised — скос наружу; bvSpace — скос заменяется отступом.

BorderStyle

Определяет, имеет ли список рамку.

Columns

Количество колонок в списке.

ExtendedSelect

Если равно значению True, то пользователь может выбрать в списке диапазон элементов (однако лишь в том случае, если MultiSelect тоже равно значению True).

IntegralHeight

Если равно значению True, то высота списка автоматически уменьшается, чтобы быть кратной высоте элемента.

ItemHeight

Высота элемента списка, когда значение свойства Style равно lbOwnerDrawFixed.

Items

Элементы списка.

MultiSelect

Если равно значению True, то пользователь может выбрать в списке несколько элементов.

ScrollWidth

Логическая ширина списка в пикселях. Если значение свойства ScrollWidth больше значения свойства Width, то появляется горизонтальная полоса прокрутки. В противном случае полоса прокрутки не показывается.

Sorted

Если равно значению True, то элементы списка сортируются в алфавитном порядке.

Style

Стиль отображения списка (см. табл. 9.15).

OnData

Предназначено для формирования списка элементов перед рисованием. Происходит только в том случае, если свойство Style содержит значение lbVirtual или lbVirtualOwnerDraw.

OnDataFind

Происходит, когда пользователь пытается быстро перейти к элементу, набирая текст элемента на клавиатуре. Обработчик этого события должен на основании введенной пользователем строки вернуть номер соответствующего элемента. Возникает только в том случае, если свойство Style содержит значение lbVirtual или lbVirtualOwnerDraw.

OnDataObject

Происходит при обращении к массиву Objects в списке Items, но только в том случае, если свойство Style содержит значение lbVirtual или lbVirtualOwnerDraw. Обработчик события должен вернуть соответствующий элементу объект.

OnDrawItem

Происходит при рисовании отдельно взятого элемента списка, но только в том случае, если свойство Style содержит одно из следующих значений: lbOwnerDrawFixed, lbOwnerDrawVariable, lbVirtualOwnerDraw.

OnMeasureItem

По замыслу разработчиков событие происходит при расчете высоты отдельно взятого элемента списка перед его рисованием на экране и лишь в том случае, если свойство Style содержит значение lbOwnerDrawVariable. Однако из-за дефекта в модуле StdCtrls событие OnMeasureItem не срабатывает.

Таблица 9.14. Важнейшие свойства и события компонента ListBox

Особенности хранения и отображения элементов списка определяются свойством Style, возможные значения которого описаны в таблице 9.15.

Значение

Описание

LbStandard

Все элементы списка имеют одинаковую высоту, которая рассчитывается исходя из размера шрифта.

LbOwnerDrawFixed

Все элементы списка имеют одинаковую высоту, заданную в свойстве ItemHeight. За рисование элементов отвечает программист, который должен создать обработчик события OnDrawItem.

lbOwnerDrawVariable

По замыслу разработчиков элементы списка имеют разную высоту, определяемую в обработчике события OnMeasureItem (из-за дефекта в модуле StdCtrls событие не срабатывает). За рисование элементов отвечает программист, который должен создать обработчик события OnDrawItem.

LbVirtual

Элементы списка хранятся отдельно от компонента и запрашиваются с помощью события OnData. За рисование элементов отвечает компонент.

LbVirtualOwnerDraw

Элементы списка хранятся отдельно от компонента и запрашиваются с помощью события OnData. За рисование элементов отвечает программист, который должен создать обработчик события OnDrawItem.

Таблица 9.15. Значения свойства Style компонента ListBox

Шаг 25. Давайте воспользуемся компонентом ListBox для организации списка будильников. Активизируйте форму MainForm, а затем опустите на нее компонент ListBox. Переименуйте компонент в AlarmListBox и скорректируйте его местоположение и размеры. Затем установите свойство TabOrder в значение 0, чтобы при отображении формы список первым получил фокус ввода (рисунок 9.45).

Рисунок 9.45. Компонент ListBox применяется для организации списка будильников

Решим теперь вопрос хранения будильников в компоненте AlarmListBox. Для хранения элементов служит свойство Items. Свойство Items — это объект класса TStrings, в нем свойство-массив Strings хранит отображаемые строки, а свойство-массив Objects — ассоциированные со строками объекты. В нашем примере массив Strings будет хранить выдаваемые по сигналу сообщения, а массив Objects — соответствующие им объекты класса TAlarm.

Теоретически все понятно, осталось реализовать все это практически. Создание, редактирование и удаление будильника осуществляется по щелчкам на кнопках NewButton, EditButton и DeleteButton соответственно. Поэтому в них требуется создать обработчики события OnClick.

Шаг 26. В кнопке New... обработчик события OnClick уже существует, но его необходимо доработать:

procedure TMainForm.NewButtonClick(Sender: TObject);

var

Alarm: TAlarm;

begin

AlarmDetailsForm := TAlarmDetailsForm.Create(Self);

try

// Выполнить диалог

if AlarmDetailsForm.ShowModal = mrOK then

begin

// Создать новый объект будильника

Alarm := TAlarm.Create;

// Получить параметры будильника из диалога

AlarmDetailsForm.GetData(Alarm);

// Добавить будильник в список и выбрать его

AlarmListBox.ItemIndex := AlarmListBox.Items.AddObject(

Alarm.GetAlarmStr, Alarm);

end;

finally

AlarmDetailsForm.Free;

end;

end;

Метод NewButtonClick создает окно диалога Alarm Details и выполняет его в монопольном режиме. Если диалог завершается щелчком кнопки OK, создается новый объект будильника и в него переносятся данные из окна диалога. Затем этот объект добавляется в список AlarmList и его номер присваивается свойству списка ItemIndex. В результате новый элемент становится выделенным.

Вы, разумеется, хотите проверить работу новоиспеченного метода. Сейчас мы так и сделаем, но прежде нужно решить небольшой вопрос. Дело в том, что при уничтожении блока списка освобождаются только строки, но не освобождаются ассоциированные с ними объекты. Хотя память объектов так или иначе освобождается при завершении приложения, мы рекомендуем всегда освобождать память явно. Это считается "хорошим тоном" программирования и иногда позволяет выявить скрытые ошибки. Освобождение использованных в форме динамических данных осуществляется в обработчике события OnDestroy. Для формы MainForm он должен быть таким:

procedure TMainForm.FormDestroy(Sender: TObject);

var

I: Integer;

begin

for I := 0 to AlarmListBox.Items.Count - 1 do

AlarmListBox.Items.Objects[I].Free;

end;

После того как вы написали этот обработчик, выполните компиляцию программы и запустите ее. Попытайтесь добавить в список несколько будильников. Если это у вас получилось, перейдем к следующему шагу — программированию реакции на нажатия кнопок Edit... и Delete.

Шаг 27. Создайте в компоненте EditButton обработчик события OnClick:

procedure TMainForm.EditButtonClick(Sender: TObject);

var

Alarm: TAlarm;

SavedIndex: Integer;

begin

AlarmDetailsForm := TAlarmDetailsForm.Create(Self);

try

// Получить выбранный будильник

with AlarmListBox do Alarm := TAlarm(Items.Objects[ItemIndex]);

// Установить управляющие элементы диалога в соответствии с

// параметрами будильника

AlarmDetailsForm.SetData(Alarm);

// Выполнить диалог

if AlarmDetailsForm.ShowModal = mrOK then

begin

// Получить из диалога новые параметры будильника

AlarmDetailsForm.GetData(Alarm);

with AlarmListBox do

begin

// Запомнить номер выбранного в списке элемента

SavedIndex := ItemIndex;

// Изменить текст элемента

// При этом элемент перестает быть выбранным

Items.Strings[ItemIndex] := Alarm.GetAlarmStr;

// Восстановить номер выбранного в списке элемента

ItemIndex := SavedIndex;

end;

end;

finally

AlarmDetailsForm.Free;

end;

end;

Этот метод создает окно диалога Alarm Details, инициализирует его компоненты данными из выбранного в списке объекта будильника, а затем выполняет диалог в монопольном режиме. Если диалог завершился щелчком на кнопке OK, то данные из окна диалога переносятся обратно в объект будильника и соответственно изменяется отображаемая в блоке списка строка. Так как в результате последнего действия в списке пропадает полоса выбора (свойство ItemIndex получает значение -1), номер выделенного элемента предварительно сохраняется в локальной переменной SavedIndex, а затем восстанавливается.

Шаг 28. Осталось создать обработчик события OnClick в компоненте DeleteButton:

procedure TMainForm.DeleteButtonClick(Sender: TObject);

begin

with AlarmListBox do

begin

// Разрушить объект будильника

Items.Objects[ItemIndex].Free;

// Удалить из списка соответствующую объекту строку

Items.Delete(ItemIndex);

end;

end;

Метод DeleteButtonClick удаляет объект будильника и соответствующую ему строку в списке.

Обработчики событий для всех кнопок заданы, однако не спешите запускать приложение. Необходимо позаботиться о том, чтобы кнопки Edit... и Delete были доступны или недоступны в зависимости от того, выделен в списке элемент или нет. Как бы это сделать попроще? Первое решение, которое напрашивается — это вставить необходимые проверки в обработчики событий кнопок. Это неплохое решение, но оно больше подходит тем, кто привык решать задачу в лоб. Мы пойдем другим путем, воспользовавшись событием OnIdle объекта Application.

В объекте Application происходит событие OnIdle в период простоя программы, например во время ожидания пользовательского ввода. Благодаря этому событию программа может выполнять некоторую фоновую работу, которая в нашем случае заключается в управлении состоянием кнопок.

Для создания обработчика события OnIdle объекта Application воспользуемся уже знакомым вам компонентом ApplicationEvents (см. главу 8).

Шаг 29. Поместите в форму компонент ApplicationEvents, дайте ему одноименный идентификатор и создайте обработчик события OnIdle:

procedure TMainForm.ApplicationEventsIdle(Sender: TObject;

var Done: Boolean);

begin

EditButton.Enabled := AlarmListBox.ItemIndex <> -1;

DeleteButton.Enabled := AlarmListBox.ItemIndex <> -1;

Done := True; // предотвращает непрерывную генерацию события OnIdle

end;

В передаваемом по ссылке параметре Done метод возвращает результат своей работы. Значение True показывает, что метод нужно вызывать не постоянно в течение простоя приложения, а только по одному разу в начале каждого периода простоя.

А сейчас выполните компиляцию, запустите программу и тщательно протестируйте работу главной формы (рисунок 9.46).

Рисунок 9.46. В этом окне создается список будильников

Будильники можно создавать, добавлять, удалять. Нам осталось сделать последний шаг — заставить будильники "звонить". Для этого нужно периодически вызывать метод CheckTime у каждого помещенного в список объекта TAlarm. Периодические по времени действия выполняются с помощью таймера, о котором мы дальше и поговорим.