Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   OOP (https://www.clubdelphi.com/foros/forumdisplay.php?f=5)
-   -   Docking y borrado de contenido ActiveX (SftTree) (https://www.clubdelphi.com/foros/showthread.php?t=78065)

prietor 16-03-2012 16:52:53

Docking y borrado de contenido ActiveX (SftTree)
 
Hola,

Estoy trasteando con el docking nativo en Delphi 2006 y me he topado con un problema serio: Tengo un TForm dockado inicialmente en un TPanel que contiene un árbol cargado con varios items (control ActiveX SftTree). Al hacer un drag & dock del TForm (bien en otro TPanel, bien como ventana flotante), se disparan automáticamente eventos de borrado de items en el ActiveX quedando el árbol vacío de datos.

El efecto parece producirse con el cambio de Parent del TForm que se está desdockando/dockando. Este comportamiento no ocurre con componentes de la VCL: si pongo una TListBox al ladito cargada con items en el mismo TForm y hago el drag & dock, los items de la lista no se borran!.

Da la sensación de que el cambio de Parent del TForm solo produce un redibujado sobre componentes VCL hijos y, sin embargo, se carga el contenido del resto de componentes no VCL.

No he llegado a encontrar ningún workaround... Alguno os habéis encontrado con esto? Alguna propuesta de solución/workaround?

Os dejo una traza del disparo del borrado de un item del árbol (ActiveX SftTree) al hacer un undock.

TFVentanaSftTree.TForm2.m_arbolItemDeleted($1420700,???)
:00487f3d TOleControl.InvokeEvent + $B1
:00486ba9 TEventDispatch.Invoke + $3D
:4f329dd5 ; C:\Windows\system32\SftTree_IX86_U_70.ocx
:4f361027 ; C:\Windows\system32\SftTree_IX86_U_70.ocx
:4f35f1d5 ; C:\Windows\system32\SftTree_IX86_U_70.ocx
:4f33d8fe ; C:\Windows\system32\SftTree_IX86_U_70.ocx
:4f38c888 ; C:\Windows\system32\SftTree_IX86_U_70.ocx
:4f3e2999 ; C:\Windows\system32\SftTree_IX86_U_70.ocx
:4f3e0980 ; C:\Windows\system32\SftTree_IX86_U_70.ocx
:7722c4e7 ; C:\Windows\system32\USER32.dll
:7722c5e7 ; C:\Windows\system32\USER32.dll
:77221b31 ; C:\Windows\system32\USER32.dll
:77221b57 USER32.CallWindowProcW + 0x1b
:4f35ca47 ; C:\Windows\system32\SftTree_IX86_U_70.ocx
:4f35ca0c ; C:\Windows\system32\SftTree_IX86_U_70.ocx
:4f35d5a0 ; C:\Windows\system32\SftTree_IX86_U_70.ocx
:4f348010 ; C:\Windows\system32\SftTree_IX86_U_70.ocx
:4f3b5566 ; C:\Windows\system32\SftTree_IX86_U_70.ocx
:4f3635d9 ; C:\Windows\system32\SftTree_IX86_U_70.ocx
:4f3bec80 ; C:\Windows\system32\SftTree_IX86_U_70.ocx
:7722c4e7 ; C:\Windows\system32\USER32.dll
:7722c5e7 ; C:\Windows\system32\USER32.dll
:77224f0e USER32.GetScrollBarInfo + 0xfd
:77224f7d ; C:\Windows\system32\USER32.dll
:779e6fee ntdll.KiUserCallbackDispatcher + 0x2e
:4f3baf45 ; C:\Windows\system32\SftTree_IX86_U_70.ocx
:4f3b77b0 ; C:\Windows\system32\SftTree_IX86_U_70.ocx
:00487774 TOleControl.DestroyWindowHandle + $38
:00478e1c TCustomForm.DefineProperties
:01463480
:0046872a TWinControl.RemoveControl + $4A

prietor 20-03-2012 12:50:54

Después de mucho buscar, he encontrado un workaround relativamente limpio basado en controlar el destroy y create del handler de la ventana del TOleControl embebido para cambiarlo de Parent a nivel de handler de Windows. Al parecer la VCL no está diseñada para tratar correctamente el ciclo de destroy-create con dockables. Es un error de diseño de la VCL...

La solución más sencilla pasa por tocar el wrapper TLB tras importar el ActiveX. Para mi caso concreto, que utilizo un TSftTree embebido en un TForm dockable:

Código Delphi [-]
...
TSftTree = class(TOleControl)
  private
    ...
    ActualHandle: HWND;
  protected
    ...
    procedure DestroyWnd; override;
    procedure CreateWnd; override;
 public
...
implementation
uses ComObj, Controls, Forms;
...
procedure TSftTree.DestroyWnd;
begin
  if (csDestroying in ComponentState) then
    inherited//TOleControl::DestroyWnd();
  else
  begin
    //Parent to the Application window which is 0x0 in size
    windows.SetParent(WindowHandle,Forms.Application.Handle);
    //save the WindowHandle
    ActualHandle := WindowHandle;
    //set it to 0 so Createwnd will be called again...
    WindowHandle := 0;
  end;
end;

procedure TSftTree.CreateWnd;
begin
  if (ActualHandle <> 0) then
  begin
    if (IsWindow(ActualHandle)) then
    begin
      WindowHandle := ActualHandle;
      ActualHandle := 0;
      windows.SetParent( WindowHandle, TWinControl(Self).Parent.Handle );
      //Force a resize on the client window
      MoveWindow( WindowHandle, 0, 0, TWinControl(Self).Parent.Width,TWinControl(Self).Parent.Height,true );
      //quick exit because there is NO need to create the window
      exit;
    end
  end;
  inherited;
end;
...

Un saludo!


La franja horaria es GMT +2. Ahora son las 21:12:56.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi