Файл: Учебное пособие для студентов Авторы А. Н. Вальвачев, К. А. Сурков, Д. А. Сурков, Ю. М. Четырько Содержание Содержание 1.doc
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 04.05.2024
Просмотров: 125
Скачиваний: 4
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
DecodeTime(DateTime, Hour2, Min2, Sec2, MSec2);
// Проверить, что текущее время совпадает с временем будильника
case Recurring of
0: // для ежедневной периодичности
Match := (Hour1 = Hour2) and (Min1 = Min2);
1..7: // для еженедельной периодичности
Match := (Hour1 = Hour2) and (Min1 = Min2) and
(Recurring = DayOfWeek(Date));
8: // для конкретной даты
Match := (Hour1 = Hour2) and (Min1 = Min2) and
(Int(DateTime) = Date);
else
Match := False;
end;
// Решить вопрос о выдаче сигнала будильником
if Match then
begin
if not Handled then // сигнал!
begin
Handled := True; // предотвратить повторные срабатывания
if PlaySound then Beep;
MessageDlg(GetAlarmStr, mtWarning, [mbOk], 0);
end;
end
else
Handled := False; // обеспечить будущие срабатывания
end;
Для правильной работы будильника метод CheckTime должен вызываться не реже одного раза в минуту. Чем чаще вызывается метод, тем меньше инерционность будильника, но тем больше пустых опросов, а значит выше загруженность операционной системы. Компромиссная частота — два раза в секунду. Так как в одну и ту же минуту метод CheckTime будет вызван несколько раз, то для избежания повторных срабатываний используется флаг Handled. Будильник выдает сообщение только в том случае, если текущее время совпадает с временем, на которое будильник установлен и при условии, что в данную минуту он еще не звенел.
Шаг 24. Давайте теперь позаботимся о передаче данных в окно диалога перед его запуском и о приеме данных после завершения. Удобнее всего, чтобы за это отвечало само окно диалога, т.е. форма AlarmDetailsForm. С этой целью определите в классе TAlarmDetilasForm два метода — GetData и SetData. Методы следует поместить в секцию public:
type
TAlarmDetailsForm = class(TForm)
...
public
procedure GetData(Alarm: TAlarm);
procedure SetData(Alarm: TAlarm);
end;
В разделе implementation наберите программный текст методов:
procedure TAlarmDetailsForm.GetData(Alarm: TAlarm);
begin
with Alarm do
begin
// Получить из диалога текст сообщения будильника
MsgText := MessageEdit.Text;
// Получить из диалога время срабатывания будильника
DateTime := StrToTime(TimeMaskEdit.Text);
// Получить из диалога состояние переключателя звука
PlaySound := SoundCheckBox.Checked;
// Получить из диалога периодичность срабатывания будильника
if EverydayRadioButton.Checked then
Recurring := 0
else if WeeklyRadioButton.Checked then
Recurring := WeeklyComboBox.ItemIndex + 1
else { DateRadioButton.Checked }
begin
Recurring := 8;
DateTime := DatePicker.Date + DateTime;
end;
end;
end;
procedure TAlarmDetailsForm.SetData(Alarm: TAlarm);
begin
with Alarm do
begin
// Установить в окне диалога текст сообщения будильника
MessageEdit.Text := MsgText;
// Установить в окне диалога время будильника
TimeMaskEdit.Text := FormatDateTime('hh:mm', DateTime);
// Установить в окне диалога состояние переключателя звука
SoundCheckBox.Checked := PlaySound;
// Установить в окне диалога периодичность будильника
case Recurring of
0: // ежедневно
EverydayRadioButton.Checked := True;
1..7: // еженедельно
begin
WeeklyRadioButton.Checked := True;
WeeklyComboBox.ItemIndex := Recurring - 1;
end;
8: // в конкретный день
begin
DateRadioButton.Checked := True;
DatePicker.Date := Int(DateTime);
end;
end;
end;
end;
Метод GetData просто заполняет поля переданного в параметре объекта Alarm значениями, которые установлены в компонентах окна диалога. Метод SetData выполняет обратные действия, заполняя компоненты окна диалога значениями, которые содержатся в полях объекта Alarm.
На этом с разработкой модуля AlarmDetails покончено и окно диалога Alarm Details полностью готово к использованию. Дальше необходимо обеспечить формирование, редактирование и визуализацию списка будильников. Эта задача решается с помощью компонента ListBox.
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 | Стиль отображения списка (см. табл. 7.14). |
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 в период простоя программы, например во время ожидания пользовательского ввода. Благодаря этому событию программа может выполнять некоторую фоновую работу, которая в нашем случае заключается в управлении состоянием кнопок.