В качестве примера рассмотрим создание подкласса для некоторого окна, дескриптор которого содержится в переменной Wnd
. Пусть нам потребовалось для этого окна нестандартным образом обрабатывать сообщение WM_KILLFOCUS
.
Тогда код новой оконной процедуры и код ее установки будет выглядеть так, как показано в листинге 1.7.
Листинг 1.7. Создание подкласса для особой обработки сообщения WM_KILLPFOCUS
var
OldWndProc: TFNWndProc;
function NewWindowProc(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM) : LRESULT; stdcall;
begin
if Msg = WM_KILLFOCUS then
// Обработка события
else
Result := CallWindowProc(OldWndProc, hWnd, Msg, wParam, lParam);
end;
...
// Установка новой оконной процедуры окну Wnd
OldWndProc := TFNWndProc(SetWindowLong(Wnd, GWL_WNDPROC, Longint(@NewWindowProc)));
...
Примечание
MSDN называет функции GetWindowLong
и SetWindowLong
устаревшими и рекомендует использовать вместо них GetWindowLongPtr
и SetWindowLongPtr
, совместимые с 64-разрядными версиями Windows. Однако до 2007-й версии Delphi включительно эти функции отсутствуют в модуле Windows, и при необходимости их следует импортировать самостоятельно.
Переопределять оконную процедуру с помощью SetWindowLong
можно и у тех окон, оконная процедура которых была переопределена ранее. Таким образом создаются цепочки оконных процедур, каждая из которых вызывает предыдущую.
1.1.7. Создание окон средствами VCL
Теперь поговорим о том, как в VCL создаются окна. Речь здесь будет идти не о написании кода для создания окна с помощью VCL (предполагается, что читатель это и так знает), а о том, какие функции API и в какой момент вызывает VCL при создании окна.
Если смотреть код методов класса TWinControl
, которые вызываются при создании и отображении окна, то найти там то место, когда окно создается, удается не сразу. На первый взгляд все выглядит так, будто этот код вообще не имеет отношения к созданию окна, как будто оно создается где-то совсем в другом месте, а TWinControl
получает уже готовый дескриптор. На самом деле окно создает, конечно же, сам TWinControl
, а спрятано его создание в свойстве Handle
. Метод GetHandle
, который возвращает значение свойства Handle
, выглядит следующим образом (листинг 1.8).
Листинг 1.8. Реализация метода TWinControl.GetHandle
procedure TWinControl.HandleNeeded;
begin
if FHandle = 0 then
begin
if Parent <> nil then Parent.HandleNeeded;
CreateHandle;
end;
end;
function TWinControl.GetHandle: HWnd;
begin
HandleNeeded;
Result := FHandle;
end;
При каждом обращении к свойству Handle
вызывается метод HandleNeeded
, который проверяет, создано ли уже окно, и если нет, создает его, попутно создавая, при необходимости, родительское окно. Таким образом, окно создается при первом обращении к свойству Handle
.
Метод CreateHandle
, который вызывается из HandleNeeded
, выполняет непосредственно лишь несколько вспомогательных операций, а для создания окна вызывает еще один метод — CreateWnd
(листинг 1.9).
Листинг 1.9. Реализация метода CreateWnd
procedure TWndControl.CreateWnd;
var
Params: TCreateParams;
TempClass: TWndClass;
ClassRegistered: Boolean;
begin
CreateParams(Params);
with Params do
begin
if (WndParent = 0) end (Style and WS_CHILD <> 0) then
if (Owner <> nil) end (csReading in Owner.ComponentState) and (Owner is TWinControl) then
WndParent TWinControl(Owner).Handle
else
raise EInvalidOperation.CreateFmt(SParentRequired, [Name]);
FDefWndProc := WindowClass.lpfnWndProc;
ClassRegistered := GetClassInfo(WindowClass.hInstance, WinClassName, TempClass);
if not ClassRegistered or (TempClass.lpfnWndProc <> @InitWndProc) then
begin
if (ClassRegistered then
Windows.UnregisterClass(WinClassName, WindowClass.hInstance);
WindowClass.lpfnWndProc := InitWndProc;
WindowClass.lpszClassName := WinClassName;
if Windows.RegisterClass(WindowClass) = 0 then RaiseLastOSError;
end;
CreationControl := Self;
CreateWindowHandle(Params);
if FHandle = 0 then RaiseLastOSError;
if (GetWindowLong(FHandle, GWL_STYLE) and WS_CHILD <> 0) and (GetWindowLong(FHandle, GWL_ID) = 0) then
SetWindowLong(FHandle, GWL_ID, FHandle);
end;
StrDispose(FText);
FText := nil;
UpdateBounds;
Perform(WM_SETFONT, FFont.Handle, 1);
if AutoSize then AdjustSize;
end;
Собственно создание окна опять происходит не здесь, а в методе CreateWindowHandle
, который очень прост: он состоит из одного только вызова API-функции CreateWindowEx
с параметрами, значения которых берутся из полей записи Params
типа TCreateParams
(листинг 1.10)
Читать дальше
Конец ознакомительного отрывка
Купить книгу