Jeff Steinkamp 1 Posted October 10, 2023 I have a number of forms that are created from the main form, but those forms are created without an owner. But if those forms are showing and we minimize the main form, all of the forms, owned by the main form or not, get minimized. That certainly is not the behavior I would expect. These forms are not created at Applicaiton run time, but whenever the user clicks on the menu item for those forms. Here is a sample of the code: <code> begin dlg := TSelectContest.CreateNew(self, false); try if dlg.showmodal = mrok then begin tablename := dlg.tablename; TMyContest.Create(nil, tablename); end; finally dlg.Free end; </code> Tmycontest is the form that should remain visible. The form is actually shown in the forms create procedure after some other initialization. I have searched for a workaround on this but have not found anything remotely related. Is there some way to make this happen? Thanks Share this post Link to post
Kas Ob. 121 Posted October 11, 2023 This might help https://stackoverflow.com/questions/52470476/how-to-restore-a-minimized-modal-form And because i am not sure if i understand the problem exactly, i mean do you need a dedicated icon on the taskbar for these forms or not, but anyway. If you try this private procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND; .. procedure TMyContest.FormCreate(Sender: TObject); begin SetWindowLong(Handle, GWL_HWNDPARENT, 0); end; procedure TMyContest.WMSysCommand(var Msg: TWMSysCommand); begin case Msg.CmdType of SC_MINIMIZE: begin Exit; end; SC_MAXIMIZE: begin Exit; end; end; inherited; end; This will prevent MyContest form form minimizing and maximizing even if the border buttons are there, and even if your main is changing state. Share this post Link to post
PeterBelow 238 Posted October 11, 2023 11 hours ago, Jeff Steinkamp said: I have a number of forms that are created from the main form, but those forms are created without an owner. But if those forms are showing and we minimize the main form, all of the forms, owned by the main form or not, get minimized. That certainly is not the behavior I would expect. These forms are not created at Applicaiton run time, but whenever the user clicks on the menu item for those forms. Here is a sample of the code: <code> begin dlg := TSelectContest.CreateNew(self, false); try if dlg.showmodal = mrok then begin tablename := dlg.tablename; TMyContest.Create(nil, tablename); end; finally dlg.Free end; </code> Tmycontest is the form that should remain visible. The form is actually shown in the forms create procedure after some other initialization. I have searched for a workaround on this but have not found anything remotely related. Is there some way to make this happen? Thanks In all newer Delphi versions ( I think this was implemented after Delphi 7) all forms use the Application.Mainform (the first form created in the application's autocreate list) as the window owner on the API level. If that owner window is minimized Windows automatically hides all the owned forms and it also makes sure the owned forms always are displayed above the owner form in the Z order. You can change this behaviour for your secondary forms by overriding the CreateParams method. procedure TFormX.CreateParams( Var params: TCreateParams ); begin inherited CreateParams( params ); // The following disconnects the form from the main form in Z order params.WndParent := GetDesktopwindow; // The following gives the form its own taskbar button params.ExStyle := params.ExStyle or WS_EX_APPWINDOW; end; The side effect is that the main form can now display on top of the secondary form and hide it. For popup forms that can be a problem. This modification also effectively disables the PopupParent of a form, since that is used in the inherited CreateParams to set the WndParent. 1 Share this post Link to post
Remy Lebeau 1398 Posted October 11, 2023 (edited) 14 hours ago, PeterBelow said: In all newer Delphi versions ( I think this was implemented after Delphi 7) all forms use the Application.Mainform (the first form created in the application's autocreate list) as the window owner on the API level. It is a bit more complicated than that. The owner window that is used at the API level depends on many factors, like if the TForm has a Parent or PopupParent assigned, or the MainForm window or the currently active TForm window may be used depending on the TForm's PopupMode. If the VCL decides the MainForm should be used, it will do so only if TApplication.MainFormOnTaskbar is True and the MainForm window has already been allocated, otherwise the TApplication window is used instead. Even then, there are TApplication.OnGetActiveFormHandle and TApplication.OnGetMainFormHandle events which allow the app to override the VCL's decision and use a different window of the app's choosing. See the implementation of TCustomForm.CreateParams(), there is a lot of decision-making going on in that method to decide which owner window to use. Edited October 12, 2023 by Remy Lebeau Share this post Link to post
Jeff Steinkamp 1 Posted October 11, 2023 I must say that I am completely perplexed and confused. I do not quite understand how a form that is created without an owner is being controlled by the mainform. Remember, this form is not being created at the application level with the Applicaiton.CreateForm syntax in the program module. It is an available form for creation at a later time. I did add this code: procedure TMyContest.CreateParams(var params: TCreateParams); begin inherited CreateParams( params ); params.ExStyle := params.ExStyle or WS_EX_APPWINDOW; end; But, as far as I can tell, it never gets fired. I placed a breakpoint in the code, but it never stops on the breakpoint even though the documentation says that this gets run when the form is created. Here is the create code for that form: constructor TMyContest.create(AOwner: TComponent; name: String); begin inherited create(AOwner); Tablename := name; show; end; and here is the calling code from the main form: dlg := TSelectContest.CreateNew(self, false); try if dlg.showmodal = mrok then begin tablename := dlg.tablename; TMyContest.Create(nil, tablename); end; finally dlg.Free So at this point, I have no clue how to create a form, show it and allow it to remain visible when the main form is minimized. I've coded in Visual Studio, QT and Swing and never run into this behavior. Share this post Link to post
Pat Foley 51 Posted October 12, 2023 I to use this on a show modal class procedure Taboutbox.showme; begin aboutbox := Taboutbox.Create(nil); and this on other form now I pass the sender so that the Sender is the owner. The owner has a list of controls that it frees on closing. Class procedure TSCSForm.showme(const AText:string; const ATime:integer); begin if not assigned(SCSForm) then SCSForm := TSCSForm.Create(nil); // should have used sender for the owner with SCSForm do begin if ATime > 0 then TimedStop := True; timer1.Interval:= Atime; Label1.caption:=Label1.caption + CR + AText; if not SCSForm.Showing then begin Show; Are you using a DataModule? Share this post Link to post
Pat Foley 51 Posted October 12, 2023 1 hour ago, Jeff Steinkamp said: and here is the calling code from the main form: dlg := TSelectContest.CreateNew(self, false); CreateNew is AFAIK is for forms without .dfm. That may hiding your Create! You should able to show and hide forms once created. and use windows task bar to navigate. Share this post Link to post
Jeff Steinkamp 1 Posted October 12, 2023 8 minutes ago, Pat Foley said: CreateNew is AFAIK is for forms without .dfm. That may hiding your Create! You should able to show and hide forms once created. and use windows task bar to navigate. If you read the code I posted, TSelectContest is NOT the issue. the Issue with TMyContest.Create(nil, tablename); Share this post Link to post
Jeff Steinkamp 1 Posted October 12, 2023 23 minutes ago, Pat Foley said: I to use this on a show modal class procedure Taboutbox.showme; begin aboutbox := Taboutbox.Create(nil); and this on other form now I pass the sender so that the Sender is the owner. The owner has a list of controls that it frees on closing. Class procedure TSCSForm.showme(const AText:string; const ATime:integer); begin if not assigned(SCSForm) then SCSForm := TSCSForm.Create(nil); // should have used sender for the owner with SCSForm do begin if ATime > 0 then TimedStop := True; timer1.Interval:= Atime; Label1.caption:=Label1.caption + CR + AText; if not SCSForm.Showing then begin Show; Are you using a DataModule? The form is not being shown as a modal form. But, Yes I do have a DataModule and I have tried passing that as the owner and still get the same behavior. Share this post Link to post
Pat Foley 51 Posted October 12, 2023 2 minutes ago, Jeff Steinkamp said: If you read the code I posted, TSelectContest is NOT the issue. the Issue with TMyContest.Create(nil, tablename); Try tablename := TMyContest.Create(Application); Share this post Link to post
Remy Lebeau 1398 Posted October 12, 2023 (edited) 1 hour ago, Jeff Steinkamp said: I do not quite understand how a form that is created without an owner is being controlled by the mainform. Remember, this form is not being created at the application level with the Applicaiton.CreateForm syntax in the program module. It is an available form for creation at a later time. A VCL owner and an API owner are two completely different things. The VCL's owner manages object lifetime. The API's owner manages window display and Z-ordering. 1 hour ago, Jeff Steinkamp said: I did add this code: procedure TMyContest.CreateParams(var params: TCreateParams); begin inherited CreateParams( params ); params.ExStyle := params.ExStyle or WS_EX_APPWINDOW; end; But, as far as I can tell, it never gets fired. Did you perhaps forget the 'override' directive on the declaration of CreateParams() in the TMyContest class? type TMyContest = class(TForm) ... protected procedure CreateParams(var params: TCreateParams); override; ^^^^^^^^^ ... end; 18 minutes ago, Pat Foley said: CreateNew is AFAIK is for forms without .dfm. That may hiding your Create! The Create() and CreateNew() constructors are just to create the TForm object. The CreateParams() method is called by the TWinControl.Handle property getter whenever the HWND window is being created at the API level. TForm object construction and HWND window creation are two different things. Edited October 12, 2023 by Remy Lebeau Share this post Link to post
Jeff Steinkamp 1 Posted October 12, 2023 11 minutes ago, Remy Lebeau said: A VCL owner and an API owner are two completely different things. The VCL's owner manages object lifetime. The API's owner manages window display and Z-ordering. Did you perhaps forget the 'override' directive on the declaration of CreateParams() in the TMyContest class? type TMyContest = class(TForm) ... protected procedure CreateParams(var params: TCreateParams); override; ^^^^^^^^^ ... end; The Create() and CreateNew() constructors are just to create the TForm object. The CreateParams() method is called by the TWinControl.Handle property getter whenever the HWND window is being created at the API level. TForm object construction and HWND window creation are two different things. Yes I did forget the Override directive. I have added that, and that procedure is now being called. I also now get the icon for that form in the taskebar. But, when I minimize the main form, the contest form still minimized, and the icon is removed from the task bar. I cannot believe this is such a freaking goat rope! How is it that a form that is created with NO owner is being manipulated by the main form. Share this post Link to post
Pat Foley 51 Posted October 12, 2023 9 minutes ago, Remy Lebeau said: The CreateParams() method is called by the TWinControl.Handle property getter whenever the TForm's HWND window is being created I did look at the source of TCustomForm to be sent to TScrollingControl did not find the stuff. I did stumble into a Menu & decider a while back 🙂 7 hours ago, Remy Lebeau said: there is a lot of decision-making going on in that method to decide which owner window to use. Share this post Link to post
Pat Foley 51 Posted October 12, 2023 10 minutes ago, Jeff Steinkamp said: But, when I minimize the main form Try hiding the mainform Share this post Link to post
Jeff Steinkamp 1 Posted October 12, 2023 1 hour ago, Pat Foley said: Try hiding the mainform What is that going to do? That is NOT what the user has selected to do. I can intercept the minimize action and change it to hide, but then how is the user going to get that back as hiding a form does not place it on the task bar. Share this post Link to post
Pat Foley 51 Posted October 12, 2023 When the main form is minimized it will hide the other forms so users can work on other Applications. Clicking on the taskbar will surface the windows again as they were before the application was minimized. It may be the main form was set in the object inspector to be topmost window. And/or bring secondary form to front and then just show over the other forms virtually hiding them. Then simply hide said form and other windows will still be there. Share this post Link to post
Die Holländer 45 Posted October 12, 2023 Has this something to do with the Application.MainFormOnTaskbar := True; statement in the .dpr main project file? Share this post Link to post
Kas Ob. 121 Posted October 12, 2023 4 hours ago, Jeff Steinkamp said: That is NOT what the user has selected to do. I can intercept the minimize action and change it to hide, but then how is the user going to get that back as hiding a form does not place it on the task bar. Have you tried my snippet above ? Here more little more complete one Main form type TMainForm = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} uses uMyContest; procedure TMainForm.Button1Click(Sender: TObject); begin //MyContestForm.Show; // or with MyContestForm.Create({Self} nil) do Show; end; end. And your ContestForm unit uMyContest; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; type TMyContestForm = class(TForm) procedure FormCreate(Sender: TObject); private procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND; public { Public declarations } end; var MyContestForm: TMyContestForm; implementation {$R *.dfm} procedure TMyContestForm.FormCreate(Sender: TObject); begin SetWindowLong(Handle, GWL_HWNDPARENT, 0); end; procedure TMyContestForm.WMSysCommand(var Msg: TWMSysCommand); begin case Msg.CmdType of SC_MINIMIZE: begin Exit; // Do nothing end; SC_MAXIMIZE: begin Exit; // Do nothing end; end; inherited; end; end. Contest form now will not hide or minimize when main form is minimized, also it have an icon on taskbar. Other than naming i didn't touch anything in the designer properties like border icons . Share this post Link to post
Jeff Steinkamp 1 Posted October 12, 2023 I'll play with this and see what happens. Right now, what I am doing is creating the contest form with the main form as the owner, hiding the main form and launching the contest form as a Modal form. When the user closes the contest form and we retune with the modal result, I'm making the main form visible again. I'll discuss this with my team of user testers and see what they say. I'll test this code above and if this works, we'll use it. Thanks!! Share this post Link to post
Remy Lebeau 1398 Posted October 12, 2023 (edited) 11 hours ago, Kas Ob. said: procedure TMyContestForm.FormCreate(Sender: TObject); begin SetWindowLong(Handle, GWL_HWNDPARENT, 0); end; FYI, your manual setting is not guaranteed to be persistent. The Form's Handle CAN be recreated dynamically during the Form's s lifetime, which would lose your manual setting. If you want to customize the Form's owner window, you need to override the Form's CreateParams() method so your manual value takes affect on every HWND that the Form creates, eg: type TMyContestForm = class(TForm) ... protected procedure CreateParams(var params: TCreateParams); override; ... end; procedure TMyContestForm.CreateParams(var params: TCreateParams); begin inherited CreateParams(params); params.WndParent := 0; // or GetDesktopWindow() end; Edited October 12, 2023 by Remy Lebeau 2 Share this post Link to post
Jeff Steinkamp 1 Posted October 15, 2023 I think we have beat this horse enough. I have two solutions that will work, and I have my beta testers evaluating each to determine which way we are going to go. Many thanks to everyone who provide input. 1 Share this post Link to post
Kas Ob. 121 Posted October 15, 2023 3 hours ago, Jeff Steinkamp said: I have two solutions that will work, and I have my beta testers evaluating each to determine which way we are going to go. It would be nice if share with us your final result for best solution, and thank you in advance. 1 Share this post Link to post