I try to use trayicon in delphi 5, but I can't hide the main form completely as which contain a MDI child form. Any solution for it? :(

Recommended Answers

All 10 Replies

I've not messed with MDI forms much, but when you create your mainform you have to modify some of the application's system window properties.

procedure FormCreate(...);
  begin
  exstyle := getWindowLong( application.hanle, GWL_EXSTYLE )
  end;

procedure FormShow(...);
  begin
  setWindowLong( application.handle, GWL_EXSTYLE, exstyle )
  end;

procedure FormHide(...);
  begin
  setWindowLong( application.handle, GWL_EXSTYLE, WS_EX_TOOLWINDOW )
  end;

The exstyle should be a cardinal variable somewhere.
Now the window will not appear in the taskbar whenever it is hidden. If you never want it in the taskbar then forget the show and hide stuff and just set the exstyle to toolwindow in the create event.

Hope this helps.

Dear friend, thanks for reply! I've try your advice, but the result same.
After closed the main form, taskbar should be nothing only trayicon there. However the main form still in taskbar as which contain a MDI child form (I've tried to take out the child form then result OK :the taskbar show nothing ).

Any advice? :( :(

My FormClose coding about :

procedure TFrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caNone;
hide;
end;

I'm afraid I've never used the trayicon component, but a lot of these inject code into your application's or main form's constructor. Make sure that isn't happening.

If the problem is one of MDI then you'll have to wait until I mess around with it to figure out why...

(I use my Delphi 5 IDE for everything. Best IDE ever as far as I'm concerned...)

Give me a day or two and if you (or someone else) haven't figured it out by then I'll post a more comprehensive response.

Sorry I couldn't be of more help in the meantime...

Thank you so much! thanks for your willing help!! :D

Me again. I just made an MDI that lives in the System Tray without any trouble (~30 min). My only guess is that the trayicon component is doing something that is messing you up. So... here's what I can do for you.

How to put your application in the system tray using Delphi 5.

File: SystemTray.pas

unit SystemTray;
interface
uses Windows;

const
  WM_ICONTRAY = WM_USER +1;

  NIM_ADD     = 0;
  NIM_MODIFY  = 1;
  NIM_DELETE  = 2;

  NIF_MESSAGE = $00000001;
  NIF_ICON    = $00000002;
  NIF_TIP     = $00000004;
  NIF_STATE   = $00000008;
  NIF_INFO    = $00000010;
  NIF_GUID    = $00000020;

type
  tNotifyIconData = record
    size:            cardinal;
    wnd:             THANDLE;
    ID:              cardinal;
    flags:           cardinal;
    callbackMessage: cardinal;
    icon:            HICON;
    tip:             array[ 0..63 ] of char;
    end;

function Shell_NotifyIcon( message: cardinal; var pnid: tNotifyIconData ): BOOL;
  stdcall; external 'shell32.dll' name 'Shell_NotifyIcon';

implementation
end.

File: MAIN.pas

unit Main;
interface
uses
  ..., SystemTray;  // add the new unit to the end of your uses clause

type
  TMainForm = class(TForm)
    ...
  private
    // used when hiding the application from the taskbar
    f_ExStyle: cardinal;
    // used to add the application's icon to the system tray
    f_TrayIconData: tNotifyIconData;
    ...
  public
    procedure TrayMessage( var msg: tMessage ); message WM_ICONTRAY;
    ...
  end;

implementation

...

procedure TMainForm.FormCreate(Sender: TObject);
  begin
  // remember the proper display mode for the application
  f_ExStyle := getWindowLong( application.handle, GWL_EXSTYLE );

  // add the application icon to the system tray
  with f_TrayIconData do begin
    size            := sizeof( tNotifyIconData );
    wnd             := handle;
    ID              := 0;
    flags           := NIF_MESSAGE or NIF_ICON or NIF_TIP;
    callbackMessage := WM_ICONTRAY;
    icon            := application.icon.handle;
    strpcopy( tip, copy( application.title, 1, 63 ) )
    end;
  Shell_NotifyIcon( NIM_ADD, f_TrayIconData )
  end;

procedure TMainForm.FormDestroy(Sender: TObject);
  begin
  // remove the application icon from the system tray
  Shell_NotifyIcon( NIM_DELETE, f_TrayIconData )
  end;

procedure TMainForm.FormShow(Sender: TObject);
  begin
  // add the application to the taskbar if visible
  setWindowLong( application.handle, GWL_EXSTYLE, f_ExStyle )
  end;

procedure TMainForm.FormHide(Sender: TObject);
  begin
  // remove the application from the taskbar if hidden
  setWindowLong( application.handle, GWL_EXSTYLE, WS_EX_TOOLWINDOW )
  end;

procedure TMainForm.TrayMessage( var msg: tMessage );
  begin
  // show the form if the user left-clicks the tray icon
  if msg.lParam = WM_LBUTTONDOWN then show
  end;

...
end.

Now, a Delphi MDI application by default uses tMainForm.close to terminate, so if you want to capture the system menu close event (clicking the [X] button on the titlebar or chosing "Close" from the application's system menu or pressing Alt+F4) you'll have to account for actual attempts to terminate the application in the FormCanClose method. Personally, I like the way ZoneAlarm AntiVirus does it. If you click the [X] button it pops up a dialogue asking whether you really want to terminate or just to minimize to the system tray, and remembers the response. If you select minimize it gives you another dialogue with instructions on how to actually terminate the application.

Well, that's it.
Hope this helps.

Thanks friend.
As the example, I need to custom the trayicon component, right?

I'll try it!:idea:

What my application request is :
At the starting only tray icon , after click the tray icon, the form display, after press the [x] to close the form, the form hidden but tryicon still fuction, only right click the trayicon and select the "terminate" the application will be terminate completly.

But I tried quite a long time still can't make the application hide completely (still display in task bar) as MDI child form. :'(

Don't give up yet. I failed to account for the MDI child forms.

The icon/button you see on the taskbar actually belongs to the application, and not the main form. So if you have any windows other than the main form open (you can't hide a MDI child form) the application button will automagically appear on the taskbar.

The trick is to get rid of the application button and put the main form's button on the taskbar instead.

Like I said, I haven't played much with MDI applications before so I've had to learn some stuff to help. I'm cleaning up my code now. Give me another day or two and I'll post an example that does everything you want (and then some) with full source code you can scavenge as you like.

Thanks your encourage.:)

Here is my simple testing :


File: ChildForm.pas

unit ChildForm;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type
  TForm2 = class(TForm)
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.DFM}

end.

File: MainForm.pas

unit MainForm;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  TrayIcon, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    TrayNotifyIcon1: TTrayNotifyIcon;
    Timer1: TTimer;
    procedure Timer1Timer(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure TrayNotifyIcon1DblClick(Sender: TObject);
    procedure TrayNotifyIcon1Click(Sender: TObject);
  private
    { Private declarations }
    procedure createchildform;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation
 uses ChildForm;
{$R *.DFM}

procedure TForm1.createchildform ;
var tempChildForm : TForm2;
begin
try
  tempChildForm := TForm2.Create(Self);
  tempChildForm.FormStyle := fsMDIChild  ;
  

finally

end;
end ;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Timer1.Enabled := False;
  try
    createchildform ;
  Finally
  End ;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin

   TrayNotifyIcon1.IconVisible := True;

end;

procedure TForm1.TrayNotifyIcon1DblClick(Sender: TObject);
begin
 show ;
end;

procedure TForm1.TrayNotifyIcon1Click(Sender: TObject);
begin
 hide;
end;
end.

I'd set Application.ShowMainForm := False before application run.

Time enough has passed that I haven't answered here.

As I mentioned already, the reason you see a button on the taskbar is because the application puts it there. Your forms don't. You can see this by going to the main menu --> Project --> Options --> Application --> Title and giving your application a title that differs from the main form's caption. The button on the taskbar will show the application title, and not the mainform caption.

When you have MDI children, which cannot be hidden, the application button is restored to the taskbar because you technically still have windows showing... even if the MDI main form is hidden.

So, to get rid of it you have to turn it off explicitly. In MAIN.pas, make sure you have the following:

interface
type
  TMainForm = class(TForm)
    procedure ApplicationActivate(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private:
    procedure SysCommandMessage(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
  protected:
    procedure CreateParams(var Params: TCreateParams); override;
  end;

implementation

procedure TMainForm.ApplicationActivate( Sender: TObject );
  begin
  // Remove the application button from the taskbar
  // (and make sure it stays that way)
  ShowWindow( Application.Handle, SW_HIDE )
  end;

procedure TMainForm.FormCreate( Sender: TObject );
  begin
  // Keep the application button out of the taskbar
  application.OnActivate := ApplicationActivate
  end;

procedure TMainForm.SysCommandMessage(var Msg: TWMSysCommand);
  begin
  // Routing WM_SYSCOMMAND messages through here instead of the default
  // handler prevents Delphi from trying to create a taskbar button for the
  // application when minimized and maximized, which when combined with
  // ApplicationActivate would otherwise cause flicker.
  //
  // You could capture the SC_MINIMIZE command here and instead hide the
  // window, but you would have to make sure to let the user know that the
  // window was minimized to the system tray. Otherwise it will look like the
  // window just disappeared...
  //
  // You could also remove biMinimize and biMaximize from the mainForm's
  // BorderIcons, and trap the SC_MINIMIZE button to do nothing... For
  // example:
  //   if msg.CmdType <> SC_MINIMIZE then defaultHandler( msg )
  //
  defaultHandler(Msg)
  end;

procedure TMainForm.CreateParams(var Params: TCreateParams);
  begin
  // Add the main form's window button to the taskbar
  inherited CreateParams(Params);
  Params.ExStyle   := Params.ExStyle or WS_EX_APPWINDOW;
  Params.WndParent := GetDesktopWindow
  //  If I understand things correctly, if you comment out the last line then
  //  when you manipulate the window (say, restore it) other toplevel windows
  //  belonging to the application will also be affected (say, restored). But
  //  since this is an MDI application you shouldn't have any other toplevel
  //  windows... And I haven't played around with this any...
  end;

If you insert this code into your main form you will get the taskbar button response you want. Weird stuff, huh?

I'll post back in a few days more with a fairly interesting example (I've been having fun with it, but my simple object pascal syntax highlighter needs a little more work...) which comes with all its own source code and demonstrates all kinds of weird things, such as how to make an application mutex to prevent multiple instances of the application from running at the same time, tray icon popup menus and animations, using XP style controls if available, etc.

commented: Helpful +1

Yeah! Hide succesfull ~~ ^o^
Thanks & Thanks~~
if you are around I must treat you a lunch...haha
really help me a lot ~ :D

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.