如何将第二个 (child) 表格与主 (parent) 表格一起带到 windows z-order 的前面

How can I bring a second (child) form to the front of the windows z-order along with main (parent) form

正如标题所暗示的那样,当我的主窗体 (FormA) 被激活时,我想将辅助窗体(没有任务栏图标的窗体 B)置于最前面(z-order) (有重点)。

例如,我的程序运行有两种形式(FormA和FormB)。然后我打开最大化的记事本(只是为了覆盖我的应用程序在屏幕上包含的两种形式 [FormA 和 FormB])。然后我单击屏幕底部我的应用程序(即 FormA)的 Windows 任务栏项以打开我的应用程序备份。发生这种情况时,FormB 不会显示,而是留在 z-order 的背景中。我希望将这两种形式置于所有其他形式的前面。

最后,当像任何正常应用程序一样恢复 FormA 时,FormB 应该是相同的 Z-ORDER 减去 ONE。

当前源示例

public partial class FormA : Form
{
  public FormA()
  {
    Log FormB = new FormB();
    FormB.Show();

    Log FormB = new FormB();
    FormB.ShowDialog();
  }

  private void FormA_Activated(object sender, EventArgs e)
  {
    if (FormB.Visible)
    {
      FormB.Show();
    }
  }
}

经过反复试验,我最终找到了如何做到这一点。希望这对以后的其他人有所帮助,我想 post 在这里发表我的发现。

因为 .NET Form class 不直接支持它,所以您需要使用 SetWindowPos API。要做到这一点,需要做四件事。以下所有源代码都可以添加到您的 FORM 源​​代码 (FormA) 中。

