15 junio 2007

Minimizar en la bandeja del sistema

Una de las características mas utilizadas en los programas P2P es la de minimizar nuestra aplicación en la bandeja del sistema (al lado del reloj de Windows en la barra de tareas).

Voy a mostraros como modificar el formulario principal de vuestra aplicación para que se minimize en la bandeja del sistema y una vez minimizado cuando se pulse sobre el icono se restaure. También vamos a añadir la posibilidad de pulsar dicho icono con el botón derecho del ratón y que muestre un menu contextual (popup) con la opción Mostrar.

Lo primero de todo es añadir un menu contextual a nuestro formulario principal (PopupMenu) con el nombre MenuBandeja. Añadimos una sola opción llamada Mostrar. A continuación añadimos en la sección uses del formulario principal la unidad ShellAPI:

uses
Windows, Messages, ...., ShellAPI;

Después en la sección private insertamos la variable:

IconData: TNotifyIconData;

En la misma sección private añadimos los procedimientos:

procedure WMSysCommand( var Msg: TWMSysCommand ); message WM_SYSCOMMAND;
procedure Restaurar( var Msg: TMessage ); message WM_USER+1;

Cuya implementación sería la siguiente:

procedure TFPrincipal.WMSysCommand( var Msg: TWMSysCommand );
begin
if Msg.CmdType = SC_MINIMIZE then
Minimizar
else
DefaultHandler( Msg );
end;

procedure TFPrincipal.Restaurar( var Msg: TMessage );
var p: TPoint;
begin
// ¿Ha pulsado el botón izquierdo del ratón?
if Msg.lParam = WM_LBUTTONDOWN then
MostrarClick( Self );

// ¿Ha pulsado en la bandeja del sistema con el botón derecho del ratón?
if Msg.lParam = WM_RBUTTONDOWN then
begin
SetForegroundWindow( Handle );
GetCursorPos( p );
MenuBandeja.Popup( p.x, p.y );
PostMessage( Handle, WM_NULL, 0, 0 );
end;
end;

El procedimiento WMSysCommand es el encargado de interceptar los mensajes del sistema que manda Windows a nuestra aplicación. En el caso de que el mensaje enviado sea SC_MINIMIZE minimizamos la ventana en la bandeja del sistema. Si es otro mensaje dejamos que Windows lo maneje (DefaultHandler).

El procedimiento Restaurar comprueba si ha pulsado el botón izquierdo del ratón sobre el icono de la bandeja del sistema para volver a mostrar nuestra ventana. Si pulsa el botón derecho llamará a nuestro menu contextual MenuBandeja.

Ahora creamos el procedimiento encargado de minimizar la ventana:

procedure TFPrincipal.Minimizar;
begin
with IconData do
begin
cbSize := sizeof( IconData );
Wnd := Handle;
uID := 100;
uFlags := NIF_MESSAGE + NIF_ICON + NIF_TIP;
uCallbackMessage := WM_USER + 1;

// Usamos de icono el mismo de la aplicación
hIcon := Application.Icon.Handle;

// Como Hint del icono, el nombre de la aplicación
StrPCopy( szTip, Application.Title );
end;

// Ponemos el icono al lado del reloj
Shell_NotifyIcon( NIM_ADD, @IconData );

// Ocultamos el formulario
Hide;
end;

Y por último el evento al pulsar la opción Mostrar en el menú contextual:

procedure TFPrincipal.MostrarClick( Sender: TObject );
begin
// Volvemos a mostrar de nuevo el formulario
FPrincipal.Show;
ShowWindow( Application.Handle, SW_SHOW );

// Eliminamos el icono de la bandeja del sistema
Shell_NotifyIcon( NIM_DELETE, @IconData );
IconData.Wnd := 0;
end;

Aunque pueda parecer algo engorroso creo que es mas limpio que tener que instalar componentes para que realicen esto. Al fin y al cabo sólo hay que hacerlo sólo en el formulario principal.

Pruebas realizadas en Delphi 7.

2 comentarios:

Jose Aantonio dijo...

hola he tratado de utilizar el codigo pero me pierdo me podrias ayudar? hago lo que dices al pie de la letra pero cuando pongo

En la misma sección private añadimos los procedimientos:

procedure WMSysCommand( var Msg: TWMSysCommand ); message WM_SYSCOMMAND;
procedure Restaurar( var Msg: TMessage ); message WM_USER+1;
ahi me marca un error unsatisfied forward or external declaration :'tprincipal.wmsyscommand'

y de ahi no se donde colocar esto
" Cuya implementación sería la siguiente:

procedure TFPrincipal.WMSysCommand( var Msg: TWMSysCommand );
begin
if Msg.CmdType = SC_MINIMIZE then
Minimizar
else
DefaultHandler( Msg );
end; "

si en la seccion private o en la seccion de implementation en los dos me da error que hago con esto y lo que resta del codigo espero respuesta gracias!!!

Administrador dijo...

A ver si es porque en vez de llamar al formulario principal FPrincipal lo has llamado Principal:

'tprincipal.wmsyscommand'

Debería quejarse diciendo:

'tfprincipal.wmsyscommand'

¿Puede ser eso?

Publicidad