Ver Mensaje Individual
  #2  
Antiguo 05-12-2004
Avatar de roman
roman roman is offline
Moderador
 
Registrado: may 2003
Ubicación: Ciudad de México
Posts: 20.269
Reputación: 10
roman Es un diamante en brutoroman Es un diamante en brutoroman Es un diamante en bruto
¡Uf! Esta estuvo difícil

Vamos a ver primero porque no funciona tal como está.

Cuando el estilo de un formulario es fsMDIChild, su posición (Left, Top) está dada en relación al área cliente del formulario principal (el área no ocupada por los bordes, la barra de menú, barras de herramientas, etc), mientras que con los otros estilos la posición está dada en relación a la pantalla.

Entonces, si un formulario MDIChild está, por decir algo, en la posición (0, 0), es decir pegada a los bordes izquierdo y superior del formulario principal, al cambiarle el estilo, sus coordenadas siguen siendo (0, 0) pero ahora con relación a la pantalla lo que ocasiona que el formulario se mueva hasta la esquina superior izquierda de la pantalla, fuera del área cliente del formulario principal, razón por la cual hay que reposicionarla.

Ahora bien, LockWindowUpdate inhibe el redibujado de la ventana cuyo identificador se pasa como parámetro, en este caso el formulario principal, pero dado que el formulario hijo se movió fuera del área cliente, nada está impidiendo su redibujado y de ahí el 'pantallazo'.

La primera opción que viene a la cabeza entonces es aplicar el bloqueo a toda la pantalla:

LockWindowUpdate(GetDesktopHandle);

Pero esto, además de ser una solución chapucera, tiende a provocar un parpadeo en los iconos del escritorio.

Pues bueno, luego de zambullirme un rato en la VCL encontré lo que parece ser una solución aceptable: impedir nosotros mismos que se muestre la ventana durante el proceso de cambio de estilo (que en el fondo implica la destrucción y recreación de la ventana) y mostrarla una vez que la hayamos movido y redimensionado (esto último porque al cambiar al estilo fsMDIChild el formulario también cambia de tamaño).

Suponiendo que TChildForm es el formulario MDIChild, pon su declaración así:

Código Delphi [-]
TChildForm = class(TForm)
private
  ChangingStyle: Boolean;
  procedure CMShowingChanged(var Msg: TMessage); message CM_SHOWINGCHANGED;

public
  procedure ChangeStyle(NewStyle: FormStyle);
end;

El mensaje CM_SHOWINGCHANGED lo manda la VCL cuando ha cambiado la visibilidad de la ventana y es quien se encarga de llamar a la API de Window para mostrar el formulario. La variable ChangingStyle la usaremos entonces como bandera para impedir que se muestre la ventana cuando estamos cambiando el estilo.

La implementación queda así:

Código Delphi [-]
procedure TChildForm.CMShowingChanged(var Msg: TMessage);
begin
  if not ChangingStyle then
    inherited;
end;

procedure TChildForm.ChangeStyle(NewStyle: TFormStyle);
var
  Base: TPoint;
  Size: TPoint;

begin
  Base := Point(Left, Top);
  Size := Point(Width, Height);

  if NewStyle = fsMdiChild
    then Windows.ScreenToClient(Application.MainForm.ClientHandle, Base)
    else Windows.ClientToScreen(Application.MainForm.ClientHandle, Base);

  ChangingStyle := true;
  FormStyle := NewStyle;
  ChangingStyle := false;

  SetBounds(Base.X, Base.Y, Size.X, Size.Y);
  BringToFront;
  ShowWindow(Handle, SW_SHOW);
end;

Base y Size nos sirven para guardar la posición y tamaño de la ventana antes de hacer el cambio de estilo y usamos las funciones ClientToScreen y ScreenToClient para la reposición de la ventana en lugar de hacer chapucerías sumando y restando cosas raras.

Y ya con esto, en el evento OnClick de tu CheckBox pondrías

Código Delphi [-]
if (Sender as TCheckBox).Checked
  then ChangeStyle(fsMDiChild)
  else ChangeStyle(fsStayOnTop);

Hasta donde lo he probado funciona bien pero faltará que lo pruebes tú a ver si te sirve.

// Saludos

Última edición por roman fecha: 05-12-2004 a las 09:40:07.
Responder Con Cita