Конференция "Компоненты" » Как обойти "control '' has no parent window"?
 
  • Krants © (11.06.07 12:06) [0]
    При использовании в крите обращения к хэндлу компонента:
    inherited Create(AOwner);
    DragAcceptFiles(Handle,True);


    возникает ошибка "control '' has no parent window"
    Как понимаю, она возникает из-за того, что Handle компонента еще не задан и процедура DragAcceptFiles прерывает проц. Create.
    Есть ли какие-нибудь методы обхода данной проблемки?
  • Юрий Зотов © (11.06.07 12:37) [1]
    В общем случае, при выполнении конструктора окно еще не создано и, соответственно, свойство Handle еще не проинициализировано. Это произойдет после выполнения метода CreateWnd.

    У некоторых контролов окно создается при первом обращении к Handle (даже если оно происходит и в конструкторе), но не у всех это так.

    Отсюда вывод - надо заместить метод CreateWnd и код, которому нужен хэндл, перенести в него ПОСЛЕ вызова Inherited. Тогда всегда будем иметь правильный хэндл, даже и при пересоздании окна.
  • Krants © (11.06.07 13:05) [2]

    > Юрий Зотов ©   (11.06.07 12:37) [1]


    Спасибо, разобрался, только теперь при закрытии программы возникает такая ошибка: "control 'ControlFiles1' has no parent window". Код дестуктора:

    destructor TControlFiles.Destroy;
    begin
     DragAcceptFiles(Handle,False); //ошибка возникает при данной процедуре
     inherited Destroy;
    end;

    procedure TControlFiles.CreateWnd;
    begin
     inherited;
     DragAcceptFiles(Handle,True); //а сюда ссылается дебугэр при ошибке
    end;



    Убрав DragAcceptFiles(Handle,False) работает нормально, но все же желательно оставить. Да и разобратся почему возникает ошибка в CreateWnd при вызове destructor'а?!
  • Krants © (11.06.07 13:27) [3]
    Вообщем так работает:
    procedure TControlFiles.DestroyWnd;
    begin
     inherited;
     DragAcceptFiles(Handle,False);
    end;


    еще раз спасибо.
  • Юрий Зотов © (11.06.07 14:19) [4]
    > Krants

    Аналогично, когда отрабатывает деструктор, то окно УЖЕ уничтожено и поэтому обращение к Handle дает ошибку.

    Перекрыть DestroyWnd - правильное решение, но строчки в нем, видимо, надо поменять местами. По логике, надо СНАЧАЛА разрегистрировать окно, как приемник операции "дрыг-н-прыг", а уж ПОТОМ вызывать inherited, чтобы это окно уничтожить.
  • Однокамушкин (12.06.07 13:54) [5]

    > Юрий Зотов ©   (11.06.07 12:37) [1]
    > У некоторых контролов окно создается при первом обращении
    > к Handle (даже если оно происходит и в конструкторе), но
    > не у всех это так.

    Не согласен, у всех оконных контролов создание окна происходит именно при обращении к Handle (точнее - при вызове HandleNeeded, который имеется в GetHandle), просто те окна, которые имеют стиль WS_CHILD, обязаны иметь окно-родителя, поэтому, если в момент обращения к Handle родитель ещё не назначен, VCL не может создать окно и выкидывает исключение... Нет таких контролов, у которых обращение к Handle допустимо после создания окна, но есть те, у которых оно допустимо только после назначения свойства Parent...

    > Юрий Зотов ©   (11.06.07 14:19) [4]
    > Аналогично, когда отрабатывает деструктор, то окно УЖЕ уничтожено
    > и поэтому обращение к Handle дает ошибку.

    Аналогично, ошибка возникает не из-за самого обращения к Handle, а из-за того, что HandleNeeded вызывает CreateHandle, а тот опять вызывает CreateWnd, а CreateWnd обламывается с повторным созданием окна, потому что там WS_CHILD, а Parent на этот момент уже установлен в nil деструктором формы, который уже убрал этот контрол из списка своих контролов... А если удалить контрол, имеющий в своём деструкторе обращение к Handle, не в момент закрытия формы, а, например, при нажатии конпки, то в этом случае исключения не произойдёт, т.к. у контрола будет до последнего и Parent, да и само уничтожение хэндла произойдёт только в унаследованном деструкторе, т.е. до вызова inherited обращение к Handle ошибок не даст...

    > Krants ©   (11.06.07 13:27) [3]
    > Вообщем так работает:
    > procedure TControlFiles.DestroyWnd;
    > begin
    >  inherited;
    >  DragAcceptFiles(Handle,False);
    > end;

    Бессмысленная конструкция... DestroyWnd уничтожает хэндл, поэтому обращения к Handle после inherited приводят к созданию нового окна, и в DragAcceptFiles передаётся совсем другой дескриптор...
  • homm © (12.06.07 21:17) [6]
    > Однокамушкин

    +1
    Все четко и по теме. Мне понравилось. :)
  • Rouse_ © (13.06.07 21:21) [7]

    > поэтому, если в момент обращения к Handle родитель ещё не
    > назначен, VCL не может создать окно и выкидывает исключение.

    Чушь... Наличие родителя в данном случае синефиолетово. Проверяется наличие владельца. Если он присутствует - то происходит пострекурсивный вызов HandleNeeded.
  • DimaBr © (14.06.07 08:48) [8]
    Щас обсудим все внутренности VCL подетально !!! :)))
  • Однокамушкин (16.06.07 20:39) [9]

    >
    > Rouse_ ©   (13.06.07 21:21) [7]
    > Чушь... Наличие родителя в данном случае синефиолетово.
    > Проверяется наличие владельца. Если он присутствует - то
    > происходит пострекурсивный вызов HandleNeeded.

    Нет, не чушь... вот код из метода TWinControl.CreateWnd (Delphi 7):

     CreateParams(Params);
     with Params do
     begin
       if (WndParent = 0) and (Style and WS_CHILD <> 0) then
         if (Owner <> nil) and (csReading in Owner.ComponentState) and
           (Owner is TWinControl) then
           WndParent := TWinControl(Owner).Handle
         else
           raise EInvalidOperation.CreateFmt(SParentRequired, [Name]);



    В CreateParams поле Params.WndParent назначается в соответствии со значением свойства Parent, Owner там никак не учитывается, а в CreateWnd Owner используется только тогда, когда компонент читается из потока - видимо, в качестве временной меры, этакой заглушки при инициализации формы, пока Parent не назначен... а если, например, создавать компонент в рантайме, то эта ветка не сработвает, и как раз наличие владельца будет синефиолетово, в отличие от родителя, который проверяется всегда при флаге WS_CHILD...
  • Rouse_ © (18.06.07 09:42) [10]

    > в качестве временной меры, этакой заглушки при инициализации
    > формы, пока Parent не назначен...

    Ну так сдесь же речь идет о компоненте по всей видимости загружаещемся из DFM в рантайме... При чем тут остальные случаи?
  • Однокамушкин (18.06.07 10:09) [11]

    > Rouse_ ©   (18.06.07 09:42) [10]
    > Ну так сдесь же речь идет о компоненте по всей видимости
    > загружаещемся из DFM в рантайме...

    Интересно, как он вообще в DFM попадёт, если исключение, о котором пишет автор, возникает в момент помещения компонента на форму? Специально для проверки создал наследника TCustomControl, который обращается к Handle в своём конструкторе, так при попытке кинуть его на форму возникает эта ошибка, и на форме ничего не остаётся, так что до загрузки компонента из DFM в рантайме дело просто не доходит...
  • Rouse_ © (18.06.07 11:45) [12]
    Хм... да. Не внимательно прочитал :)
 
Конференция "Компоненты" » Как обойти "control '' has no parent window"?
Есть новые Нет новых   [134427   +34][b:0][p:0.001]