将表单置于其他应用程序之上 windows

Keep form on top of other application windows

我正在尝试展示一个位于所有其他应用程序之上的 VCL 表单,以便可以将数据从该表单复制到另一个应用程序,而无需在两个应用程序之间切换哪个在前。

我可以显示一个表单并将它放在所有其他应用程序的顶部,并使用 fsStayOnTop 与其他应用程序交互并使用以下代码打开它:

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();

我目前遇到的问题是,当我单击我的表单以从中复制和粘贴数据时,我的应用程序的其他表单也会出现,这隐藏了我正在将数据复制到的应用程序.主应用程序表单使用 ShowModal 打开 TForm1,然后可以使用上面的代码打开 TForm2,我认为它会继续将 TForm1 向前推进,因为它不应该有所有者或父级。

我已经看过这个问题 How can get my form to be on top of everything all the time? 不幸的是,它并没有阻止其他表格的提出。

所以当 TForm2 被聚焦时,windows 的顺序是:

mainform
TForm1
Whatever application, normally word
TForm2

相反,我得到

Whatever application, normally word
mainform
TForm1
TForm2

我知道这个功能看起来有点奇怪,但它对于提高可用性很重要,主要是在单显示器机器上,因为用户可能会非常频繁地在我的表单和其他应用程序之间切换。

我目前在 Windows 10 Professional 64 位上使用 Delphi 10 Seattle 以防有帮助。

超级简单的例子

(如果有更好的方法来提供上述示例,我很想知道):

Form1 按钮

procedure TForm1.Button1Click(Sender: TObject);
var
    form2: TForm2;
begin
    form2 := TForm2.Create(self);
    form2.ShowModal();
end;

Form2 按钮

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

感谢 Nat 关于隐藏其他表格的建议,我利用从 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;

我们的想法是简单地使用 Screen.Forms/Screen.FormCount 遍历所有当前显示的表单并使它们不可见,然后当我们完成后使它们再次可见。

扩展解决方案

如果您的表单是使用 Application.CreateForm() 创建的,并且只是显示和关闭但从未释放,那么这也将打开所有这些表单。我找到的解决方案是存储可见表单的列表,并只使最后可见的表单可见。

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;