windows - How to correctly have modeless form appear in taskbar -


i trying achieve age-old delphi dream of having modeless form appear in taskbar.

what correct way have modeless form appear in taskbar?


research effort

these attempts solve problem. there lot of things needed make behave correctly - having button appear on taskbar not solution. having windows application behave correctly windows application should goal.

for know me, , how deep "shows research effort" goes, hang on because wild ride down rabbit hole.

the question in title, above horizontal line above. below serves show why on oft-repeated suggestions incorrect.

windows creates taskbar button unowned windows

initially have "main form", show other modeless form:

procedure tfrmmain.button2click(sender: tobject); begin     if frmmodeless = nil         application.createform(tfrmmodeless, frmmodeless);      frmmodeless.show; end; 

this correctly shows new form, no new button appears on taskbar:

enter image description here

the reason no taskbar button created because design. windows show taskbar button window "unowned". modeless delphi form owned. in case owned application.handle:

enter image description here

my project's name modelessformfail.dpr, origin of windows class name modelessformfail associated owner.

fortunately there way force windows create taskbar button window, though window owned:

just use ws_ex_appwindow

the msdn documentation of ws_ex_appwindow says it:

ws_ex_appwindow 0x00040000l forces top-level window onto taskbar when window visible.

it well-known delphi trick override createparams , manually add ws_ex_appwindow style:

procedure tfrmmodeless.createparams(var params: tcreateparams); begin     inherited;      params.exstyle := params.exstyle or ws_ex_appwindow; //force owned window appear in taskbar end; 

when run this, newly created modeless form does indeed own taskbar button:

enter image description here

and we're done? no, because doesn't behave correctly.

if user clicks on frmmain taskbar button, window not brought forward. instead other form (frmmodeless) brought forward:

enter image description here

this makes sense once understand windows concept of ownership. windows will, design, bring child owned forms forward. entire purpose of ownership - keep owned forms on top of owners.

make form unowned

the solution, as of know not fight against taskbar heuristics , windows. if want form unowned, make unowned.

this (fairly) simple. in createparam force owner windows null:

procedure tfrmmodeless.createparams(var params: tcreateparams); begin     inherited;      //doesn't work, because form still owned //  params.exstyle := params.exstyle or ws_ex_appwindow; //force owned windows appear in taskbar      //make form unonwed; it's want     params.wndparent := 0; //unowned. unowned windows naturally appear on taskbar.           //there may way simulate popupparent , popupmode. end; 

as aside, wanted investigate there way use popupmode , popupparent properties make window unowned. swear read comment (from david) somewhere on saying if passed self popupparent, e.g.:

procedure tfrmmain.button1click(sender: tobject); begin     if frmmodeless = nil     begin         application.createform(tfrmmodeless, frmmodeless);         frmmodeless.popupparent := frmmodeless; //the super-secret way "unowned"? swear david heffernan mentioned somewhere on so, damned if can find now.         frmmodeless.popupmode := pmexplicit; //happens automatically when set popupparent, idea     end;      frmmodeless.show; end; 

it supposed super-secret way indicate delphi want form have "no owner". cannot find comment anywhere on now. unfortunately, no combination of popupparent , popupmode cause form actually un-owned:

  • popupmode: pmnone
    • owner hwnd: application.handle/application.mainform.handle
  • popupmode: pmauto
    • owner hwnd: screen.activeform.handle
  • popupmode: pmexplicit
    • popupparent: nil
      • owner hwnd: application.mainform.handle
    • popupparent: aform
      • owner hwnd: aform.handle
    • popupparent: self
      • owner hwnd: application.mainform.handle

nothing could cause form have no owner (each time checking spy++).

setting wndparent manually during createparams:

  • does make form unowned
  • it does have taskbar button
  • and both taskbar buttons dobehave correctly:

enter image description here

and we're done, right? thought so. changed use new technique.

except there problems fix seem cause other problems - delphi didn't me changing ownership of form.

hint windows

one of controls on modeless window has tooltop:

enter image description here

the problem when tooltip window appears, causes other form (frmmain, modal one) come forward. doesn't gain activation focus; obscure form at:

enter image description here

