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

Шрифт
Фон

Перекрывая обработку сообщения 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.

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

Рис 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);

private

// Здесь будет храниться номер, присвоенный системой

// глобальному сообщению

FSendNumberMessage: Cardinal;

protected

// Так как номер сообщения станет известным только при

// выполнении программы, объявить обработчик сообщения

// с помощью директивы message нельзя. Приходится

// перекрывать метод WndProc и обрабатывать сообщение в

// нем. Можно было бы вместо WndProc перекрыть метод

// DefaultHandler, но при этом зарегистрированное

// сообщение обрабатывалось бы медленнее, потому что

// сначала выполнялся бы метод WndProc, затем Dispatch

// пытался бы найти подходящий обработчик среди методов

// объекта, и лишь затем дело доходило бы до перекрытого

// DefaultHandler. Но, с другой стороны, при перекрытии

// WndProc обработка всех сообщений начинается со

// сравнения их номера с FSendNumberMessage и вызова

// унаследованного WndProc, если это другое сообщение.

// А до DefaultHandler многие сообщения не дойдут, т.к.

// будут обработаны ранее, и накладные расходы на

// сравнение и вызов унаследованного метода будут меньше.

procedure WndProc(var Msg: TMessage); override;

public

constructor Create(AOwner: TComponent); override;

end;

var

Form1: TForm1;

implementation

{$R *.dfm}

constructor TForm1.Create(AOwner: TComponent);

begin

// Регистрируем глобальное сообщение с именем

// WM_DelphiKingdom_APISample_SendNumber. Имя достаточно

// длинное и осмысленное, поэтому можно надеяться, что

// никакое другое приложение не зарегистрирует сообщение с

// таким же именем. Регистрация сообщения выполняется до

// вызова унаследованного конструктора, т.к. при

// выполнении этого конструктора окно получит ряд

// сообщений, и метод WndProc будет несколько раз вызван.

// Если вызвать унаследованный конструктор до вызова

// RegisterWindowMessage, то поле FSendNumberMessage

// будет иметь присвоенное ему по умолчанию значение 0,

// а это - код сообщения WM_NULL. Таким образом, если в

// это время окно получит сообщение WM_NULL, оно будет

// неправильно обработано. Конечно, вероятность получения

// WM_NULL во время выполнения унаследованного

// конструктора крайне мала, но лучше подстраховаться и

// сделать так, чтобы поле FSendNumberMessage на момент

// первого вызова WndProc уже имело правильное значение.

FSendNumberMessage := RegisterWindowMessage('WM_DelphiKingdom_APISample_SendNumber');

inherited;

// Здесь мы меняем стиль окна поля ввода, добавляя в него

// ES_NUMBER. Стиль ES_NUMBER запрещает полю ввода

// вводить какие-либо символы, кроме цифр. Это уменьшает

// риск ошибки ввода в тех случаях, когда требуется целое

// неотрицательное число.

SetWindowLong(EditNumber.Handle, GWL_STYLE, GetWindowLong(EditNumber.Handle, GWL_STYLE) or ES_NUMBER);

end;

Ваша оценка очень важна

0
Шрифт
Фон

Помогите Вашим друзьям узнать о библиотеке