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;
Theexstyle 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.
Duoas
Postaholic
2,043 posts since Oct 2007
Reputation Points: 1,140
Solved Threads: 229
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...
Duoas
Postaholic
2,043 posts since Oct 2007
Reputation Points: 1,140
Solved Threads: 229
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 usestMainForm.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.
Duoas
Postaholic
2,043 posts since Oct 2007
Reputation Points: 1,140
Solved Threads: 229
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.
Duoas
Postaholic
2,043 posts since Oct 2007
Reputation Points: 1,140
Solved Threads: 229
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.
Duoas
Postaholic
2,043 posts since Oct 2007
Reputation Points: 1,140
Solved Threads: 229