Для рисования на поверхности панели
, вообще говоря, существует более простой и правильный способ: нужно положить на панель компонент TPaintBox
, растянуть его на всю область панели и рисовать в его событии OnPaint. Мы здесь используем более сложный способ перехвата сообщения WM_PAINT
только в учебных целях.
При перехвате сообщения WM_PAINT
любого компонента, на котором расположены неоконные визуальные компоненты, может возникнуть проблема с перерисовкой этих компонентов. Чтобы продемонстрировать способ решения этих проблем, разместим на панели компонент TLabel, который заодно будет показывать пользователю реакцию на двойной щелчок правой кнопкой мыши. В результате получается окно, показанное на рис. 1.9. При двойном щелчке правой кнопкой мыши на панели надпись Сделайте двойной щелчок правой кнопкой перемещается в то место, где находится курсор. Чтобы перехватить оконную процедуру панели, следует написать метод, который ее подменит, а адрес старого метода сохранить в предназначенном для этого поле. Сам перехват будем осуществлять в обработчике события OnCreate
формы (листинг 1.29).
Рис. 1.9.
Окно программы PanelMsg
type
TForm1 = class(TForm)
Panel: TPanel;
Label1: TLabel;
procedure FormCreate(Sender: TObject);
private
// Здесь будет храниться исходный обработчик сообщений
// панели
FOldPanelWndProc: TWndMethod;
// Этот метод будет перехватывать сообщения,
// предназначенные панели
procedure NewPanelWndProc(var Msg: TMessage);
end;
…
procedure TForm1.FontCreate(Sender: TObject);
begin
FOldPanelWndProc:= Panel.WindowProc;
Panel.WindowProc:= NewPanelWndProc;
end;
Сам перехватчик выглядит так, как показано в листинге 1.30.
procedure TForm1.NewPanelWndProc(var Msg: TMessage);
var
NeedDC: Boolean;
PS: TPaintStruct;
PanelCanvas: TCanvas;
begin
if Msg.Msg = WM_RBUTTONDBLCLK then
begin
Label1.Left:= Msg.LParamLo;
Label1.Top:= Msg.LParamHi;
Msg.Result:= 0;
end
else if Msg.Msg = WM_PAINT then
begin
// Проверяем, был ли запрошен контекст устройства
// обработчиком, стоящим раньше по цепочке, и если не
// был, то запрашиваем его.
NeedDC:= Msg.WParam = 0;
if NeedDC then Msg.WParam:= BeginPaint(Panel.Handle, PS);
// Вызываем старый обработчик WM_PAINT. Его нужно
// вызывать обязательно до того, как мы начнем рисовать
// на поверхности что-то свое, т. к. в противном случае
// это что-то будет закрашено стандартным обработчиком.
POldPanelWndProc(Msg);
// При использовании графических функций API самое
// неудобное — это вручную создавать и уничтожать кисти,
// карандаш и т. п. Поэтому здесь создается экземпляр
// класса TCanvas для рисования на контексте устройства
// с дескриптором, полученным при вызове BeginPaint.
PanelCanvas:= TCanvas.Create;
try
PanelCanvas.Handle:= Msg.WParam;
FanelCanvas.Pen.Style:= psClear;
PanelCanvas.Brush.Style:= bsSolid;
PanelCanvas.Brush.Color:= clWhite;
PanelCanvas.Ellipse(10, 10, Panel.Width — 10, Panel.Height — 10);