the reason logical. delphi hintwindow owned either application.handle or application.mainform.handle, rather being owned form should owned by:

enter image description here

i have considered bug on delphi's part; using wrong owner.

diversion see actual app layout

now it's important take moment show application isn't main form , modeless form:

enter image description here

it's actually:

  • a login screen (a sacrificial main form gets hidden)
  • a main screen
  • a modal control panel
  • that shows modeless form

enter image description here

even reality of application layout, except hint window ownership works. there 2 taskbar buttons, , clicking them brings proper form forward:

enter image description here

but still have problem of hintwindow ownership bringing wrong form forward:

enter image description here

showmainformontaskbar

it when attempting create minimal application reproduce problem when realize couldn't. there different:

  • between delphi 5 application ported xe6
  • a new application created in xe6

after comparing everything, traced down fact new applications in xe6 add mainformontaskbar := true default in new project (presumably not break existing applications):

program modelessformfail; //... begin   application.initialize;   application.mainformontaskbar := true;   application.createform(tfrmsacrificialmain, frmsacrificialmain);   //application.createform(tfrmmain, frmmain);   application.run; end. 

when added option, appearance of tooltip didn't bring wrong form forward!:

enter image description here

success! except, people know what's coming know what's coming. "sacrificial" main login form shows "real" main form, hiding itself:

procedure tfrmsacrificialmain.button1click(sender: tobject); var     frmmain: tfrmmain; begin     frmmain := tfrmmain.create(application);     self.hide;     try         frmmain.showmodal;             self.show;     end; end; 

when happens, , "login", taskbar icon disappers entirely:

enter image description here

this happens because:

  • the un-owned sacrificial main form not invisible: button goes it
  • the real main form owned not toolbar button

use ws_app_appwindow

now have opportunity use ws_ex_appwindow. want force main form, owned, appear on taskbar. override createparams , force appear on taskbar:

procedure tfrmmain.createparams(var params: tcreateparams); begin     inherited;      params.exstyle := params.exstyle or ws_ex_appwindow; //force owned window appear in taskbar end; 

and give whirl:

enter image description here

looking pretty good!

  • two taskbar buttons
  • the tooltip doesn't pop wrong owner form forward

except, when click on first toolbar button, wrong form comes up. shows modal frmmain, rather modal frmcontrolpanel:

enter image description here

presumably because newly created frmcontrolpanel popupparented application.mainform rather screen.activeform. check in spy++:

enter image description here

