Ver Mensaje Individual
  #2  
Antiguo 27-10-2006
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
He estado probando tu código y no termina de convencerme. El botón en la barra de tareas se comporta de forma extraña:

- nunca queda presionado cuando la aplicación está activa
- al minimizar, el formulario principal se iconiza como si se tratase de un formulario secundario
- si un formulario secundario se activa, al presionar el botón de la barra, vuelve a activarse el principal, lo que no debe ocurrir.

Según leí alguna vez, la razón por la que Delphi no usa un menú "estandar" en el botón de la barra de tareas es porque dicho menú corresponde al menú de sistema, no del formulario principal, sino de la ventana de la aplicación, que es una ventana oculta. Los comandos minimizar y cerrar se aplican a toda la aplicación: minimizar oculta todos los formularios activos y cerrar cierra la aplicación. Pero un comando como maximizar es distinto pues no se maximiza una aplicación, sino uno de sus formularios, y lo mismo el comando mover.

El punto es: ¿a qué ventana se tendrían que aplicar esas acciones? ¿a la principal? ¿a la activa?

La razón quizá no es muy convincente pues pudieron optar por aplicarlos a una de estas dos ventanas, tal como lo hace tu código. Pero el problema, hasta donde he visto, es que el comportamiento es extraño.

Haciendo pruebas, creo que he logrado algo añadiendo items directamente al menú de sistema de la aplicación, por ejemplo:

Código Delphi [-]
procedure AddSysItem(Handle: HWND; Command: Integer; Caption: String);
var
  SysMenu: HMENU;
  Item: TMenuItemInfo;

begin
  SysMenu := GetSystemMenu(Handle, false);

  Item.cbSize := SizeOf(Item);
  Item.fMask := MIIM_FTYPE or MIIM_STRING or MIIM_ID;
  Item.fType := MFT_STRING;
  Item.dwTypeData := PChar(Caption);
  Item.wID := Command;

  Windows.InsertMenuItem(SysMenu, 1, true, Item);
end;

Esta función inserta un item en el menú de sistema de la ventana identificada por Handle. El parámetro Command es el identificador del comando a ejecutar, que corresponde al parámetro WPARAM del mensaje WM_SYSCOMMAND. No sirve que pasemos directamente un comando como SC_MOVE, precisamente porque la ventana de la aplicación no es la que deseamos mover, sino, por ejemplo, la ventana principal. Entonces debemos pasar un comando personalizado:

Código Delphi [-]
const
  // alguna costante que no entre en conflicto con otros comandos
  // para que sea compatible con otros comandos, hay que recorrerlo 4 bits
  SC_MENEAR = 84 shl 4;

...

AddSysItem(Application.Handle, SC_MENEAR, 'Menear');

Para captar este comando podemos usar el evento OnMessage del objeto Application o de un objeto ApplicationEvents, da lo mismo:

Código Delphi [-]
procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean);
begin
  if (Msg.message = WM_SYSCOMMAND) then
    case Msg.wParam and $FFF0 of
      SC_MENEAR:
        Application.MainForm.Perform(WM_SYSCOMMAND, SC_MOVE, 0);
    end;
end;

Esto es, traspasamos el comando personalizado SC_MENEAR que llega a la aplicación, al formulario principal como SC_MOVE.

Con esto básicamente funciona, pero debemos cuidar otros detalles como el habilitar o inhabilitar el item cuando sea necesario. El comando SC_MENEAR debe inhabiltarse si la aplicación se minimiza o si el formulario principal se maximiza. Y debe habilitarse de vuelta, cuando se restaure la aplicación o el formulario principal.

Para empezar, complementamos el evento OnMessage dela aplicación:

Código Delphi [-]
procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean);
begin
  if (Msg.message = WM_SYSCOMMAND) then
    case Msg.wParam and $FFF0 of
      SC_MENEAR:
        Application.MainForm.Perform(WM_SYSCOMMAND, SC_MOVE, 0);

      SC_MINIMIZE:
        // inhabilitar el item cuando se minimiza la aplicación
        EnableSysItem(Application.Handle, SC_MENEAR, false);

      SC_RESTORE:
        // al restaurar la aplicación, rehabilitamos el item pero sólo si la ventana
        // principal no está maximizada
        if Application.MainForm.WindowState <> wsMaximized then
          EnableSysItem(Application.Handle, SC_MENEAR, true);
    end;
end;

donde EnableSysItem es:

Código Delphi [-]
procedure EnableSysItem(Handle: HWND; Command: Integer; Enable: Boolean);
const
   Flags: array[Boolean] of Integer = (MF_GRAYED, MF_ENABLED);

var
  SysMenu: HMENU;

begin
  SysMenu := GetSystemMenu(Handle, false);
  Windows.EnableMenuItem(SysMenu, Command, MF_BYCOMMAND or Flags[Enable]);
end;

Pero también hay que actuar cuando se maximice o restaure el formulario principal. Para ello podemos crear un manejador para el mensaje WM_SYSCOMMAND del formulario:

Código Delphi [-]
type
  TForm1 = class(TForm)
  private
    procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
  end;

implementation

procedure TForm1.WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
begin
  inherited;

  case Msg.CmdType and $FFF0 of
    SC_RESTORE: EnableSysItem(Application.Handle, SC_MENEAR, true);
    SC_MAXIMIZE: EnableSysItem(Application.Handle, SC_MENEAR, false);
  end;
end;

Adjunto un ejemplo ya hecho al cual le he añadido el comando SC_ESTRUJAR para cambiar de tamaño el formulario principal. Quedaría agregar el comando SC_INFLAR para maximizarlo. El ejecutable que está en el zip sólo correrá si se tiene Delphi 7 instalado.

// Saludos
Archivos Adjuntos
Tipo de Archivo: zip AppSysMenu.zip (8,8 KB, 49 visitas)
Responder Con Cita