Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Principal > API de Windows
Registrarse FAQ Miembros Calendario Guía de estilo Buscar Temas de Hoy Marcar Foros Como Leídos

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 03-12-2004
zerelho zerelho is offline
Miembro
 
Registrado: mar 2004
Posts: 28
Poder: 0
zerelho Va por buen camino
Hacer una ventana MDI siempre delante (always on top)

Hola a todos, os cuento el problema que tengo:

Estoy haciendo una aplicación MDI y necesito que las ventanas hijas tengan la posibilidad de estar siempre delante (la típica opción de "Always on Top").

Para ésto lo que estoy haciendo es cambiar el FormStyle de las ventanas hijas de "fsMDIChild" a "fsStayOnTop" y viceversa, lo que pasa es que al hacer los cambios no me coloca la ficha en el mismo lugar por lo que almaceno la posición actual y despues la recupero (de forma bastante chapucera) pero el problema es que se ven los cambios de posición y me salen unos "pantallazos" bastante feos.

Corregi el problema a medias minimizando y restaurando la ventana, pero aun así no queda muy bien.

¿Alguien sabe otra forma mas correcta y eficiente de hacer esto si que se note el cambio de FormStyle?

Este es el codigo del metodo donde realizo el cambio:

Código:
	  
Izq:=Self.Left;
Arriba:=Self.Top;
{**}
// esto lo quité del libro de "La Cara Oculta de Delphi": 
// Anula la actualización de la ventana en el monitor, para evitar el parpadeo
LockWindowUpdate(Self.Handle); 
try
	WindowState:=wsMinimized;
	{**}
	if SiempreVisible.Checked then 
		// Checkbox activado (de MDIChild a StayOnTop)
		Begin
		FormStyle:=fsStayOnTop;
		Self.Left:=(Izq+2);
		Self.Top:=Arriba+(FormPrincipal.Height-FormPrincipal.ClientHeight+23);
		End
	else 
		 // Checkbox desactivado (de StayOnTop a MDIChild)
		 Begin
		 FormStyle:=fsMDIChild;
		 Self.Left:=(Izq-2);
		 Self.Top:=Arriba-(FormPrincipal.Height-FormPrincipal.ClientHeight+23);
		 End;
	{**}
finally
	WindowState:=wsNormal;
	LockWindowUpdate(0);
	end;
No me pregunteis de donde viene el Top:=...+23 y el Left:=...+2 pero me posiciona la ventana el mismo lugar exacto... si alguien sabe por qué este valor?

Aunque sea otra duda diferente, que tipo de botón tengo que utilizar para que si hago click en él me quede como presionado y volviendo a hacer click vuelve a su posición habitual?

La idea sería cambiar el checkbox que utilizo por un botón de este tipo, para ponerle el típico icono con la agujita pinchada.
Responder Con Cita
  #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
Poder: 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 10:40:07.
Responder Con Cita
  #3  
Antiguo 06-12-2004
zerelho zerelho is offline
Miembro
 
Registrado: mar 2004
Posts: 28
Poder: 0
zerelho Va por buen camino
Thumbs up Perfecto!!!

dpm!!! Muchas gracias roman, Todo perfecto a la primera, un 10... la verdad es que me quitas de un apuro... aparte todo perfectamente claro y explicado.

Ya estuviera intentando cacharrear con el tema de las funciones ClientToScreen y ScreenToClient pero no daba hecho y lo de los mensajes a la VCL me sobrepasa completamente.

Ahora solo me queda lo del botoncillo "pulsado" pero bueno eso ya son temas menores... podría utilizar el componente JvSwitch de las JEDI pero me gustaría mas un botón pulsado, como podría hacerlo? existe ya un componente específico? (No se si puedo preguntar esto aquí o tengo que abrir otro post).

Otra vez muchas gracias y hasta otra.
Responder Con Cita
  #4  
Antiguo 06-12-2004
Avatar de roman
roman roman is offline
Moderador
 
Registrado: may 2003
Ubicación: Ciudad de México
Posts: 20.269
Poder: 10
roman Es un diamante en brutoroman Es un diamante en brutoroman Es un diamante en bruto
Lo del botón es sencillo: usa un SpeedButton (paleta Additional) con su propiedad GroupIndx = 1 y su propiedad AllowAllUp = true;

Lo del típico icono con la agujita pinchada será muy típico pero no sé a qué te refieres.

En cuanto a que los de los mensajes de la VCL te sobrepasa en realidad no es tan difícil. Es cuestión de armarse de paciencia y trazar la aplicación a través de la VCL. Para ello, en Project|Options|Compiler seleccionas la opción "Use Debug DCUs".

Lo que yo hice fue poner un punto de ruptura en la línea

FormStyle := fsStayOnTop

y a partir de ahí comencé a trazar el programa con F7. De lo primero que te das cuenta es de que el cambio de estilo provoca una reconstrucción de la ventana, es decir, primero debe destruirse y volverse a crear, así que en un momento dado durante el trazado desaparece la ventana.

Entonces busco si pasa por algún método que pueda redefinir para ahí poner Visible := false de manera que no se muestre en la nueva posición al momento de reconstruirse y la muestre yo mismo hasta que la cambie de lugar.

Desafortunadamente no hay tal método, mejor dicho sí lo hay pero ocurre antes de que cambie la propiedad FormStyle de manera que al poner Visible := true se genera el consabido error de que no se puede esconder una ventana MDIChild.

Entonces sigo trazando hasta ver aparecer de nuevo a la ventana y así me doy cuenta que ocurre justo después de la línea (en el código de la VCL):

Perform(CM_SHOWINGCHANGED, 0, 0);

Entonces es cuando hago el manejador de CM_SHOWINGCHANGED y veo que el inherited no hace gran cosa sino llamar a la función ShowWindow (o SetWindowPos, no recuerdo bien) de la API de Windows así que nada más había que deshabilitarla y ahora sí llamarla nosotros mismos cuando estuvieramos listos.

Te comento esto para que te des una idea de por donde buscar este tipo de cosas en un futuro.

// Saludos
Responder Con Cita
  #5  
Antiguo 08-12-2004
zerelho zerelho is offline
Miembro
 
Registrado: mar 2004
Posts: 28
Poder: 0
zerelho Va por buen camino
Gracias otra vez roman por las respuestas...
Lo de la agujita pinchada pues no es tan "típico" como pensaba, estoy buscando algun programilla donde la utilice (para coger prestado el icono ) y no lo doy encontrado y eso que estoy seguro de ver mogollon de aplicaciones que la utilizan para activar la opción de always on top.
Cuando lo encuentre pongo aqui unas capturas para que sepais de que hablo. Ta'luego.
Responder Con Cita
Respuesta


Herramientas Buscar en Tema
Buscar en Tema:

Búsqueda Avanzada
Desplegado

Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro


La franja horaria es GMT +2. Ahora son las 13:34:43.


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
Copyright 1996-2007 Club Delphi