I'm trying to show a VCL form which stays on top of all other applications so that data can be copied from this form into another application without the two applications flicking between which one is in front.
I can show a form and keep it on top of every other application and interact with the other application using fsStayOnTop and opening it using this code:
form := TForm2.Create(nil);
SetWindowPos(form.Handle, // handle to window
HWND_TOPMOST, // placement-order handle
form.Left, // horizontal position
form.Top, // vertical position
form.Width, form.Height,
// window-positioning options
SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE);
form.Show();
The problem I currently have is that when I click on my form, to copy and paste the data out of it, the other forms for my application are also brought forward which hides the application I'm copying the data to. The main application form opens a TForm1 using ShowModal which can then open TForm2 using the above code which I thought would keep it bringing the TForm1 forward since it shouldn't have an owner or parent.
I have looked at this question How can get my form to be on top of everything all the time? and unfortunately it doesn't stop the other forms from being brought forward.
So the ordering of the windows I would like when TForm2 is focused is:
mainform
TForm1
Whatever application, normally word
TForm2
Instead I'm getting
Whatever application, normally word
mainform
TForm1
TForm2
I know that this functionality seems a little weird but it is important to improving the usability, mainly on single monitor machine, as the user could be flicking between my form and the other application quite frequently.
I'm currently using Delphi 10 Seattle on Windows 10 Professional 64-bit in case that helps.
(if there's a better way to provide said example I'd love to know):
Form1 Button
procedure TForm1.Button1Click(Sender: TObject);
var
form2: TForm2;
begin
form2 := TForm2.Create(self);
form2.ShowModal();
end;
Form2 Button
procedure TForm2.Button1Click(Sender: TObject);
var
form3: TForm3;
begin
form3 := TForm3.Create(nil);
SetWindowPos(form3.Handle, // handle to window
HWND_TOPMOST, // placement-order handle
form3.Left, // horizontal position
form3.Top, // vertical position
form3.Width, form3.Height,
// window-positioning options
SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE);
form3.Show();
end;
Form3: FormStyle = fsStayOnTop
Thanks to Nat's suggestion to hide the other forms I have come up with this solution making use of the information found from How to get a list from all opened forms of my software?
procedure TForm2.Button1Click(Sender: TObject);
var
form3: TForm3;
ii: integer;
begin
for ii := 0 to Screen.FormCount - 1 do
Screen.Forms[ii].Visible := false;
form3 := TForm3.Create(nil);
SetWindowPos(form3.Handle, // handle to window
HWND_TOPMOST, // placement-order handle
form3.Left, // horizontal position
form3.Top, // vertical position
form3.Width, form3.Height,
// window-positioning options
SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE);
form3.ShowModal();
form3.Free();
for ii := 0 to Screen.FormCount - 1 do
Screen.Forms[ii].Visible := true;
end;
The idea is to simply loop through all the currently displayed forms using Screen.Forms/Screen.FormCount and make them invisible and then when we're done make them all visible again.
If your forms are created using Application.CreateForm()
and are just showed and closed but never freed then this will open all of those forms as well. The solution I've found is to store a list of the visible forms and make only those visible at the end.
procedure TForm2.Button1Click(Sender: TObject);
var
form3: TForm3;
ii: integer;
visibleForms: TList<TForm>;
begin
visibleForms := TList<TForm>.Create();
try
for ii := 0 to Screen.FormCount - 1 do
begin
if Screen.Forms[ii].Visible then
visibleForms.Add(Screen.Forms[ii]);
Screen.Forms[ii].Visible := false;
end;
form3 := TForm3.Create(nil);
SetWindowPos(form3.Handle, // handle to window
HWND_TOPMOST, // placement-order handle
form3.Left, // horizontal position
form3.Top, // vertical position
form3.Width, form3.Height,
// window-positioning options
SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE);
form3.ShowModal();
form3.Free();
finally
for ii := 0 to visibleForms.Count - 1 do
visibleForms[ii].Visible := true;
visibleForms.Free();
end;
end;