FText := nil;
UpdateBounds;
Perform(WM_SETFONT, FFont.Handle, 1);
if AutoSize then AdjustSize;
end;
Собственно создание окна опять происходит не здесь, а в методе CreateWindowHandle
, который очень прост: он состоит из одного только вызова API-функции CreateWindowEx
с параметрами, значения которых берутся из полей записи Params
типа TCreateParams
(листинг 1.10)
TCreateParams
TCreateParams = record
Caption: PChar;
Style: WORD;
ExStyle: DWORD;
X, Y: Integer;
Width, Height: Integer;
WndParent: HWnd;
Param: Pointer;
WindowClass: TWndClass;
WinClassName: array[0..63] of Char;
end;
В записи Params
хранятся параметры как окна, передаваемые в функцию WindowCreateEx
, так и оконного класса (поля WindowClass
и WndClassName
). Все поля инициализируются методом CreateParams
на основе значений свойств оконного компонента. Данный метод виртуальный и может быть перекрыт в наследниках, что бывает полезно, когда необходимо изменить стиль создаваемого окна. Например, добавив расширенный стиль WS_EX_CLIENTEDGE
(или, как вариант, WS_EX_STATICEDGE
), можно получить окно с необычной рамкой (листинг 1.11).
CreateParams
procedure TForm1.CreateParams(var Params: TCreateParams);
begin
// Вызов унаследованного метода заполнения всех полей
// записи Params
inherited CreateParams(Params);
// Добавляем флаг WS_EX_CLIENTEEDGE к расширенному стилю окна
Params.ExStyle := Params.ExStyle or WS_EX_CLIENTEDGE;
end;
ВParams.WinClassName
.
Обратите внимание, что всем без исключения классам метод CreateWnd
назначает одну и ту же оконную процедуру — InitWndProc
. Это является основой в обработке сообщений с помощью VCL, именно поэтому оконная процедура назначается не в методе CreateParams
, а в методе CreateWnd
, чтобы в наследниках нельзя было изменить это поведение (метод CreateWnd
тоже виртуальный, но при его переопределении имеет смысл только добавлять какие-то действия, а не изменять поведение унаследованного метода).
Чтобы понять, как работает процедура InitWndProc
, обратите внимание на еще одну особенность метода CreateWnd
: перед вызовом CreateWindowHandle
(т.е. непосредственно перед созданием окна) он записывает ссылку на текущий объект в глобальную переменную СreationСontrol
. Эта переменная затем используется процедурой InitWndProc
(листинг 1.12).
InitWndProc
function InitWndProc(HWindow: HWnd; Message, WParam, LParam: LongInt): LongInt;
begin
CreationControl.FHandle := HWindow;
SetWindowLong (HWindow, GWL_WNDPROC, LongInt(CreationControl.FObjectInstance));
if (GetWindowLong(HWindow, GWL_STYLE) and WS_CHILD <> 0) and (GetWindowLong(HWindow, GWL_ID) = 0) then
SetWindowLong(HWindow, GWL_ID, HWindow);
SetProp(HWindow, MakeIntAtom(ControlAtom), THandle(CreationControl));
SetProp(HWindow, MakeIntAtom(WindowAtom), THandle(CreationControl));
asm
PUSH LParam
PUSH WParam
PUSH Message
PUSH HWindow
MOV EAX, CreationControl
MOV CreationControl, 0
CALL [EAX].TWinControl.FObjectInstance
MOV Result, EAX
end;
end;