There's an existing question on StackOverflow on how to show a form without stealing focus. The answer is override ShowWithoutActivation
and return 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 (i.e. make it visible), but have it be behind other forms in the z-order.
Possible in .net?
If not, possible with P/Invoke?
Calling SendToBack()
doesn't work:
RunnerForm frm = new RunnerForm();
// frm.Show();
frm.Visible = true;
frm.SendToBack();
A little bit of PInvoke using SetWindowPos function
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);
}
To add on to the viable solution Lars provided, you could also prevent the user from ever changing the window's Z order altogether.
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. 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.
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. 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. 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. I don't handle SWP_NOZORDER
, but you can get an idea of how it might work.
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. It looks like the ref
s didn't get properly inserted everywhere that they should be when making function calls. Follow the urging of the compiler to fix it...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With