yes, parent mainform.handle. turns out because of bug in vcl. if form's popupmode is:

  • pmauto
  • pmnone (if it's modal form)

the vcl attempts use application.activeformhandle hwndparent. unfortunately checks if modal form's parent enabled:

if (wndparent <> 0) , (       isiconic(wndparent) or        not iswindowvisible(wndparent) or       not iswindowenabled(wndparent)) 

of course modal form's parent not enabled. if was, not modal form. vcl falls using:

wndparent := application.mainformhandle; 

manual parenting

this means have sure manually(?) set popup parenting?

procedure tfrmmain.button2click(sender: tobject); var     frmcontrolpanel: tfrmcontrolpanel; begin     frmcontrolpanel := tfrmcontrolpanel.create(application);     try         frmcontrolpanel.popupparent := self;         frmcontrolpanel.popupmode := pmexplicit; //automatically set pmexplicit when set popupparent. idea.         frmcontrolpanel.showmodal;             frmcontrolpanel.free;     end; end; 

except didn't work either. clicking first taskbar button causes wrong form activate:

enter image description here

at point i'm thoroughly confused. parent of modal form should frmmain, , is!:

enter image description here

so now?

i have sense of might going on.

that taskbar button representation of frmmain. windows bringing forward.

except behaved correctly when mainformontaskbar set false.

there must magic in delphi vcl caused correctness before, gets disabled mainformontaskbar := true, it?

i not first person want delphi application behave nicely windows 95 toolbar. , i've asked question in past, answers geared towards delphi 5 , it's old central routing window.

i've been told fixed around delphi 2007 timeframe.

so correct solution?

bonus reading

it seems me fundamental problem main form is, in eyes of vcl, not main form. once fix that, problems go away.

you should:

  1. call application.createform once, real main form. rule follow. consider job of application.createform to create main form of application.
  2. create login form , set wndparent 0. makes sure appears on taskbar. show modally.
  3. create main form in usual way calling application.createform.
  4. set mainformontaskbar true.
  5. set wndparent 0 modeless form.

and that's it. here's complete example:

project1.dpr

program project1;  uses   vcl.forms,   umain in 'umain.pas' {mainform},   ulogin in 'ulogin.pas' {loginform},   umodeless in 'umodeless.pas' {modelessform};  {$r *.res}  begin   application.initialize;   application.showhint := true;   application.mainformontaskbar := true;   tloginform.create(application) begin     showmodal;     free;   end;   application.createform(tmainform, mainform);   application.run; end. 

ulogin.pas

unit ulogin;  interface  uses   winapi.windows, winapi.messages, system.sysutils, system.variants, system.classes, vcl.graphics,   vcl.controls, vcl.forms, vcl.dialogs;  type   tloginform = class(tform)   protected     procedure createparams(var params: tcreateparams); override;   end;  implementation  {$r *.dfm}  procedure tloginform.createparams(var params: tcreateparams); begin   inherited;   params.wndparent := 0; end;  end. 

ulogin.dfm

object loginform: tloginform   left = 0   top = 0   caption = 'loginform'   clientheight = 300   clientwidth = 635   color = clbtnface   font.charset = default_charset   font.color = clwindowtext   font.height = -11   font.name = 'ms sans serif'   font.style = []   oldcreateorder = false   pixelsperinch = 96   textheight = 13 end 

umain.pas

unit umain;  interface  uses   winapi.windows, winapi.messages, system.sysutils, system.variants, system.classes, vcl.graphics,   vcl.controls, vcl.forms, vcl.dialogs, vcl.stdctrls, umodeless;  type   tmainform = class(tform)     button1: tbutton;     procedure button1click(sender: tobject);   end;  var   mainform: tmainform;  implementation  {$r *.dfm}  procedure tmainform.button1click(sender: tobject); begin   tmodelessform.create(self) begin     show;   end; end;  end. 

umain.dfm

object mainform: tmainform   left = 0   top = 0   caption = 'mainform'   clientheight = 300   clientwidth = 635   color = clbtnface   font.charset = default_charset   font.color = clwindowtext   font.height = -11   font.name = 'ms sans serif'   font.style = []   oldcreateorder = false   pixelsperinch = 96   textheight = 13   object button1: tbutton     left = 288     top = 160     width = 75     height = 23     caption = 'button1'     taborder = 0     onclick = button1click   end end 

umodeless.pas

unit umodeless;  interface  uses   winapi.windows, winapi.messages, system.sysutils, system.variants, system.classes, vcl.graphics,   vcl.controls, vcl.forms, vcl.dialogs, vcl.stdctrls;  type   tmodelessform = class(tform)     label1: tlabel;   protected     procedure createparams(var params: tcreateparams); override;   end;  implementation  {$r *.dfm}  procedure tmodelessform.createparams(var params: tcreateparams); begin   inherited;   params.wndparent := 0; end;  end. 

umodeless.dfm

object modelessform: tmodelessform   left = 0   top = 0   caption = 'modelessform'   clientheight = 300   clientwidth = 635   color = clbtnface   font.charset = default_charset   font.color = clwindowtext   font.height = -11   font.name = 'ms sans serif'   font.style = []   oldcreateorder = false   showhint = true   pixelsperinch = 96   textheight = 13   object label1: tlabel     left = 312     top = 160     width = 98     height = 13     hint = 'this hint'     caption = 'i'#39'm label hint'   end end 

if you'd rather modeless form owned main form, can achieve replacing tmodelessform.createparams with:

procedure tmodelessform.createparams(var params: tcreateparams); begin   inherited;   params.exstyle := params.exstyle or ws_ex_appwindow; end; 

Comments

Popular posts from this blog

facebook - android ACTION_SEND to share with specific application only -

python - Creating a new virtualenv gives a permissions error -

javascript - cocos2d-js draw circle not instantly -