Чтение онлайн

на главную - закладки

Жанры

О чём не пишут в книгах по Delphi

Григорьев Антон Борисович

Шрифт:

Рис. 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
, либо назначение родительским окном компонента, вставляемого в сетку, формы, панели или иного оконного компонента, который правильно транслирует это сообщение.

Рассмотрим все методы, с помощью которых можно встроить свой код в цепочку обработки сообщений оконным компонентом и перехватить сообщения. Всего существует шесть способов сделать это.

1. Как

и у всякого окна, у оконного компонента VCL можно изменить оконную процедуру с помощью функции
SetWindowLong
. Этот способ лучше не применять, поскольку код VCL не будет ничего "знать" об этом переопределении, и сообщения, получаемые компонентом не через оконную процедуру, а с помощью
Perform
, не будут перехвачены. Другой недостаток данного способа — то, что изменение некоторых свойств компонента (например,
FormStyle
и
BorderStyle
у формы) невозможно без уничтожения окна и создания нового. Для программиста это пересоздание окна выглядит прозрачно, но новое окно получит новую оконную процедуру, и нужно будет выполнять перехват заново. Отследить момент пересоздания окна можно с помощью сообщения
CM_RECREATEWND
, обработчик которого уничтожает старое окно, а создание нового окна откладывается до момента первого обращения к свойству
Handle
. Если перехватить по сообщение, то, в принципе, после выполнения стандартного обработчика можно зaново установить перехват с помощью SetWindowLong, но т.к. этот способ не дает никаких преимуществ перед другими, более простыми, им все равно лучше не пользоваться.

2. Можно создать собственный метод обработки сообщения и поместить указатель на него в свойство

WindowProc
. При этом старый указатель обычно запоминается, т.к. новый обработчик обрабатывает лишь некоторые сообщения, а остальные передает старому. Достоинство этого способа — то, что метод, указатель на который помещается в
WindowProc
, не обязан принадлежать тому компоненту, сообщения которого перехватываются. Это позволяет, во-первых, создавать компоненты, которые влияют на обработку сообщений родительскими формами, а во-вторых, реализовывать нестандартную обработку сообщений стандартными компонентами, не порождая от них наследника.

3. При написании нового компонента можно перекрыть виртуальный метод

WndProc
и реализовать обработку нужных сообщений в нем. Это позволяет компоненту перехватывать сообщения в самом начале цепочки (за исключением внешних обработчиков, установленных с помощью свойства
WindowProc
— здесь разработчик компонента не властен).

4. Наиболее удобный способ самостоятельной обработки событий — написание их методов-обработчиков. Этот способ встречается чаще всего. Его недостатком является то, что номера обрабатываемых сообщений должны быть известны на этапе компиляции. Для системных сообщений и внутренних сообщений VCL это условие выполняется, но далее мы будем говорить об определяемых пользователем сообщениях, номера которых в некоторых случаях на этапе компиляции неизвестны. Обрабатывать такие сообщения с помощью методов с директивой невозможно.

5. Для перехвата сообщений, которые не были обработаны с помощью методов-обработчиков, можно перекрыть виртуальный метод.

6. И наконец, можно написать оконную процедуру и поместить указатель на нее в свойство

DefWndProc
. Этот способ по своим возможностям практически эквивалентен предыдущему, но менее удобен. Однако предыдущий способ пригоден только для создания собственного компонента, в то время как
DefWndProc
можно изменять у экземпляров существующих классов. Напомним, что этот способ не подходит для форм, у которых
FormStyle = fsMDIForm
, т.к. такие формы игнорируют значение свойства
DefWndProc
.

Для перехвата сообщений неоконных визуальных компонентов допустимы все перечисленные способы, за исключением первого и последнего.

Метод

