Tray icons and Borderstyle set to bsnone.

Hi.

I have 2 buttons in a form.
- 1st button make the form show only important info and because i want to make it very small i need
   to set BorderStyle=bsNone.
- 2nd button make the form normal again.

BUT my program tray icon disappears.
How can i prevent that ??

Thanks for your help.
LVL 16
CodedKAsked:
Who is Participating?
 
ZhaawZConnect With a Mentor Software DeveloperCommented:
mmm.. I had a look at Forms.pas and Controls.pas to find out why does tray icon disappear after changing border..

// here's what is called when you change BorderStyle property
procedure TCustomForm.SetBorderStyle(Value: TFormBorderStyle);
begin
  if FBorderStyle <> Value then
  begin
    FBorderStyle := Value;
    AutoScroll := FBorderStyle in [bsSizeable, bsSizeToolWin];
    if not (csDesigning in ComponentState) then RecreateWnd;
  end;
end;

// TWinControl is "parent class" of TCustomForm, and it has RecreateWnd procedure that's called in prev. procedure
procedure TWinControl.RecreateWnd;
begin
  if FHandle <> 0 then Perform(CM_RECREATEWND, 0, 0);
end;

// here we can see which procedure is called when TWinControl receives CM_RECREATEWND message
procedure CMRecreateWnd(var Message: TMessage); message CM_RECREATEWND;

// here's this procedure
procedure TWinControl.CMRecreateWnd(var Message: TMessage);
var
  WasFocused: Boolean;
begin
  WasFocused := Focused;
  DestroyHandle;
  UpdateControlState;
  if WasFocused and (FHandle <> 0) then Windows.SetFocus(FHandle);
end;

// DestroyHandle procedure destroys all child controls and also itself
// UpdateControlState calls UpdateShowing which creates form.. actually i'm sick of all those procedure-in-procedure calls :D


So.. when you change border style by setting value to BorderStyle property, application completely recreates a form (I'm not sure about what it does with child controls). This means that handle of a form also is changed. Here's small test:
[source]
  ShowMessage(IntToStr(Handle));
  BorderStyle := bsNone;
  ShowMessage(IntToStr(Handle));
[/source]
Both values should be (and was, when I tested it) different. Tray icon is attached to a window:
[source]
With F1Tray do
   begin
    cbSize:=SizeOf(F1Tray);
    Wnd:=Handle;  // <--- here you specify which window is 'owner' of tray icon
[/source]
So when window is recreated (i.e., when you set value to BorderStyle property), owner of icon is destroyed, so icon also is destroyed.


To avoid such problems, do one of following:
1) destroy tray icons before setting BorderStyle property, then change BorderStyle, then recreate tray icons
2) change border by using SetWindowLong() function - this will not destroy anything and it will not recreate anything, so this is much better (faster, cleaner) way than using default Delphi stuff
0
 
LMuadDIbCommented:
well, your trayicon shouldnt disappear

whose trayicon component are you using?
Is it your own code? If it is, can you paste code here?
0
 
CodedKAuthor Commented:
Yes ... its not a component.
Here is the code :


unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ShellApi, ImgList, StdCtrls, XPMan;

const
  WM_ICONTRAY=WM_USER+1;

type
  TForm1 = class(TForm)
    ImageList1: TImageList;
    Button1: TButton;
    Button2: TButton;
    XPManifest1: TXPManifest;
    Button3: TButton;
    procedure WMNCHitTest(var Msg: TWMNCHitTest);
     message WM_NCHITTEST;
    procedure FormTray(var Msg:TMessage);
     message WM_ICONTRAY;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure Button3Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    F1Tray:TNotifyIconData;
    F1Icon:TIcon;
    F2Tray:TNotifyIconData;
    F2Icon:TIcon;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.WMNCHitTest(var Msg: TWMNCHitTest);
begin
inherited;
if (Msg.Result = HTCLIENT)and(ControlAtPos(ScreenToClient(SmallPointToPoint
(TWMNCHitTest(Msg).Pos)), False)= nil) then Msg.Result := HTCAPTION;
end;

procedure TForm1.FormTray(var Msg:TMessage);
begin
 case Msg.lParam of  WM_LBUTTONDOWN:
 Form1.Show;
 end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
   With F1Tray do
   begin
    cbSize:=SizeOf(F1Tray);
    Wnd:=Handle;
    uID:=0;
    uFlags:=NIF_MESSAGE+NIF_ICON+NIF_TIP;
    uCallbackMessage:=WM_ICONTRAY;
    F1Icon:=TIcon.Create;
    ImageList1.GetIcon(0,F1Icon);
    hIcon:=F1Icon.Handle;
    StrPCopy(szTip,'This is F1 ');
   end;
   Shell_NotifyIcon(NIM_ADD,@F1Tray);

   With F2Tray do
   begin
    cbSize:=SizeOf(F2Tray);
    Wnd:=Handle;
    uID:=1;
    uFlags:=NIF_MESSAGE+NIF_ICON+NIF_TIP;
    uCallbackMessage:=WM_ICONTRAY;
    F2Icon:=TIcon.Create;
    ImageList1.GetIcon(0,F2Icon);
    hIcon:=F2Icon.Handle;
    StrPCopy(szTip,'This is F2');
   end;
   Shell_NotifyIcon(NIM_ADD,@F2Tray);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
Form1.Height:=25;
Form1.BorderStyle:=bsNone;   //---- PROBLEM
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
Form1.Height:=160;
Form1.BorderStyle:=bsToolWindow;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  Shell_NotifyIcon(NIM_DELETE,@F1Tray);
  Shell_NotifyIcon(NIM_DELETE,@F2Tray);
  F1Icon.Free;
  F2Icon.Free;
end;

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
CanClose:=False;
Form1.Hide;
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
Application.Terminate;
end;

end.
0
Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

 
ZhaawZSoftware DeveloperCommented:
I'm not sure why do they disappear when you change borders by using default BorderStyle property, but here's a WinAPI alternative that does border changing without that 'bug':

procedure TForm1.Button1Click(Sender: TObject);
begin
Form1.Height:=25;
//Form1.BorderStyle:=bsNone;
SetWindowLong(handle, gwl_style, GetWindowLong(handle, gwl_style) and not (ws_caption or ws_sizebox)); // remove both 'blue border' and 'resizing border'
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
Form1.Height:=160;
//Form1.BorderStyle:=bsToolWindow;
SetWindowLong(handle, gwl_exstyle, GetWindowLong(handle, gwl_exstyle) or ws_ex_toolwindow); // set form-type to toolwindow
SetWindowLong(handle, gwl_style, GetWindowLong(handle, gwl_style) or ws_caption or ws_sizebox); // add 'blue border' and 'resizing border'
SetWindowPos(handle, 0, 0, 0, 0, 0, swp_nozorder or swp_nomove or swp_nosize or swp_drawframe); // redraw new border
end;
0
 
CodedKAuthor Commented:
Thank you very much :)

But i dont want a form that can resize...
Can you please tell me the command ?

Thanks :)
0
 
ZhaawZSoftware DeveloperCommented:
take a look at:
SetWindowLong(handle, gwl_style, GetWindowLong(handle, gwl_style) or ws_caption or ws_sizebox); // add 'blue border' and 'resizing border'
ws_sizebox style - that's the resizable border. change it to:
SetWindowLong(handle, gwl_style, GetWindowLong(handle, gwl_style) or ws_caption);
0
 
CodedKAuthor Commented:
:) :) :) :) :) :)
Thanks :)
0
 
ZhaawZSoftware DeveloperCommented:
Glad to help :)
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.