Выбрать главу

События мыши метод WndProc диспетчеризует самостоятельно, без помощи функции DispatchMessage. Это связано с тем, что DispatchMessage передаёт сообщение тому оконному компоненту, которому оно предназначено с точки зрения системы. Однако с точки зрения VCL этот компонент может являться родителем для неоконных визуальных компонентов, и если сообщение от мыши связано с их областью, то оно должно обрабатываться соответствующим неоконным компонентом, а не его оконным родителем. DispatchMessage ничего о неоконных компонентах не "знает" и не может передать им сообщения, поэтому разработчикам VCL пришлось реализовывать свой способ. Те сообщения, которые метод WndProc не обрабатывает самостоятельно (а их — подавляющее большинство), он передает в метод Dispatch, который объявлен и реализован в классе TObject. На первый взгляд может показаться странным, что в самом базовом классе реализована функциональность, использующаяся только в визуальных компонентах. Эта странность объясняется тем, что разработчики Delphi встроили поддержку обработки сообщений непосредственно в язык. Методы класса, описанные с директивой message, служат специально для обработки сообщений. Синтаксис описания такого метода следующий:

procedure <Name>(var Message: <TMsgType>); message <MsgNumber>;

<MsgNumber> — это номер сообщения, для обработки которого предназначен метод. Имя метода может быть любым, но традиционно оно совпадает с именем константы сообщения за исключением того, что в нем выбран более удобный регистр символов и отсутствует символ "_" (например, метод для обработки WM_SIZE будет называться WMSize).

В качестве типа параметра <TMsgType> компилятор разрешает любой тип, но на практике имеет смысл только использование типа TMessage или "совместимого" с ним. Тип TMessage описан в листинге 1.14.

Листинг 1.14. Описание типа TMessage

TMessage = packed record

 Msg: Cardinal;

 case Integer of

 0: (

  WParam: LongInt;

  LParam: LongInt;

  Result: LongInt);

 1: (

  WParamLo: Word;

  WParamHi: Word;

  LParamLo: Word;

  LParamHi: Word;

  ResultLo: Word;

  ResultHi: Word);

end;

Поле Msg содержит номер сообщения, поля WParam и LParam — значение одноименных параметров сообщения. Поле Result — выходное: метод, осуществляющий окончательную обработку сообщения, заносит в него то значение, которое должна вернуть оконная процедура. Поля с суффиксами Lo и Hi позволяют обращаться отдельно к младшему и старшему словам соответствующих полей, что может быть очень полезно, когда эти параметры содержат пару 16-разрядных значений. Например, у сообщения WM_MOUSEREMOVE младшее слово параметра LParam содержит X-координату мыши, старшее — Y-координату. В случае обработки этого сообщения поле LParamLo будет содержать X-координату, LParamHi — Y-координату.

"Совместимыми" с TMessage можно назвать структуры, которые имеют такой же размер, а также параметр Msg, задающий сообщение. Эти структуры учитывают специфику конкретного сообщения. Их имена образуются из имени сообщения путем отбрасывания символа и добавления префикса T. Для уже упоминавшегося сообщения WM_MOUSEMOVE соответствующий тип выглядит, как показано в листинге 1.15.

Листинг 1.15. Тип TWMNCMouseMove

TWMNCMouseMove = packed record

 Msg: Cardinal;

 HitTest: LongInt;

 XCursor: SmallInt;

 YCursor: SmallInt;

 Result: LongInt;

end;

Параметр WParam переименован в HitTest, что лучше отражает его смысл в данном случае, а параметр LParam разбит на две 16-разрядных части: XCursor и YCursor.

Параметр метода для обработки сообщения имеет тип, соответствующий обрабатываемому сообщению (при необходимости можно описать свой тип), или тип TMessage. Таким образом, обработчик сообщения WM_MOUSEMOVE будет выглядеть так, как показано в листинге 1.16.

Листинг 1.16. Объявление и реализация метода для обработки сообщения WM_MOUSEMOVE

type

 TSomeForm = class(TForm)

 ...............

 procedure WMNCMouseMove(var Message: TWMNCMouseMove); message WM_NCMOUSEMOVE;

 ................

end;

procedure TSomeForm.WMNCMouseMove(var Message: TWMNCMouseMove);

begin

 ...............

 inherited; // Возможно, этот вызов не будет нужен

end;

Метод для обработки сообщения может выполнить ее полностью самостоятельно, тогда он не должен вызывать унаследованный метод обработки сообщения. Если же реакция предка на сообщение в целом устраивает разработчика, но нуждается только в дополнении, ключевое слово inherited позволяет вызвать унаследованный обработчик для данного сообщения. Таким образом, может образовываться целая цепочка вызовов унаследованных обработчиков одного и того же сообщения, каждый из которых выполняет свою часть обработки. Если у предков класса нет обработчика данного сообщения, директива inherited передает управление методу TObject.DetaultHandler. Вернемся к методу Dispatch. Он ищет среди обработчиков сообщения класса (собственных или унаследованных) метод для обработки сообщения, заданного полем Msg параметра Message и, если находит, передает управление ему. Если ни сам класс, ни его предки не содержат обработчика данного сообщения, то обработка передаётся методу DefaultHandler.