Унаследованный обработчик закрашивает всю панель целиком, поэтому его нужно вызывать до того, как мы нарисуем что-то свое, иначе он просто закрасит то, что мы нарисовали. Так что следующий шаг — это вызов стандартного обработчика сообщений панели, указатель на который мы сохранили в поле FOldPanelWndProc
. Только после этого можно что-то рисовать.
Примечание
Перекрывая обработку сообщения WM_PAINT
, мы лишаем код VCL возможности полностью контролировать процесс перерисовки. В частности, это означает что значение свойства DoubleBuffered
будет игнорироваться, двойной буферизации не будет. Поэтому еще раз напоминаем, что программа PanelMsg
— это учебный пример, помогающий разобраться с механизмами взаимодействия VCL и Windows API, но не являющийся образцом для подражания. Если в реальной жизни потребуется рисовать что-то непосредственно на панели, нужно порождать от класса TPanel
наследника и перекрывать в нем метод Paint
.
Теперь можно нарисовать что-то свое. Здесь мы рисуем большой белый круг, а на его фоне — желтый прямоугольник. Для этого используем класс TCanvas
способом, который был продемонстрирован в листинге 1.17 (см. разд. 1.1.11). Если бы мы остановились на этом, то увидели бы интересную картину: нарисованные фигуры лежат поверх текста метки Label1
. Объяснение этому очень простое: метка является неоконным визуальным компонентом и рисуется на поверхности своего родительского компонента при обработке его сообщения WM_PAINT
. А поскольку стандартный обработчик у нас вызывается до того, как рисуются круг и прямоугольник, любой неоконный компонент будет перекрыт ими. К оконным компонентам это, разумеется, не относится, они лежат над родительской панелью, и то, что мы рисуем на этой панели, не может оказаться над ними.
Мы не можем вставить свой код между рисованием непосредственно поверхности панели и рисованием компонентов на ней. Поэтому после отработки нашего кода приходится рисовать неоконные компоненты еще раз. Проще всего это сделать, вызвав метод PaintControls
, который и используется стандартным обработчиком. Конечно, получится, что неоконные компоненты рисуются дважды: в стандартном обработчике и в нашем, и это не очень хорошо. Но повторим еще раз, что программа PanelMsg
— не образец для подражания, а что-то вроде зонда для исследования особенностей работы VCL.
Вызов метода PaintControls
затруднен тем, что он объявлен в разделе protected
, а потому не может быть вызван из метода NewPanelWndProc
, который относится к классу формы. Чтобы обойти это ограничение, нужно породить наследника от TPanel
— TFakePanel
. Этот наследник ничего не добавляет к классу TPanel
и ничего не переопределяет в нем. Но раз он объявлен в нашем модуле, все его protected
-члены, в том числе и унаследованный метод PaintControls
, становятся доступными в нем. После этого мы можем привести поле, содержащее ссылку на панель, к этому типу и вызвать PaintControls
. Так как структуры типов TPanel
и TFakePanel
идентичны, это приведет к вызову нужного метода.
Для завершения обработки сообщения WM_PAINT
осталось только вызвать EndPaint
, разумеется, только в том случае, если BeginPaint
вызывали мы сами.
И последнее, что мы должны сделать, — это передать все остальные сообщения стандартному обработчику. После этого программа PanelMsg готова.
1.2.5. Пример NumBroadcast
Программа NumBroadcast демонстрирует широковещательную рассылку глобальных сообщений. Окно программы показано на рис. 1.10.
Рис 1.10.Окно программы NumBroadcast
Для того чтобы увидеть, как работает программа, нужно запустить несколько ее экземпляров. После ввода числа и нажатия кнопки Разослатьлюбому из экземпляров программы число под кнопкой меняется во всех экземплярах. Чтобы добиться такого эффекта, программа NumBroadcast регистрирует глобальное сообщение с помощью функции RegisterWindowMessage
, а в оконной процедуре предусмотрена реакция на это сообщение (число передастся через параметр WParam
). Код программы приведен в листинге 1.31.
Листинг 1.31. Модуль главного окна программы NumBroadcast
unit NBMain;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type TForm1 = class(TForm)
EditNumber: TEdit;
BtnBroadcast: TButton;
LabelNumber: TLabel;
procedure BtnBroadcastClick(Sender: TObject);
Читать дальше
Конец ознакомительного отрывка
Купить книгу