Показать форму за всем остальным и без кражи фокуса? - PullRequest
6 голосов
/ 18 февраля 2012

В StackOverflow существует вопрос о том, как отобразить форму без кражи фокуса. Ответ переопределить ShowWithoutActivation и вернуть true:

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

Это работает достаточно хорошо.

Теперь я хочу сделать еще один шаг. я хочу показать форму (т.е. сделать ее видимой), но она должна быть позади других форм в z-порядке.

Возможно в .net?

Если нет, возможно с P / Invoke?

Бонусная болтовня

Вызов SendToBack() не работает:

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

Ответы [ 3 ]

6 голосов
/ 18 февраля 2012

Немного PInvoke с использованием функции SetWindowPos

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);
}
0 голосов
/ 18 февраля 2012

Чтобы добавить к жизнеспособное решение, предоставленное Ларсом , вы также можете запретить пользователю вообще изменять 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 не были вставлены должным образом везде, где они должны быть при вызове функций. Следуйте указаниям компилятора, чтобы исправить это ...

0 голосов
/ 18 февраля 2012

Это должно сработать:

RunnerForm frm = new RunnerForm();
myMainForm.Owner = frm;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...