Уроки Delphi
понедельник, 28 июля 2008 г.
Сообщения для пользователя
В этом уроке мы с вами рассмотрим организацию некоторых сообщений в программе.
Сообщения присутствуют повсюду: когда вы пытаетесь закрыть не сохраненный проект, при появлении ошибки, когда программа сообщает о некотором событии.
Сообщения, «вылетающие» при работе программы, можно разделить на те, которые программист предусмотрел, и системные сообщения.
В большинстве случаев второй тип сообщений имеет непонятный для обычного пользователя вид. Как правило, сообщается англоязычный термин, иногда имеется и шестнадцатеричный адрес ошибки. Например, сообщение «I/O Error» говорит программисту или пользователю об ошибке ввода-вывода. Это может быть попытка записи данных в неоткрытый файл, попытка открыть несуществующий файл и т.п. Если такая ошибка в вашей русскоязычной версии программы имеет место, то, скорее всего данной ситуации программист просто не предусмотрел. В таких случаях, программа может себя повести совершенно непредсказуемо. Ведь вы помните из прошлых уроков, что не проконтролированный кусок программы на присутствие ошибки ведет к моментальному выходу из обрабатываемой процедуры, со всеми вытекающими из этого последствиями.
Но это маленькое отклонение от темы. Идея такова, надо самостоятельно просчитывать все возможные случаи и самостоятельно обрабатывать эти ситуации. Иногда, если надо, предупреждать пользователя об ошибках, может даже сообщать об окончании обработки данных. Вот о таких сообщениях мы и поговорим в этом уроке.
Можно разделить все программные сообщения на: информационные сообщения («Загрузка данных с дискеты завершена») , предупреждающие сообщения («Файл модифицирован. Сохранить?»), сообщения об ошибке («Файл данных программы не найден. Требуется переустановка программы»). Эта разбивка на типы сообщений является, естественно, не полным, его можно продолжать, но об этом немного позже.
Ради экономии своего времени, вы можете всегда, из любого места программы показать пользователю, к примеру, следующее сообщение:
Конечно, сообщение может быть и серьезным, можно подобные окошки использовать и для других целей вывода информации. Дело ваше. Я иногда этот вид сообщений использую для вывода информации о состоянии программы на этапе программирования.
В чем же заключается экономия времени и экономия текста кода программы. Такое сообщение выводится на экран одной строчкой:
Тип данных в скобках — String.
Все довольно просто, мы с вами эту команду неоднократно применяли в прошлых уроках.
На этой команде работа процедуры (не всей программы!) приостанавливается. Пока пользователь не нажмет на кнопку Ok, работа с приложением становится невозможным, т.е. нельзя «добраться» до окна, расположенного позади. Т.е. это сообщение открывается модально.
Как вы заметили, заголовок окна простой. Он содержит в себе текст, который отображен на панели задач. По умолчанию имеет название запускаемого EXE файла. Изначально это Project1, в последствии вы его можете сохранить под другим именем («Save Project As. «), при этом название проекта, вместе с ним название компилируемого EXE файла меняется.
Изменить название запущенной программы в панели задач можно в любом месте программы с помощью команды:
К примеру, вы обрабатываете довольно объемный размер данных (чтение файлов), и хотите показывать процент выполнения задания прямо в панели задач (как это сделано в программе DrWeb). Ведь пользователь не всегда сможет смотреть на ваш 10-минутный процесс обработки данных, а скорее всего переключится на другую, менее трудоемкую операцию (карточный пасьянс), постоянно следя за процессом обработки на панели задач.
Изначально, еще до запуска программы на выполнение, на этапе разработки, вы можете это задать название программы в панели задач с помощью главного меню delphi «Project», дальше пункт «Options. «, в открывшемся окне на вкладке Application указать в поле Title необходимую строку. Эта строка и будет отображена в панели задач. При этом следует помнить, что слишком длинная фраза в кнопке на панели задач полностью не будет показана. При этом она будет обрезана троеточием, а для того, чтобы узнать полное название запущенной программы, нужно будет подвести мышку (всплывающая подсказка Hint вам покажет полное название).
Вы уже знаете все прелести простой команды вывода строки ShowMessage. Из недостатков отмечу, что нельзя отдельно от названия программы в панели задач, менять заголовок окошка, нельзя получить иконку в окошке, нельзя отображать другие кнопки.
В delphi есть, можно сказать, встроенная команда отображения окна сообщения. Звучит оно так:
Скажу сразу, что к нашим программам мы пока не пишем дополнительно файлов справки, поэтому ИНДЕКС_ПОМОЩИ у нас всегда будет нулевым. Для информации скажу, что если у нас таковой файл имеется, то можно в таком сообщении сделать кнопку «Help». Если пользователь озадачен вопросом или сообщением, то может, не закрывая этого окна, узнать подробнее о дальнейших этапах работы при выборе того или иного пункта.
ТЕКСТ_СООБЩЕНИЯ — строковая величина. Как в предыдущей команде, сообщение показывается внутри окна.
ТИП_СООБЩЕНИЯ — может принимать несколько значений. От этих значений зависит содержимое заголовка и иконка в левом верхнем углу окна.
Тип сообщения | Описание | Вид окна |
mtWarning | Можно использовать в предупреждающих сообщениях. Например, «Вы действительно желаете удалить все данные с диска С:» | |
mtError | Обычное окошко вывода сообщения об ошибки. Всем знаком его вид т.к. это наиболее частое окно в windows 🙂 | |
mtInformation | Какая-нибудь информация. Например, «Не найден файл настройки, создается заново» | |
mtConfirmation | Это запрос. Запрос на сохранение перед выходом, спрашивает перед удалением параметра, и т.п. На ваш собственный вкус | |
mtCustom | Это сообщение полностью аналогично ShowMessage |
КНОПКИ — содержит в себе массив кнопок, которые следует показывать в сообщении.
Даю перечень кнопок.
* mbYes
* mbNo
* mbOK
* mbCancel
* mbHelp
* mbAbort
* mbRetry
* mbIgnore
* mbAll
Рассказывать про каждую кнопку не буду, т.к. все равно ее название нельзя сменить. А если вам англоязычный термин непонятен, то тогда какой смысл ее применять :).
Массив кнопок задается в квадратных кавычках []. Например, нам надо задать три кнопки Yes, No, Cancel. Это делается так [mbYes,mbNo,mbCancel].
Поскольку кнопки в сообщении могут быть разные, то MessageDLG является функцией. Она возвращает результат нажатой кнопки.
Соответственно указанным выше кнопкам результат может принимать следующие значения
* mrNone — окно сообщения закрыто не с помощью кнопки (Alt+F4 или кнопкой «закрыть»)
* mrAbort
* mrYes
* mrOk
* mrRetry
* mrNo
* mrCancel
* mrIgnore
* mrAll
Рассмотрим пример. Нам надо спросить у пользователя о дальнейших действиях перед выходом из программы.
1. Сохранить файл.
2. Не сохранять файл.
3. Продолжить редактирование.
Var R:Word; // переменная, в которой хранится результат
.
R:=MessageDLG(‘Сохранить файл перед выходом?’,mtConfirmation,[mbYes,mbNo,mbCancel],0);
if R=mrYes then // если нажата кнопка Yes
begin
// сохраняем файл и завершаем программу
end;
if R=mrNo then // если нажата кнопка No
begin
// завершаем работу программы без сохранения
end;
if R=mrCancel then // если нажата кнопка Cancel
begin
// продолжаем работу без сохранения
end;
Мы рассмотрели команду MessageDLG. Это очень гибкая команда, есть много достоинств, но есть один существенный недостаток — англоязычный интерфейс.
Следующая команда использует системные сообщения пользователю вашей операционной системы. Т.е., если у вас установлена, например немецкая версия windows, то кнопки будут иметь соответствующие названия на немецком языке.
Вот эта команда:
Первый параметр — указатель на владельца окна сообщения. Этот параметр вам пока ничего не говорит, устанавливайте его в Handle (это ссылка на окно, откуда это сообщение вызывается).
ТЕКСТ_СООБЩЕНИЯ и ЗАГОЛОВОК_ОКНА — имеют тип PChar, поэтому, во избежание недоразумений и появления неизвестного рода ошибок, выдаваемых компилятором, меняйте тип String в PChar «на ходу». Например:
Это был перевод из одного типа строковой величины в другой тип.
Теперь поговорим о немного сложном параметре ТИП_СООБЩЕНИЯ. Он включает в себя иконку и кнопки.
Кнопки:
* MB_ABORTRETRYIGNORE — кнопки «Прервать», «Повторить», «Пропустить».
* MB_OK — кнопка «Ok».
* MB_OKCANCEL — кнопки «Ok», «Отмена».
* MB_RETRYCANCEL — кнопки «Повторить» и «Отмена».
* MB_YESNO — две кнопки «Да» и «Нет».
* MB_YESNOCANCEL — кнопки «Да», «Нет», «Отмена».
Для того, чтобы отобразить иконку, нужно указать:
* MB_ICONEXCLAMATION
* MB_ICONWARNING
* MB_ICONINFORMATION
* MB_ICONASTERISK
* MB_ICONQUESTION
* MB_ICONSTOP
* MB_ICONERROR
* MB_ICONHAND
Если у вас в сообщении несколько кнопок, а по умолчанию нужно выбрать определенную, то такая кнопка задается:
MB_DEFBUTTON1 — где последняя цифра указывает номер кнопки, выбранной по умолчанию. Это свойство может быть полезным, например, чтобы обезопасить данные от случайного уничтожения. «Удалить файл?». Две кнопки — «Да», «Нет». По умолчанию мы программно выбираем вторую кнопку. Если пользователь сразу нажал на Enter, не осознавая своего поступка, можно сказать по привычке, то ничего страшного не произойдет.
Как же указать параметры иконки, кнопок, кнопки по умолчанию в одном параметре ТИП_СООБЩЕНИЯ. Очень просто. Простым знаком +
Например:
MessageBox(Handle,PChar(‘Выйти из программы?’),PChar(‘Мое сообщение’),MB_ICONINFORMATION+MB_OKCANCEL+MB_DEFBUTTON2);
Выглядит это в программе так, как показано на рисунке:
Итак, MessageBox можно считать идеальной командой вывода сообщения пользователю, которая будет совместима со всеми языковыми версиями windows.
Контроль нажатия на кнопку в MessageBox мы осуществляем аналогично MessageDLG, только возвращаемая величина может принимать следующие значение (соответственно нажатой кнопке):
* IDABORT
* IDCANCEL
* IDIGNORE
* IDNO
* IDOK
* IDRETRY
* IDYES
Сообщения Windows (типы сообщений, принципы работы системы сообщений Windows, система сообщений Delphi, специализированные записи, обработка сообщений)
При разработке приложений обычно достаточно возможностей работы с событиями, предоставляемыми VCL. Но при создании нестандартных приложений и при разработке собственных компонентов Delphi часто требуется непосредственно обрабатывать сообщения Windows и создавать на их основе события Delphi.
Сообщение Windows – это извещение о некотором имевшем место событии, посылаемое Windows в адрес приложения. Любые действия пользователя – щелчок мышью, изменение размеров окна приложения, нажатие клавиши на клавиатуре – вынуждают Windows отправить приложению сообщение, извещающее о том, что же произошло в системе. Сообщение представляет собой запись, объявленную в модуле Windows следующим образом:
TMsg = packed record
hwnd: HWND; // дескриптор окна-получателя
message: UINT; // идентификатор сообщения
wParam: WPARAM; // 32 бита дополнительной информации
lParam: LPARAM; // еще 32 бита дополнительной информации
time: DWORD; // время создания сообщения
pt: TPoint; // положение указателя мыши в момент создания сообщения
Каждому сообщению Windows поставлено в соответствие определенное числовое значение, которое заносится в поле message записи TMsg. В Delphi эти величины, идентифицирующие сообщения определены в модуле Messages. Имя каждой константы начинается с символов WM (Windows Message). Примеры некоторых сообщений приведены в таблице.
Идентификатор сообщения | Значение | Описание |
WM_ACTIVATE | $0006 | Окно активируется или деактивируется. Что именно происходит, определяется младшим словом wParam (LOWORD(wParam)). Старшее слово содержит значение типа BOOL, которое содержит значение True, если окно минимизируется. Поле lParam содержит дескриптор окна, которое будет деактивировано, когда наше окно активируется или дескриптор окна, которое будет активировано, если наше окно деактивируется |
WM KEYDOWN | $0100 | На клавиатуре была нажата клавиша |
WM KEYUP | $0101 | На клавиатуре была отпущена клавиша |
WM PAINT | $000F | Требуется перерисовать клиентскую часть окна |
WM CLOSE | $0010 | Окно должно быть закрыто |
WM_QUIT | $0012 | Приложение должно быть завершено |
Какие для каждого события данные содержатся в wParam и lParam догадаться невозможно и надо узнавать в справочной системе или в литературе.
Принципы работы системы сообщений Windows
Система сообщений Windows состоит из трех составляющих:
1. Очередь сообщений. Windows поддерживает отдельную очередь сообщений для каждого приложения. Приложение Windows должно получать сообщения из этой очереди и передавать их соответствующему окну.
2. Цикл сообщений. Группа операторов, осуществляющая выборку сообщения из очереди и передачу его соответствующему окну для обработки.
3. Процедура окна. Каждое окно приложения имеет собственную процедуру, которая получает все передаваемые данному окну сообщения. В ответ на полученное сообщение процедура должна выполнить некоторые действия.
Цикл сообщений встроен в модуль Forms, благодаря чему прикладному программисту не нужно беспокоиться о выборке сообщений из очереди и передаче их соответствующим процедурам окон. Кроме того, Delphi помещает информацию из записи типа TMsg в собственную запись типа TMessage.
TMessage = packed record
case Integer of
0: (WParam: Longint;
1: (WParamLo: Word;
Эта запись содержит меньше информации, чем в исходной записи TMsg. Кроме того, в TMessage содержится поле Result, которое используется для возврата результата обработки сообщения.
Обработка сообщений в Delphi
Обработка сообщений означает, что приложение тем или иным образом реагирует на получаемые от операционной системы сообщения.
Сообщение в Delphi может быть обработано четырьмя способами.
1. С помощью процедуры обработки сообщения.
2. В методе WndProc.
3. В методе Dispatch.
4. С помощью обработки события OnMessage объекта Application.
1. Наиболее простой способ обработать некоторое сообщение Windows, это создать для него специальную процедуру обработки сообщения. Такая процедура должна отвечать следующим требованиям:
• Должна быть методом.
• Должна иметь один var-параметр типа TMessage или любого специализированного типа.
• В конце объявления содержится ключевое слово message и константа, задающая тип обрабатываемого сообщения.
Пример процедуры, обрабатывающей сообщение WM_PAINT:
procedure WMPaint(var Msg: TWMPaint); message WM_PAINT;
2. В классе TControl имеется виртуальный метод WndProc:
procedure WndProc(var Message: TMessage); virtual;
Можно в любом потомке этого класса перекрыть этот метод, чтобы обработать нужное событие или сразу несколько событий.
3. У класса TObject существует виртуальный метод Dispatch:
procedure Dispatch(var Message); virtual;
Этот метод также можно перекрыть у класса-потомка, чтобы обработать одно или несколько сообщений.
4. Последний способ – использовать событие OnMessage объекта Application.
// фрагмент модуля Forms:
TMessageEvent = procedure (var Msg: TMsg; var Handled: Boolean) of object;
property OnMessage: TMessageEvent read FOnMessage write FOnMessage;
Это событие перехватывает все сообщения, выбранные из очереди, направляемые в адрес всех окон, относящихся к данному приложению.
В дополнение к обычной записи типа TMessage в Delphi определен набор специализированных записей для всех типов сообщений Windows. Они предназначены для работы с дополнительными данными сообщений без необходимости декодировать значения полей wParam и lParam.
Названия этих типов соответствуют типу сообщения, для которого они предназначены (начинаются с буквы T, и в имени нет символа подчеркивания). Для примера ниже приведено определение записи для события WM_ACTIVATE.
// фрагмент модуля Windows:
// фрагмент модуля Messages:
TWMActivate = packed record
Active: Word; // WA_INACTIVE, WA_ACTIVE, WA_CLICKACTIVE