Чтобы добавить к жизнеспособное решение, предоставленное Ларсом , вы также можете запретить пользователю вообще изменять Z-порядок окна.
Чтобы сделать это, вы должны переопределить метод WndProc
формы и перехватить сообщение WM_WINDOWPOSCHANGING
, которое отправляется окну, когда его размер, позиция или позиция Z-порядка вот-вот изменятся. Очень крутая вещь в этом сообщении - я думаю, что оно одно из моих любимых - в том, что оно позволяет вам изменить или изменить параметры изменения, которое должно произойти.
Так что в этом случае вы захотите установить флаг SWP_NOZORDER
, чтобы предотвратить изменение порядка Z окна.
Примечательным в этом подходе является то, что он всегда будет поддерживать ваше окно в Z-последовательности в последней позиции, в которой вы его оставили. Пользователь не сможет вывести его на передний план, что может или не может быть хорошей вещью, в зависимости от того, как разработан ваш пользовательский интерфейс. Он также будет отлично работать с элементами управления, отличными от форм.
Пример кода, извлеченного из одной из моих библиотек:
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);
}
}
}
А затем у меня есть небольшая небольшая подклассная форма, которая вызывает события .NET, соответствующие этим сообщениям, и позволяет обработчику событий изменять значения или отменять событие при желании. Я не работаю с 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
}
Редактировать: Хм, этот код был изначально написан на VB.NET, и я запускал его через автоматический переводчик. Похоже, что ref
не были вставлены должным образом везде, где они должны быть при вызове функций. Следуйте указаниям компилятора, чтобы исправить это ...