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

на главную

Жанры

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

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

Шрифт:

MoveToEx(DC, FCoords[0], FCoords[1], nil);

LineTo(DC, FCoords[2], FCoords[3]);

FCoords[0] := Msg.XPos;

FCoords[1] := Msg.YPos;

MoveToEx(DC, FCoords[0], FCoords[1], nil);

LineTo(DC, FCoords[2], FCoords[3]));

SetROP2(DC, OldMode);

ReleaseDC(FWinOwner.Handle, DC);

Msg.Result := 0;

 end

 else if FEndMoving then

 begin

DC := GetDC(FWinOwner.Handle);

OldMode := SetROP2(DC, R2_NOT);

SelectObject(DC, GetStockObject(BLACK_PEN));

MoveToEx(DC, FCoords[0], FCoords[1], nil);

LineTo(DC, FCoords[2], FCoords[3]);

FCoords[2] := Msg.XPos;

FCoords[3] := Msg.YPos;

MoveToEx(DC, FCoords[0], FCoords[1], nil);

LineTo(DC, FCoords[2], FCoords[3]);

SetROP2(DC, OldMode);

ReleaseDC(FWinOwner.Handle, DC);

Msg.Result := 0;

 end

 else inherited;

end;

Здесь

реализован инверсный способ создания "резиновой" линии, когда при рисовании линии все составляющие ее пикселы инвертируются, а при стирании инвертируются еще раз. Этот способ подробно описан в разд. 1.3.4.2. Перехват сообщений родителя — дело относительно простое, гораздо хуже обстоят дела с удалением компонента, перехватившего сообщения родителя. Пока такой компонент один, проблем не возникает, но когда их несколько приходится обращаться с ними очень аккуратно. Рассмотрим, например, такой код (листинг 1.27).

Листинг 1.27. Пример кода, вызывающего ошибку

Line1 := TLine.Create(Form1);

Line2 := TLine.Create(Form2);

...

Line1.Free;

...

Line2.Free;

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

Form1.WindowProc
ссылается на
Form1.WndProc
, т.е. на собственный обработчик сообщений формы. При создании объекта
Line1
он перехватывает обработчик, и
Form1.WindowProc
начинает ссылаться на
Line1.HookOwnerMessage
, а ссылка на
Form1.WndProc
сохраняется в
Line1.FOldProc
. Объект
Line2
также перехватывает обработчик сообщений, и после его создания
Form1.WindowProc
будет ссылаться на
Line2.HookOwnerMessage
, a
Line2.FOldProc
— на
Line1.HookOwnerMessage
.

Теперь удалим

Line1
. При удалении объект восстановит ссылку на тот обработчик сообщений, который был установлен на момент его создания, т.е.
Form1.WindowProc
вновь станет указывать на
Form1.WndProc
. Соответственно, компонент
Line2
потеряет способность реагировать на сообщения владельца. Поле
Line2.FOldProc
при этом останется без изменений. Но самое неприятное начнется при удалении объекта
Line2
. Он тоже восстановит ссылку на
обработчик, который был назначен на момент его создания, т.е. запишет в свойство
Form1.WindowProc
ссылку на
Line1.HookOwnerMessage
. Но поскольку объекта
Line1
уже не существует, это будет ссылка в никуда, и обработка первого же сообщения, пришедшего форме, даст ошибку Access violation.

Примечание

Аналогичная проблема возникнет и в режиме проектирования, если на форму положить два компонента

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

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

1.2.3. Пример CoordLabel

CoordLabel — это пример визуального компонента, перехватывающего сообщения своего родителя. Компонент

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

Установка компонента

TCoordLabel
полностью аналогична установке компонента
TLine
из предыдущего раздела. На прилагаемом компакт-диске находится также проект LineCoordSample для того, чтобы работу компонента можно было увидеть без установки в палитру компонентов. На форме проекта LineCoordSample находится панель, кнопка Переместить и компонент
TLineCoordSample
, который по нажатию кнопки меняет родителя с формы на панель и обратно.

Код компонента

TCoordLabel
приведен в листинге 1.28.

Листинг 1.28. Компонент
TCoordLabel

type

 TCoordLabel = class(TLabel)

private

 // Здесь хранится адрес обработчика

 // сообщений, бывший до перехвата.

 FOldProc: TWndMethod;

protected

 procedure SetParent(AParent: TWinControl); override;

 // Этот метод будет новым обработчиком

 // сообщений владельца

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

Жребий некроманта 2

Решетов Евгений Валерьевич
2. Жребий некроманта
Фантастика:
боевая фантастика
6.87
рейтинг книги
Жребий некроманта 2

Предатель. Вернуть любимую

Дали Мила
4. Измены
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Предатель. Вернуть любимую

Бывший муж

Рузанова Ольга
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Бывший муж

Мама для дракончика или Жена к вылуплению

Максонова Мария
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Мама для дракончика или Жена к вылуплению

Жандарм 5

Семин Никита
5. Жандарм
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Жандарм 5

Менталист. Эмансипация

Еслер Андрей
1. Выиграть у времени
Фантастика:
альтернативная история
7.52
рейтинг книги
Менталист. Эмансипация

Кодекс Охотника. Книга XXIV

Винокуров Юрий
24. Кодекс Охотника
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Кодекс Охотника. Книга XXIV

Лорд Системы 14

Токсик Саша
14. Лорд Системы
Фантастика:
фэнтези
попаданцы
рпг
5.00
рейтинг книги
Лорд Системы 14

Смерть может танцевать 4

Вальтер Макс
4. Безликий
Фантастика:
боевая фантастика
5.85
рейтинг книги
Смерть может танцевать 4

Идеальный мир для Лекаря 2

Сапфир Олег
2. Лекарь
Фантастика:
юмористическая фантастика
попаданцы
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря 2

Черный Маг Императора 8

Герда Александр
8. Черный маг императора
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Черный Маг Императора 8

Не отпускаю

Шагаева Наталья
Любовные романы:
современные любовные романы
эро литература
8.44
рейтинг книги
Не отпускаю

Идеальный мир для Лекаря 8

Сапфир Олег
8. Лекарь
Фантастика:
юмористическое фэнтези
аниме
7.00
рейтинг книги
Идеальный мир для Лекаря 8

Темный Патриарх Светлого Рода 3

Лисицин Евгений
3. Темный Патриарх Светлого Рода
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Темный Патриарх Светлого Рода 3