WndProc
оконного компонента транслирует сообщения от мыши неоконным визуальным компонентам, родителем которых он является. Например. если положить на форму компонент
TImage
и переопределить у этой формы метод для обработки сообщения
WM_LBUTTONDOWN
, то нажатие кнопки мыши над
TImage
не приведет к вызову этого метода, т.к.
WndProc
передаст это сообщение в
TImage
, и
Dispatch
не будет вызван. Но если переопределить
WndProc
или изменить значение свойства
WindowProc
(т.е. использовать второй или третий метод перехвата), то можно получать и обрабатывать и те "мышиные" сообщения, которые должны транслироваться неоконным дочерним компонентам. Это общее правило: чем раньше встраивается собственный код в цепочку обработки сообщений, тем больше у него возможностей. Как мы уже говорили, начиная с BDS 2006 появился еще один способ перехвата сообщений — перекрытие метода
PreProcessMessage
. Этот способ нельзя ставить в один ряд с перечисленными ранее шестью способами, т.к. он имеет два существенных отличия от них. Во-первых, с помощью этого способа перехватываются все сообщения, попавшие в петлю сообщений, а не только те, которые посланы конкретному компоненту, из-за чего может понадобиться дополнительная фильтрация сообщений. Во-вторых, метод
PreProcessMessage
перехватывает сообщения, попавшие в петлю сообщений, а не в оконную процедуру компонента. С одной стороны, это даёт возможность перехватывать те сообщения, которые метод
Аррlication.ProcessMessage
не считает нужным передавать в оконную процедуру, но с другой стороны, не позволяет перехватывать те сообщения, которые окно получает, минуя петлю сообщений (например, те, которые отправлены с помощью
SendMessage
или
Perform
). По этим причинам область применения данного способа совсем другая, чем у способов, связанных с внедрением кода в оконную процедур. Перекрытие
PreProcessMessage
сопоставимо, скорее, с использованием события
Application.OnMessage
.

Поделиться:
Популярные книги

Мир-о-творец

Ланцов Михаил Алексеевич
8. Помещик
Фантастика:
альтернативная история
5.00
рейтинг книги
Мир-о-творец

Виконт. Книга 4. Колонист

Юллем Евгений
Псевдоним `Испанец`
Фантастика:
фэнтези
попаданцы
аниме
7.50
рейтинг книги
Виконт. Книга 4. Колонист

Титан империи

Артемов Александр Александрович
1. Титан Империи
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Титан империи

Запретный Мир

Каменистый Артем
1. Запретный Мир
Фантастика:
фэнтези
героическая фантастика
8.94
рейтинг книги
Запретный Мир

Ты предал нашу семью

Рей Полина
2. Предатели
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Ты предал нашу семью

Цеховик. Книга 2. Движение к цели

Ромов Дмитрий
2. Цеховик
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Цеховик. Книга 2. Движение к цели

Сила рода. Том 3

Вяч Павел
2. Претендент
Фантастика:
фэнтези
боевая фантастика
6.17
рейтинг книги
Сила рода. Том 3

Проданная невеста

Wolf Lita
Любовные романы:
любовно-фантастические романы
5.80
рейтинг книги
Проданная невеста

Волк 7: Лихие 90-е

Киров Никита
7. Волков
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Волк 7: Лихие 90-е

Падение Твердыни

Распопов Дмитрий Викторович
6. Венецианский купец
Фантастика:
попаданцы
альтернативная история
5.33
рейтинг книги
Падение Твердыни

Приручитель женщин-монстров. Том 9

Дорничев Дмитрий
9. Покемоны? Какие покемоны?
Фантастика:
юмористическое фэнтези
аниме
5.00
рейтинг книги
Приручитель женщин-монстров. Том 9

Ночь со зверем

Владимирова Анна
3. Оборотни-медведи
Любовные романы:
любовно-фантастические романы
5.25
рейтинг книги
Ночь со зверем

Я – Орк. Том 6

Лисицин Евгений
6. Я — Орк
Фантастика:
городское фэнтези
попаданцы
аниме
5.00
рейтинг книги
Я – Орк. Том 6

Совок-8

Агарев Вадим
8. Совок
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Совок-8