Ver Mensaje Individual
  #9  
Antiguo 14-05-2005
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
Hola,

Ya entiendo lo que quieres hacer y por qué. No estoy seguro de que sea la mejor forma. Quizá podrías dejar un Memo o algo por el estilo fijo dentro de tu formulario principal para mostrar los mensajes pues creo que tantas ventanas apareciendo de improviso pueden resultar disturbantes para el usuario. El memo lo irías actualizando conforme se generen nuevos avisos.

De cualquier forma estuve haciendo varias pruebas y creo que he encontrado la solución. Sé dónde está el problema aunque no me queda muy claro el porqué.

Vamos a fijar ideas. Llamemos MenuForm al formulario menú, LoginForm al formulario del login y AvisosForm al formulario de avisos.

En MenuForm hay dos timer, TimerMenu y TimerAvisos para controlar cuándo se muestran los respectivos formularios.

Según entiendo, AvisosForm sólo debe mostrarse si LoginForm no está activado ya que primero debe hacerse el login, y LoginForm debe cerrar el de avisos antes de mostrarse.

Entonces, un primer intento sería:

Código Delphi [-]
procedure TimerLoginTimer(Sender: TObject);
begin
  if not LoginForm.Visible then
  begin
    AvisosForm.Close;
    LoginForm.ShowModal;
  end;
end;

procedure TimerAvisosTimer(Sender: TObject);
begin
  if not AvisosForm.Visible and not LoginForm.Visible then
    AvisosForm.ShowModal;
end;

Es decir, LoginForm se muestra sólo si no está ya visible y en tal caso cierra AvisosForm (no hace falta verificar si AvisosForm está abierto ya que no vamos a destruir ninguna ventana, sólo ocultarlas, de manera que Close no afecta en caso de que AvisosForm estuviera ya cerrada).

Y AvisosForm se muestra sólo si no está ya abierto y si en esos momentos no está abierto LoginForm.

Tal como está el código me parece que no presenta errores ya que las verificaciones de visibilidad evitan que se use ShowModal sobre un formulario ya abierto.

Sin embargo, como ya lo notaste tú, AvisosForm.Close no parece funcionar, el formulario no se cierra sino hasta después de que se cierra LoginForm.

Dada la naturaleza de la aplicación que describes, supongo que es deseable o indispensable que AvisosForm se cierre cuando el sistema se suspenda por falta de actividad y se muestre LoginForm.

¿Cuál es el problema?

La llamada a AvisosForm.Close sí funciona pero en forma "retardada" y sólo tiene efecto hasta que se cierra LoginForm. Esta es la parte que no entiendo del todo pero tiene que ver con la forma en que se procesan los mensajes cuando se muestra una ventana modal.

Normalmente, toda la aplicación se ejecuta dentro de un bucle:


Código:
repeat
  PeekMessage;
  ProcessMessage;
until Application.Terminate;
Como quizá sepas, en Windows todo funciona mediante mensajes que se mandan a las aplicaciones. Toda actividad de teclado y ratón se manda en forma de mensajes a las aplicaciones y toda aplicación Windows consta de un blucle similar al anterior:

toma mensaje -> procesa mensaje

Así hasta que termine la aplicación.

Pero en Delphi, el comportamiento modal de una ventana se logra haciendo que la ventana misma maneje la cola de mensajes. El método ShowModal básicamente reintroduce un bucle como el de arriba hasta que el valor de ModalResult sea distinto de 0.

Es decir, la ventana modal inhibe el flujo normal de los mensajes; en lugar de que éstos sean procesados por el bucle principal de la aplicación, ella misma los procesa.

Entonces, en

Código Delphi [-]
procedure TimerLoginTimer(Sender: TObject);
begin
  if not LoginForm.Visible then
  begin
    AvisosForm.Close;
    LoginForm.ShowModal;
  end;
end;

la llamada a ShowModal impide que se procesen todos los mensajes que normalmente culminan en el cierre de AvisosForm y sólo se procesan hasta que LoginForm se cierra y se devuelve el conrol al bucle de la aplicación.

Era de esperarse entonces que una llamada intermedia a Application.ProcessMessages:

Código Delphi [-]
procedure TimerLoginTimer(Sender: TObject);
begin
  if not LoginForm.Visible then
  begin
    AvisosForm.Close;

    Application.ProcessMessages;

    LoginForm.ShowModal;
  end;
end;

hubiera bastado ya que ProcessMessages procesa todos los mensajes pendientes y no devuelve el control hasta que termina con todos.

Pero no. El formulario sigue sin cerrarse.

Esto es lo que no me queda claro. Pero puede solucionarse "retardando" la llamada a LoginForm.ShowModal.

Para ello deberás mandar un mesaje personalizado al formulario:

Código Delphi [-]
procedure TimerLoginTimer(Sender: TObject);
begin
  if not LoginForm.Visible then
  begin
    AvisosForm.Close;
    PostMessage(Handle, CM_CLOSEAVISOS, 0, 0);
  end;
end;

y postergar la llamada a LoginForm.ShowModal en el manejador del mensaje:

Código Delphi [-]
const
  // mensaje personalizado
  CM_CLOSEAVISOS = WM_USER + 100;

type
  TMenuForm = class(TForm)
  private
    // manejador del mensaje CM_CLOSEAVISOS
    procedure CMCloseAvisos(var Msg: TMessage); message CM_CLOSEAVISOS;
  end;

implementation

procedure TMenuForm.CMCloseAvisos(var Msg: TMessage);
begin
  LoginForm.ShowModal;
end;

¡Y listo! Ahora parece funcionar todo correctamente. La ventana de avisos se cierra cuando se muestra la del Login.

Sugerencia:

Los timer más que manejar el tiempo en que se muestran las ventanas, deberían simplemente tomar nota de cuánto tiempo ha pasado desde la última vez que se abrió una de ellas llevando la cuenta de este tiempo aparte y reinicializándolo cada vez que se cierren. De lo contrario, el tiempo en que se muestran es confuso. Por ejemplo, dependiendo de qué tanto tiempo se haya mantenido abierta LoginForm pudiera ser que al cerrarla el timer la volviera a mostrar inmediatamente.

// Saludos
Responder Con Cita