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
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:
EnableSysItem(Application.Handle, SC_MENEAR, false);
SC_RESTORE:
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