Элемент управления "ххх" не имеет родительского окна
Я пытался написать библиотеку DLL в Delphi с функцией, которая создает экземпляр потомка TFrame и возвращает его. Но когда я импортировал эту функцию в приложение, каждый раз, когда я вызывал ее, я получал исключение типа "элемент управления" xxx "не имеет родительского окна". Я не уверен на 100%, но в конструкторе этого класса появилось исключение при обращении к любому из элементов управления GUI.
Не могли бы вы сказать мне, в чем причина такого поведения? Должен ли я просто использовать потомков TForm или есть лучшее решение?
Спасибо!
6 ответов
Об ошибке
Это сообщение об ошибке выдается из модуля Controls.pas, из TWinControl.CreateWnd
метод. По сути, этот код используется для создания дескриптора Window для вашего потомка TWinControl (TFrame, TButton, TEdit... если он может иметь фокус клавиатуры, это потомок TWinControl), и на самом деле это очень разумное сообщение об ошибке: вы не можете иметь Window без WindowParent, и, поскольку мы говорим о VCL, имеет смысл попытаться получить дескриптор родительского окна из TWinControl.Parent; И это не назначено.
Это не ПОЧЕМУ появляется сообщение об ошибке. Вы видите это сообщение об ошибке, потому что для некоторого кода, который вы используете для настройки фрейма, для какой-либо операции требуется дескриптор Window. Это может быть что угодно, например, установка Заголовка некоторого компонента (который внутренне требует, чтобы дескриптор окна выполнял некоторые вычисления). Лично я действительно ненавижу, когда это происходит. Когда я создаю GUI из кода, я пытаюсь максимально задержать назначение Parent, пытаясь задержать создание окна, поэтому меня это укусило много раз.
В зависимости от использования вашей DLL, возможно исправить
Я собираюсь надеть свою шляпу читателя психического разума. Так как вам нужно вернуть FRAME из вашей DLL, и вы не можете вернуть фактический Frame, потому что это специфичный для Delphi объект, и вам не разрешено возвращать специфичные для Delphi объекты через границы DLL, я предполагаю, что вы возвращаете дескриптор окна, как и все хорошие API, использует определение функции следующим образом:
function GiveMeTheNiceFrame:HWND;
Проблема в том, что эта подпрограмма требует создания фактического дескриптора окна путем вызова TWinControl.CreateWnd
и, в свою очередь, этот вызов требует дескриптор родительского окна, чтобы установить вызов Windows.CreateWindowEx
и подпрограмма не может получить дескриптор родительского окна, поэтому она выдает ошибки.
Попробуйте заменить свою функцию чем-то вроде строки:
function GiveMeTheNiceFrame(OwnerWindow:HWND):HWND;
begin
Result := TMyNiceFrame.CreateParanted(OwnerWindow).Handle;
end;
... т.е. использовать CreateParented(AParentWindow:HWND)
конструктор, а не обычный Create(AOwner:TComponent)
и передать владельца HWND в вашу DLL.
Есть несколько важных вещей, которые нужно запомнить:
- При использовании DLL и ваша DLL, и ваша EXE имеют экземпляр Application, который борется за контроль. Элементы управления в вашей DLL будут видеть экземпляр приложения, который принадлежит к DLL; Элементы управления в вашем EXE увидят экземпляр приложения, который принадлежит EXE. Этой борьбы не возникает при использовании пакетов, так как тогда будет только один экземпляр приложения.
- Кадры - это элементы управления, но они не являются формами.
- При использовании элементов управления в приложении они не могут визуально существовать без родительского элемента управления (обычно это форма или контейнер, который имеет родительскую иерархию по отношению к форме).
- Некоторые элементы управления не могут раскрыть свою полную функциональность, если они не существуют визуально и не имеют допустимого родителя.
Попробуйте воспроизвести вашу проблему внутри EXE; если вы не можете воспроизвести, вероятно, это первое, что есть в приведенном выше списке.
--jeroen
Вы можете избежать этого сообщения, назначив nil родительскому событию OnClose, иногда это работает:
SomeControl.Parent := nil;//Before free your TControl
SomeControl.Free;
Я думаю, что это очень крутое решение. Я думаю, что это не пытались раньше:) Я использую Dummy Parent (который является формой).
function MyFrame_Create(hApplication, hwndParent:THandle; X, Y, W, H:Integer):Pointer; stdcall;
var Fr: TMyFrame;
F: TForm;
CurAppHandle: THandle;
begin
CurAppHandle:=Application.Handle;
Application.Handle:=hApplication;
//---
F:=TForm. Create(Application);//Create a dummy form
F.Position:=poDesigned;
F.Width:=0; F.Top:=0; F.Left:=-400; F.Top:=-400;//Hide Form
F.Visible:=True;
//---
Fr:=TMyFrame.Create(Application);
Fr.Parent:=F;//Set Frame's parent
//Fr.ParentWindow:=hwndParent;
Windows.SetParent(Fr.Handle, hwndParent);//Set Frame's parent window
if CurAppHandle>0 then Application.Handle:=CurAppHandle;
//---
Fr.Left:=X;
Fr.Top:=Y;
Fr.Width:=W;
Fr.Height:=H;
Result:=Fr;
end;//MyFrame_Create
procedure MyFrame_Destroy(_Fr:Pointer); stdcall;
var Fr: TMyFrame;
F: TObject;
begin
Fr:=_Fr;
F:=Fr.Parent;
Fr.Parent:=Nil;
if (F is TForm) then F.Free;
//SetParent(Fr.Handle, 0);
//Fr.ParentWindow:=0;
Fr.Free;
end;//MyFrame_Destroy
Похоже, вам просто нужно назначить компонент (форму или часть формы, например панель), который удерживает кадр, в theframe.parent.
Вы не можете выполнять работу с графическим интерфейсом, пока он не назначен. Кадры являются частями форм для повторного использования, и обычно им необходимо назначить некоторый родительский элемент.
Переместите код GUI в onshow или процедуру, которую вы вызываете явно, чтобы вызывающий код мог назначить parent.
Или сделайте родителя параметром в функции.
Я нашел это (CreateParams вызывается как часть CreateWnd):
procedure TCustomFrame.CreateParams(var Params: TCreateParams);
begin
inherited;
if Parent = nil then
Params.WndParent := Application.Handle;
end;
И Application.Handle = 0, поэтому он всегда выдает ошибку позже в CreateWnd.
После прочтения этого Delphi: Как вызвать унаследованного наследуемого предка на виртуальный метод?
Я решил это путем переопределения CreateParams в моем кадре, чтобы пропустить версию tCustomFrame:
type
tCreateParamsMethod = procedure(var Params: TCreateParams) of object;
type
tMyScrollingWinControl = class(TScrollingWinControl);
procedure TDelphiFrame.CreateParams(var Params: TCreateParams);
var
Proc: tCreateParamsMethod;
begin
TMethod(Proc).Code := @TMyScrollingWinControl.CreateParams;
TMethod(Proc).Data := Self;
Proc(Params);
end;
Теперь он просто выдает ошибки при попытке установить фокус на субконтролях, что, я думаю, я исправлю, перехватив WM_FOCUS, но мы узнаем, как это будет происходить.
function CreateFrame(hwndParent: HWnd): HWnd; stdcall;
var
frame: tFrame;
begin
Result := 0;
try
frame := TDelphiFrame.CreateParented(hwndParent);
Result := frame.Handle;
except on e: Exception do
ShowMessage(e.Message);
end;
end;