Но основное преимущество 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
, либо назначение родительским окном компонента, вставляемого в сетку, формы, панели или иного оконного компонента, который правильно транслирует это сообщение.
Рассмотрим все методы, с помощью которых можно встроить свой код в цепочку обработки сообщений оконным компонентом и перехватить сообщения. Всего существует шесть способов сделать это.
1. Как и у всякого окна, у оконного компонента VCL можно изменить оконную процедуру с помощью функции SetWindowLong
. Этот способ лучше не применять, поскольку код VCL не будет ничего "знать" об этом переопределении, и сообщения, получаемые компонентом не через оконную процедуру, а с помощью Perform
, не будут перехвачены. Другой недостаток данного способа — то, что изменение некоторых свойств компонента (например, FormStyle
и BorderStyle
у формы) невозможно без уничтожения окна и создания нового. Для программиста это пересоздание окна выглядит прозрачно, но новое окно получит новую оконную процедуру, и нужно будет выполнять перехват заново. Отследить момент пересоздания окна можно с помощью сообщения CM_RECREATEWND
, обработчик которого уничтожает старое окно, а создание нового окна откладывается до момента первого обращения к свойству Handle
. Если перехватить по сообщение, то, в принципе, после выполнения стандартного обработчика можно зaново установить перехват с помощью SetWindowLong, но т.к. этот способ не дает никаких преимуществ перед другими, более простыми, им все равно лучше не пользоваться.
Читать дальше
Конец ознакомительного отрывка
Купить книгу