如下所示,用法 (#4) 显示了我在主窗体 (FormA) 顶部使用工具条菜单项(文件、编辑等)的示例。然后第二种形式是我的日志形式(FormB)被认为是次要形式。这可以通过多种形式完成(如我的示例所示,多于 2 个)。

注意:我所有的工具条菜单项都 CheckOnClick 设置为 TRUE。这样每次您 select 菜单项都会被打开和关闭。

  1. 声明枚举

    #region Enums
    public enum SpecialWindowHandles
    {
        /// <summary>
        ///     Places the window at the top of the Z order.
        /// </summary>
        HWND_TOP = 0,
    
        /// <summary>
        ///     Places the window at the bottom of the Z order. If the hWnd parameter identifies a topmost window, the window loses its topmost status and is placed at the bottom of all other windows.
        /// </summary>
        HWND_BOTTOM = 1,
    
        /// <summary>
        ///     Places the window above all non-topmost windows. The window maintains its topmost position even when it is deactivated.
        /// </summary>
        HWND_TOPMOST = -1,
    
        /// <summary>
        ///     Places the window above all non-topmost windows (that is, behind all topmost windows). This flag has no effect if the window is already a non-topmost window.
        /// </summary>
        HWND_NOTOPMOST = -2
    }
    
    [Flags]
    public enum SetWindowPosFlags : uint
    {
        /// <summary>
        ///     If the calling thread and the thread that owns the window are attached to different input queues, the system posts the request to the thread that owns the window. This prevents the calling thread from blocking its execution while other threads process the request.
        /// </summary>
        SWP_ASYNCWINDOWPOS = 0x4000,
    
        /// <summary>
        ///     Prevents generation of the WM_SYNCPAINT message.
        /// </summary>
        SWP_DEFERERASE = 0x2000,
    
        /// <summary>
        ///     Draws a frame (defined in the window's class description) around the window.
        /// </summary>
        SWP_DRAWFRAME = 0x0020,
    
        /// <summary>
        ///     Applies new frame styles set using the SetWindowLong function. Sends a WM_NCCALCSIZE message to the window, even if the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE is sent only when the window's size is being changed.
        /// </summary>
        SWP_FRAMECHANGED = 0x0020,
    
        /// <summary>
        ///     Hides the window.
        /// </summary>
        SWP_HIDEWINDOW = 0x0080,
    
        /// <summary>
        ///     Does not activate the window. If this flag is not set, the window is activated and moved to the top of either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter parameter).
        /// </summary>
        SWP_NOACTIVATE = 0x0010,
    
        /// <summary>
        ///     Discards the entire contents of the client area. If this flag is not specified, the valid contents of the client area are saved and copied back into the client area after the window is sized or repositioned.
        /// </summary>
        SWP_NOCOPYBITS = 0x0100,
    
        /// <summary>
        ///     Retains the current position (ignores X and Y parameters).
        /// </summary>
        SWP_NOMOVE = 0x0002,
    
        /// <summary>
        ///     Does not change the owner window's position in the Z order.
        /// </summary>
        SWP_NOOWNERZORDER = 0x0200,
    
        /// <summary>
        ///     Does not redraw changes. If this flag is set, no repainting of any kind occurs. This applies to the client area, the nonclient area (including the title bar and scroll bars), and any part of the parent window uncovered as a result of the window being moved. When this flag is set, the application must explicitly invalidate or redraw any parts of the window and parent window that need redrawing.
        /// </summary>
        SWP_NOREDRAW = 0x0008,
    
        /// <summary>
        ///     Same as the SWP_NOOWNERZORDER flag.
        /// </summary>
        SWP_NOREPOSITION = 0x0200,
    
        /// <summary>
        ///     Prevents the window from receiving the WM_WINDOWPOSCHANGING message.
        /// </summary>
        SWP_NOSENDCHANGING = 0x0400,
    
        /// <summary>
        ///     Retains the current size (ignores the cx and cy parameters).
        /// </summary>
        SWP_NOSIZE = 0x0001,
    
        /// <summary>
        ///     Retains the current Z order (ignores the hWndInsertAfter parameter).
        /// </summary>
        SWP_NOZORDER = 0x0004,
    
        /// <summary>
        ///     Displays the window.
        /// </summary>
        SWP_SHOWWINDOW = 0x0040,
    }
    #endregion
    
  2. 声明 APIs

    #region APIs
    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool SetWindowPos(IntPtr hWnd, SpecialWindowHandles hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);
    #endregion
    
  3. 创建函数

    #region Functions
    private void BringVisibleWindowsToFront()
    {
        //Get parent form handle
        IntPtr hndParentWindow = this.Handle;
    
        //Bring all childern forms to the front
        foreach (Form frmChild in Application.OpenForms)
        {
            //If form is not this form
            if (frmChild.Handle != this.Handle)
            {
                //If form is visible and not minimized
                if (frmChild.WindowState != FormWindowState.Minimized && frmChild.Visible == true)
                    SetWindowPos(frmChild.Handle, SpecialWindowHandles.HWND_TOP, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE);
            }
        }
    
        //Set the parent form to the top most z order
        SetWindowPos(hndParentWindow, SpecialWindowHandles.HWND_TOP, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE);
    }
    
    private void SetAllFormWindowsAlwaysOnTop(bool active)
    {
        SpecialWindowHandles OnTop = (active ? SpecialWindowHandles.HWND_TOPMOST : SpecialWindowHandles.HWND_NOTOPMOST);
    
        //Get parent form handle
        IntPtr hndParentWindow = this.Handle;
    
        //Bring all childern forms to the front
        foreach (Form frmChild in Application.OpenForms)
        {
            //If form is not this form
            if (frmChild.Handle != this.Handle)
            {
                //If form is visible and not minimized
                if (frmChild.WindowState != FormWindowState.Minimized && frmChild.Visible == true)
                    SetWindowPos(frmChild.Handle, OnTop, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE);
            }
        }
    
        //Set the parent form to the top most z order
        SetWindowPos(hndParentWindow, OnTop, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE);
    }
    #endregion
    
  4. 用法(工具条菜单项示例)

    #region ToolStripMenuItems events
    //Shows or hides the log form (FormB)
    private void showLogToolStripMenuItem_Click(object sender, EventArgs e)
    {
        if (showLogToolStripMenuItem.Checked)
        {
            FormB.ShowLog(true);
            SetAllFormWindowsAlwaysOnTop(alwaysOnTopToolStripMenuItem.Checked);
        }
        else
            FormB.ShowLog(false);
    }
    
    //Sets all the forms (in this case FormA and FormB) to always be on top or not
    private void alwaysOnTopToolStripMenuItem_Click(object sender, EventArgs e)
    {
        SetAllFormWindowsAlwaysOnTop(alwaysOnTopToolStripMenuItem.Checked);
    }
    #endregion