简体   繁体   English

在其他一切背后展示一张表格,而不是偷不懂焦点?

[英]Show a Form behind everything else, and without stealing focus?

There's an existing question on StackOverflow on how to show a form without stealing focus. StackOverflow上存在一个关于如何在不窃取焦点的情况下显示表单的问题。 The answer is override ShowWithoutActivation and return true: 答案是覆盖ShowWithoutActivation并返回true:

protected override bool ShowWithoutActivation
{
  get { return true; }
}

This works well enough. 这很好用。

Now i want to go one step further. 现在我想更进一步。 i want a show a Form (ie make it visible), but have it be behind other forms in the z-order. 我希望展示一个表格(即使其可见),但让它在z顺序中的其他表格后面

Possible in .net? 可能在.net?

If not, possible with P/Invoke? 如果没有,可以使用P / Invoke吗?

Bonus Chatter 奖金Chatter

Calling SendToBack() doesn't work: 调用SendToBack()不起作用:

   RunnerForm frm = new RunnerForm();
// frm.Show();
   frm.Visible = true;
   frm.SendToBack();

A little bit of PInvoke using SetWindowPos function 使用SetWindowPos函数的一点点PInvoke

public static class HWND {
  public static readonly IntPtr
  NOTOPMOST = new IntPtr(-2),
  BROADCAST = new IntPtr(0xffff),
  TOPMOST = new IntPtr(-1),
  TOP = new IntPtr(0),
  BOTTOM = new IntPtr(1);
}

public static class SWP {
  public static readonly int
  NOSIZE = 0x0001,
  NOMOVE = 0x0002,
  NOZORDER = 0x0004,
  NOREDRAW = 0x0008,
  NOACTIVATE = 0x0010,
  DRAWFRAME = 0x0020,
  FRAMECHANGED = 0x0020,
  SHOWWINDOW = 0x0040,
  HIDEWINDOW = 0x0080,
  NOCOPYBITS = 0x0100,
  NOOWNERZORDER = 0x0200,
  NOREPOSITION = 0x0200,
  NOSENDCHANGING = 0x0400,
  DEFERERASE = 0x2000,
  ASYNCWINDOWPOS = 0x4000;
}

[DllImport("user32.dll")]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);

private void button1_Click(object sender, EventArgs e) {
  RunnerForm frm = new RunnerForm();
  SetWindowPos(frm.Handle, HWND.BOTTOM, 0, 0, 0, 0, SWP.SHOWWINDOW | SWP.NOMOVE | SWP.NOOWNERZORDER | SWP.NOSIZE | SWP.NOACTIVATE);
}

This should do the trick: 这应该做的伎俩:

RunnerForm frm = new RunnerForm();
myMainForm.Owner = frm;

To add on to the viable solution Lars provided , you could also prevent the user from ever changing the window's Z order altogether. 要添加Lars提供的可行解决方案 ,您还可以防止用户完全更改窗口的Z顺序。

To do that, you would override the form's WndProc method and catch the WM_WINDOWPOSCHANGING message that is sent to a window when its size, position, or Z order position is about to change. 为此,您将覆盖窗体的WndProc方法,并捕获在其大小,位置或Z顺序位置即将更改时发送到窗口的WM_WINDOWPOSCHANGING消息 The super cool thing about this message—I think it's one of my favorites—is that it actually allows you to change or modify the parameters of the change that is about to take place. 关于这条消息的超级酷的东西 - 我认为这是我的最爱之一 - 它实际上允许你改变或修改即将发生的变化的参数。

So in this case, you'll want to set the SWP_NOZORDER flag to prevent the window's Z order from being changed. 因此,在这种情况下,您需要设置SWP_NOZORDER标志以防止更改窗口的Z顺序。

The notable thing about this approach is that it will always maintain your window in the Z order at the last position you left it. 这种方法值得注意的是它始终会在您离开它的最后位置以Z顺序维护您的窗口。 The user won't be able to bring it to the front, which may or may not be a good thing, depending on how your UI is designed. 用户将无法将其带到前面,这可能是也可能不是好事,具体取决于UI的设计方式。 It will also work just fine with controls other than forms. 除了表单之外的控件也可以正常工作。

Sample code pulled from one of my libraries: 从我的一个库中提取示例代码:

internal class NativeMethods
{
    public const int WM_WINDOWPOSCHANGING = 0x46;
    public const int WM_WINDOWPOSCHANGED = 0x47;

