KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программирование » А. Григорьев - О чём не пишут в книгах по Delphi

А. Григорьев - О чём не пишут в книгах по Delphi

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн А. Григорьев, "О чём не пишут в книгах по Delphi" бесплатно, без регистрации.
Перейти на страницу:

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

 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.

Метод DefaultHandler виртуальный, в классе TObject он не выполняет никаких действий, но наследники его переопределяют. Впервые он переопределяется в классе TControl для обработки сообщений, связанных с получением и установкой заголовка окна — WM_GETTEXT, WM_GETTEXTLENGTH и WM_SETTEXT. Напомним, что класс TControl является предком для всех визуальных компонентов, а не только оконных, и появление обработчика системных сообщений в этом классе — часть той имитации обработки сообщений неоконными компонентами, о которой мы уже говорили.

В классе TWinControl метод DefaultHandler также переопределен. Помимо передачи некоторых сообщений дочерним окнам (об этом мы будем подробнее говорить чуть позже) и обработки некоторых внутренних сообщений он вызывает оконную процедуру, адрес которой хранится в свойстве DefWndProc. Это свойство содержит адрес, который был присвоен полю WindowClass.lpfnWndProc структуры TCreateParams в методе CreateParams. По умолчанию это поле содержит адрес стандартной оконной процедуры DefWindowProc. Как было сказано ранее, обработка сообщений при использовании API обычно завершается вызовом этой процедуры. В классе TCustomForm метод DefaultHandler также переопределен, если форма является MDI-формой, сообщения, присланные ей, передаются в процедуру DefFrameProc (за исключением WM_SIZE, которое передается в DefWindowProc) независимо от того, какое значение имеет свойство DefWindowProc. Для всех остальных типов форм вызывается унаследованный от TWinControl DefaultHandler.

Повторим еще раз всю цепочку обработки сообщений оконными компонентами VCL (рис. 1.7). Для каждого компонента создается уникальная оконная процедура, которая передает управление методу MainWndProc. MainWndProc передает управление методу, указатель на который хранится в свойстве WindowProc. По умолчанию это метод компонента WndProc. Он осуществляет обработку некоторых сообщений, но в большинстве случаев передает управление методу Dispatch, который ищет среди методов компонента или его предков обработчик данного сообщения. Если обработчик не найден, управление получает метод DefaultHandler (он может также получить управление и в том случае, если обработчик найден, но он вызывает inherited). DefaultHandler самостоятельно обрабатывает некоторые сообщения, но большинство из них передаётся оконной процедуре, адрес хранится в свойстве DefWndProc (по умолчанию это стандартная функция Windows API DefWindowProc).

Рис. 1.7. Блок-схема оконной процедуры оконных компонентов VCL


Класс TControl имеет метод Perform, с помощи которого можно заставить визуальный компонент выполнить обработку конкретного сообщения в обход оконной процедуры и системного механизма передачи сообщений. Perform приводит к непосредственному вызову метода, указатель на который хранится в свойстве WindowProc. Дальше цепочка обработки сообщений такая же, как и при получении сообщения через оконную процедуру. Для оконных компонентов вызов Perform по своим последствиям практически эквивалентен передаче сообщения с помощью SendMessage с двумя исключениями. Во-первых, при использовании SendMessage система обеспечивает переключение между нитями, и сообщение будет выполнено в той нити, которая создала окно, a Perform никакого переключения не производит, и обработка сообщения будет выполнена той нитью, которая вызвала Perform. Поэтому Perform, в отличие от SendMessage, можно использовать только в главной нити (напомним, что VCL — принципиально однонитевая библиотека, и создание форм вне главной нити с ее помощью недопустимо). Во-вторых, Perform выполняется чуть быстрее, т.к. оконная процедура и метод MainWndProc исключаются из цепочки обработки сообщения.

Но основное преимущество Perform перед SendMessage заключается в том, что Perform пригоден для работы со всеми визуальными компонентами, а не только с оконными. Неоконные визуальные компоненты не могут иметь оконной процедуры, но цепочка обработки сообщений у них есть. В ней отсутствует оконная процедура и метол MainWndProc, a DefaultHandler не вызывает никаких стандартных оконных процедур, но во всем остальном эта цепочка полностью эквивалентна цепочке оконных компонентов. Таким образом, цепочка обработки сообщений оконных компонентов имеет две точки входа: оконную процедуру и метод Perform, а цепочка неоконных компонентов — только метод Perform. Следовательно, метод Perform универсален: он одинаково хорошо подходит как для оконных, так и для неоконных компонентов. Он широко применяется в VCL, т.к. позволяет единообразно работать с любыми визуальными компонентами.

Неоконным визуальным компонентам сообщения посылает их родительское окно. Например, как мы уже говорили, обработка сообщений, связанных с мышью, в классе TWinControl включает в себя, не попадают ли координаты курсора в область какого-либо из дочерних неоконных компонентов. И если попадает, оконный компонент не обрабатывает это сообщение самостоятельно, а транслирует его соответствующему неоконному компоненту с помощью Perform. Эта трансляция и обеспечивает получение сообщений неоконными компонентами.

Сообщения в VCL транслируются не только неоконным, но и оконным компонентам. В Windows все сообщения, информирующие об изменении состояния стандартных элементов управления, получает их родительское окно, а не сам элемент. Например, при нажатии на кнопку уведомительное сообщение об этом получает не сама кнопка, а окно, ее содержащее. Сама кнопка получает и обрабатывает только те сообщения, которые обычно разработчику неинтересны. Это упрощает работу программиста, т.к. не требуется для каждого элемента управления писать свою оконную процедуру, все значимые сообщения получает оконная процедура родительского окна. Рассмотрим, что происходит при нажатии кнопки на форме. Окно, содержащее эту кнопку, получает сообщение WM_COMMAND, уведомляющее о возникновении события среди оконных компонентов. Параметры сообщения позволяют определить, какое именно событие и с каким элементом управления произошло (в данном случае событие будет BN_CLICKED). Обработчик WM_COMMAND класса TWinControl находит компонент, вызвавший сообщение, и посылает ему сообщение CN_COMMAND (как видно из префикса, это внутреннее сообщение VCL) с теми же параметрами. В нашем примере это будет экземпляр класса TButton, реализующий кнопку, которую нажал пользователь. Получив CN_COMMAND, компонент начинает обработку произошедшего с ним события (в частности, TButton инициирует событие OnСlick).

Примечание

К переопределению обработчика WM_COMMAND нужно относиться осторожно, чтобы не нарушить механизм трансляции сообщений. Примером неправильного переопределения может служить класс TCustomGrid. В форумах нередко встречаются вопросы, почему элементы управления, родителем которых является TDrawGrid или TStringGrid, некорректно ведут себя: кнопки при нажатии не генерируют событие OnClick, выпадающие списки остаются пустыми и т.д. Это связано с тем, что обработчик WM_COMMAND в TCustomGrid учитывает возможность существования только одного дочернего компонента — внутреннего редактора, возникающего при включенной опции goEditing. Остальным дочерним компонентам WM_COMMAND не транслируются, и они лишены возможности корректно реагировать на происходящие с ними события. Выходом из ситуации может стать либо создание наследника от TDrawGrid или TStringGrid, который правильно транслирует WM_COMMAND, либо назначение родительским окном компонента, вставляемого в сетку, формы, панели или иного оконного компонента, который правильно транслирует это сообщение.

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*