Ver Mensaje Individual
  #4  
Antiguo 04-03-2008
Jose_Pérez Jose_Pérez is offline
Miembro
 
Registrado: may 2003
Posts: 156
Reputación: 22
Jose_Pérez Va por buen camino
Hola de nuevo:

Después de darles muchas vueltas finalmente he conseguido resolver el problema. Voy a intentar explicar la solución lo mejor que pueda, porque la verdad es que hay que saber primero como se comporta el mensaje WM_MOVE para comprender la solución; finalmente he decidido capturar este mensaje y no WM_WINDOWPOSCHANGING propuesto por Lepe, ya que pensaba (sin haberlo probado a fondo) que a efectos prácticos era lo mismo uno que otro.

El problema estaba en que cada componente (TComponenteA y TComponenteB) respondían a su correspondiente mensaje WM_MOVE sin tener en cuenta la respuesta del otro. De ese modo cuando, en tiempo de diseño, seleccionaba y arrastraba los dos componente a la vez se liaban los cálculos.

Para comprobar el orden de llamada incluí varios ShowMessage en el código. Así pude comprobar, para mi sorpresa, que el modo en que se sucedían los mensajes WM_MOVE no era el que yo esperaba. Este es el orden (recuerden siempre que estamos hablando de tiempo de diseño y que estamos arrastrando los dos componentes a la vez):

1.- Se ejecuta la respuesta al mensaje WM_MOVE en TComponenteA. Aquí se modifican las propiedades Left y Top de TComponenteB.

2.- Primera sorpresa: se ejecuta dos veces la respuesta al mensaje WM_MOVE en TComponenteB: uno para Left (desplazamiento horizontal) y otro para Top (desplazamiento vertical).

3.- Segunda sorpresa: Se ejecuta una tercera respuesta al mensaje WM_MOVE en TComponenteB, que correspondería al momento en que se suelta el componente en el form. Para empeorar las cosas, en este momento el propio Delphi suma (o resta) las propiedades Top y Left del componente según el desplazamiento.

Vamos, un lio.

El principal problema era saber si el evento correspondía al punto 2 o al 3, sin necesidad de que los componentes se comunicaran a través de eventos o propiedades. Al final utilicé la función GetTickCount, del API, para medir el tiempo en milésimas de segundo entre una llamada y otra. ¡Ojo, hay que calcular un tiempo para Left y otro para Top, porque no siempre el desplazamiento es en diagonal! Tras hacer pruebas, vi que la diferencia de tiempo entre el punto 2 y 3 era 0.

Y este es el resultado...

Código Delphi [-]
  TComponenteA = class(TPadre)
  private
    FTop: Integer;
    FLeft: Integer;
    FTickCountTop: Cardinal;
    FTickCountLeft: Cardinal;
    procedure WMMove(var Msg: TMessage); message WM_MOVE;
    function GetComponenteB: TComponent;
  protected
    { Protected declarations }
  public
    Constructor Create(AOwner: TComponent); Override;
  published
    {Published declarations}
  end;

Constructor TComponenteA.Create(AOwner: TComponent);
Begin

  Inherited Create(AOwner);

  FTop:=Top;
  FLeft:=Left;

  FTickCountTop:=GetTickCount;
  FTickCountLeft:=FTickCountTop;

End;

procedure TComponenteA.WMMove(var Msg: TMessage);
var
  MoveTickCountTop: Cardinal;
  MoveTickCountLeft: Cardinal;
  DiferenciaTickCount: Cardinal;
begin

  inherited;

  If GetComponenteB=Nil Then
     begin
     FTop:=Top;
     FLeft:=Left;
     exit;
     end;

  If (FTop<>Top) Then
     begin

     MoveTickCountTop:=GetTickCount;
     DiferenciaTickCount:=MoveTickCountTop-FTickCountTop;
     FTickCountTop:=MoveTickCountTop;

     If DiferenciaTickCount=0 Then Top:=FTop;

     (GetComponenteB As TComponenteB).Top:=Top;

     end;

  If (FLeft<>Left) Then
     begin

     MoveTickCountLeft:=GetTickCount;
     DiferenciaTickCount:=MoveTickCountLeft-FTickCountLeft;
     FTickCountLeft:=MoveTickCountLeft;

     If DiferenciaTickCount=0 Then Left:=FLeft;

     (GetComponenteB As TComponenteB).Left:=Left+((GetComponenteB As TComponenteB).Width-1);

     end;

  FTop:=Top;
  FLeft:=Left;

end;

Function TComponenteA.GetComponenteB: TComponent;
var
  Padre: TWinControl;
begin

  Padre:=Parent;
  While Not (Padre is TForm) Do
        Padre:=Padre.Parent;

  // FNombreComponenteB sería un string con el nombre del componente.
  // En este ejemplo no está dfeclarado. El resto se los dejo a ustedes  

  If Padre.FindComponent(FNombreComponenteB)<>Nil Then
     Result:=Padre.FindComponent(FNombreComponenteB)
     else
     Result:=Nil;

end;

Última edición por Jose_Pérez fecha: 17-03-2008 a las 12:33:30.
Responder Con Cita