    [Flags()]
    public enum SetWindowPosFlags
    {
        SWP_NOSIZE = 0x1,
        SWP_NOMOVE = 0x2,
        SWP_NOZORDER = 0x4,
        SWP_NOREDRAW = 0x8,
        SWP_NOACTIVATE = 0x10,
        SWP_FRAMECHANGED = 0x20,
        SWP_DRAWFRAME = SWP_FRAMECHANGED,
        SWP_SHOWWINDOW = 0x40,
        SWP_HIDEWINDOW = 0x80,
        SWP_NOCOPYBITS = 0x100,
        SWP_NOOWNERZORDER = 0x200,
        SWP_NOREPOSITION = SWP_NOOWNERZORDER,
        SWP_NOSENDCHANGING = 0x400,
        SWP_DEFERERASE = 0x2000,
        SWP_ASYNCWINDOWPOS = 0x4000,
    }

    public enum WindowZOrder
    {
        HWND_TOP = 0,
        HWND_BOTTOM = 1,
        HWND_TOPMOST = -1,
        HWND_NOTOPMOST = -2,
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct WINDOWPOS
    {
        public IntPtr hWnd;
        public IntPtr hwndInsertAfter;
        public int x;
        public int y;
        public int cx;
        public int cy;
        public SetWindowPosFlags flags;

        // Returns the WINDOWPOS structure pointed to by the lParam parameter
        // of a WM_WINDOWPOSCHANGING or WM_WINDOWPOSCHANGED message.
        public static WINDOWPOS FromMessage(Message msg)
        {
            // Marshal the lParam parameter to an WINDOWPOS structure,
            // and return the new structure
            return (WINDOWPOS)Marshal.PtrToStructure(msg.LParam, typeof(WINDOWPOS));
        }

        // Replaces the original WINDOWPOS structure pointed to by the lParam
        // parameter of a WM_WINDOWPOSCHANGING or WM_WINDOWPSCHANGING message
        // with this one, so that the native window will be able to see any
        // changes that we have made to its values.
        public void UpdateMessage(Message msg)
        {
            // Marshal this updated structure back to lParam so the native
            // window can respond to our changes.
            // The old structure that it points to should be deleted, too.
            Marshal.StructureToPtr(this, msg.LParam, true);
        }
    }
}

And then I have a slick little subclassed form that raises .NET events corresponding to these messages and allows the event handler to modify the values or cancel the event if desired. 然后我有一个光滑的小子类形式,它引发对应于这些消息的.NET事件,并允许事件处理程序修改值或取消事件(如果需要)。 I don't handle SWP_NOZORDER , but you can get an idea of how it might work. 我不处理SWP_NOZORDER ,但你可以知道它是如何工作的。

public class FormEx : System.Windows.Forms.Form
{
    // ...snip constructors and such          

    protected override void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            case NativeMethods.WM_WINDOWPOSCHANGING:
                this.WmWindowPosChanging(m);
                return;
            // ...snip
        }

        base.WndProc(m);
    }

    private void WmWindowPosChanging(ref Message m)
    {
        // Extract the WINDOWPOS structure corresponding to this message
        NativeMethods.WINDOWPOS wndPos = NativeMethods.WINDOWPOS.FromMessage(m);

        // Determine if the size is changing (absence of SWP_NOSIZE flag)
        if (!((wndPos.flags & NativeMethods.SetWindowPosFlags.SWP_NOSIZE) == NativeMethods.SetWindowPosFlags.SWP_NOSIZE))
        {
            // Raise the LowLevelSizeChanging event
            SizeChangingEventArgs e = new SizeChangingEventArgs(this.Size, new Size(wndPos.cx, wndPos.cy));
            this.OnLowLevelSizeChanging(e);

            // Determine if the user canceled the size changing event
            if (e.Cancel)
            {
                // If so, add the SWP_NOSIZE flag
                wndPos.flags = wndPos.flags | NativeMethods.SetWindowPosFlags.SWP_NOSIZE;
                wndPos.UpdateMessage(m);
            }
        }

        // Determine if the position is changing (absence of SWP_NOMOVE flag)
        if (!((wndPos.flags & NativeMethods.SetWindowPosFlags.SWP_NOMOVE) == NativeMethods.SetWindowPosFlags.SWP_NOMOVE))
        {
            // Raise the LowLevelPositionChanging event
            PositionChangingEventArgs e = new PositionChangingEventArgs(this.Location, new Point(wndPos.x, wndPos.y));
            this.OnLowLevelPositionChanging(e);

            // Determine if the user canceled the position changing event
            if (e.Cancel)
            {
                // If so, add the SWP_NOMOVE flag
                wndPos.flags = wndPos.flags | NativeMethods.SetWindowPosFlags.SWP_NOMOVE;
                wndPos.UpdateMessage(m);
            }
        }

        base.WndProc(m);
    }

    // ...snip event infrastructure
}

Edit: Hmm, this code was originally written in VB.NET and I ran it through an automatic translator. 编辑:嗯,这段代码最初是用VB.NET编写的,我是通过自动翻译器运行的。 It looks like the ref s didn't get properly inserted everywhere that they should be when making function calls. 它似乎没有在进行函数调用时应该在任何地方正确插入ref Follow the urging of the compiler to fix it... 按照编译器的要求修复它...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM