从最小化状态恢复窗口状态

43

有没有一种简单的方法可以将最小化的窗体恢复到其以前的状态,无论是普通状态还是最大化状态?我希望实现与点击任务栏(或右键单击并选择还原)相同的功能。

目前为止,我已经尝试了这个方法,但如果窗体之前是最大化状态,它仍然会以普通窗口的形式返回。

if (docView.WindowState == FormWindowState.Minimized)
    docView.WindowState = FormWindowState.Normal;

我是否需要在表单中处理状态更改以记住先前的状态?

10个回答

64

我使用以下扩展方法:

using System.Runtime.InteropServices;

namespace System.Windows.Forms
{
    public static class Extensions
    {
        [DllImport( "user32.dll" )]
        private static extern int ShowWindow( IntPtr hWnd, uint Msg );

        private const uint SW_RESTORE = 0x09;

        public static void Restore( this Form form )
        {
            if (form.WindowState == FormWindowState.Minimized)
            {
                ShowWindow(form.Handle, SW_RESTORE);
            }
        }
    }
}

那么在我的代码中调用 form.Restore()


1
当你点击箭头(也称为更多图标)时,是否还有其他人看到系统托盘中出现多个图标?当你悬停在额外的图标上时,它们会消失,因此似乎是某种刷新问题。 - Entree
在恢复窗体后,您可能还想调用Activate()方法将其置于前台并获得焦点。 - typpo
1
@MacGyver,当您使用Visual Studio中的停止按钮而非正常关闭应用程序时,就可能会发生这种情况。 - The Berga

12

恢复表单到正常状态的最简单方法是:

if (MyForm.WindowState == FormWindowState.Minimized)
{
    MyForm.WindowState = FormWindowState.Normal;
}

6
如果之前的窗口状态不是“正常”,而是“最大化”呢? - Uwe Keim
5
这并没有回答楼主的问题。实际上,这段代码与楼主已经拥有的代码是相同的。 - The Berga
对于WPF来说,在这两种情况下都很简单:如果窗口之前是正常状态,它将保持正常;如果它是最大化的,则会继续保持最大化。 - Daniele Armanasco

5
您可以通过以下方式模拟单击任务栏按钮:
SendMessage(docView.Handle, WM_SYSCOMMAND, SC_RESTORE, 0);

1
这个方法存在在哪里? - shashwat
这是一个 Win32 函数,您需要使用 P/Invoke 进行调用(https://www.pinvoke.net/default.aspx/user32.sendmessage)。 - jeffm

4

对我来说,上面的代码不起作用。

但最后我找到了可用的代码。这是它:

CxImports.ManagedWindowPlacement placement = new CxImports.ManagedWindowPlacement();
CxImports.GetWindowPlacement(Convert.ToUInt32(Handle.ToInt64()), placement);

if (placement.flags == CxImports.WPF_RESTORETOMAXIMIZED)
    WindowState = FormWindowState.Maximized;
else
    WindowState = FormWindowState.Normal;

我想你可以通过简单的搜索找到所有需要的“导入”函数。

1
请注意,MSDN文档与此相矛盾,它声称“此函数检索的WINDOWPLACEMENT的标志成员始终为零”,但这根本不是真的。 - Dwedit

3
如果有人想知道如何在其他应用程序窗口中执行此操作,这段代码对我有效:
    public void UnMinimize(IntPtr handle)
    {
        WINDOWPLACEMENT WinPlacement = new WINDOWPLACEMENT();
        GetWindowPlacement(handle, out WinPlacement);
        if(WinPlacement.flags.HasFlag(WINDOWPLACEMENT.Flags.WPF_RESTORETOMAXIMIZED))
        {
            ShowWindow(handle, (int)SW_MAXIMIZE);
        }
        else
        {
            ShowWindow(handle, (int)SW_RESTORE);
        }
    }

以下是相关内容:

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    public Int32 Left;
    public Int32 Top;
    public Int32 Right;
    public Int32 Bottom;
}

public struct POINT
{
    public int x;
    public int y;
}

public struct WINDOWPLACEMENT
{

    [Flags]
    public enum Flags : uint
    {
        WPF_ASYNCWINDOWPLACEMENT = 0x0004,
        WPF_RESTORETOMAXIMIZED = 0x0002,
        WPF_SETMINPOSITION = 0x0001
    }


    /// <summary>
    /// The length of the structure, in bytes. Before calling the GetWindowPlacement or SetWindowPlacement functions, set this member to sizeof(WINDOWPLACEMENT).
    /// </summary>
    public uint length;
    /// <summary>
    /// The flags that control the position of the minimized window and the method by which the window is restored. This member can be one or more of the following values.
    /// </summary>
    /// 
    public Flags flags;//uint flags;
                       /// <summary>
                       /// The current show state of the window. This member can be one of the following values.
                       /// </summary>
    public uint showCmd;
    /// <summary>
    /// The coordinates of the window's upper-left corner when the window is minimized.
    /// </summary>
    public POINT ptMinPosition;
    /// <summary>
    /// The coordinates of the window's upper-left corner when the window is maximized.
    /// </summary>
    public POINT ptMaxPosition;
    /// <summary>
    /// The window's coordinates when the window is in the restored position.
    /// </summary>
    public RECT rcNormalPosition;
}

public class UnMinimizeClass
{
    [DllImport("user32.dll")]
    public static extern bool GetWindowPlacement(IntPtr hWnd, out WINDOWPLACEMENT lpwndpl);

    [DllImport("user32.dll")]
    public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

    const int SW_MAXIMIZE = 3;
    const int SW_RESTORE = 9;

    public static void UnMinimize(IntPtr handle)
    {
        WINDOWPLACEMENT WinPlacement = new WINDOWPLACEMENT();
        GetWindowPlacement(handle, out WinPlacement);
        if (WinPlacement.flags.HasFlag(WINDOWPLACEMENT.Flags.WPF_RESTORETOMAXIMIZED))
        {
            ShowWindow(handle, SW_MAXIMIZE);
        }
        else
        {
            ShowWindow(handle, (int)SW_RESTORE);
        }
    }
}

1
几乎了。代码有问题,类型不正确,导致GetWindowPlacement返回虚假数据。标志和长度应该是uint,POINT成员X和Y必须是int!(Windows LONG类型不能映射到C# long) - Martin Maat

3

使用MainWindow.WindowState = WindowState.Normal;是不够的。

下面的方法适用于我的WPF应用程序:

MainWindow.WindowState = WindowState.Normal;
MainWindow.Show();
MainWindow.Activate();

1
这很简单,如果您不想使用任何PInvoke或API技巧,则可以使用它。跟踪窗体何时被调整大小,忽略最小化时的情况。
    FormWindowState _PreviousWindowState;

    private void TestForm_Resize(object sender, EventArgs e)
    {
        if (WindowState != FormWindowState.Minimized)
            _PreviousWindowState = WindowState;
    }

稍后如果你想要恢复它 -- 例如当系统托盘图标被点击时:
    private void Tray_MouseClick(object sender, MouseEventArgs e)
    {
        Activate();
        if (WindowState == FormWindowState.Minimized)
            WindowState = _PreviousWindowState; // former glory
    }

1
我刚刚增加了一些内容来泛化@Mesmo提供的解决方案。如果尚未创建实例,则会创建该实例,如果已经从应用程序的任何地方创建了该实例,则会还原并聚焦该表单。我的要求是,在应用程序的某些功能中,我不想打开多个表单。
实用程序类:
public static class Utilities
{
  [DllImport("user32.dll")]
  private static extern int ShowWindow(IntPtr hWnd, uint Msg);

  private const uint SW_RESTORE = 0x09;

  public static void Restore(this Form form)
  {
    if (form.WindowState == FormWindowState.Minimized)
    {
      ShowWindow(form.Handle, SW_RESTORE);
    }
  }

  public static void CreateOrRestoreForm<T>() where T: Form
  {
    Form form = Application.OpenForms.OfType<T>().FirstOrDefault();

    if (form == null)
    {
      form = Activator.CreateInstance<T>();
      form.Show();
    }
    else
    {
      form.Restore();
      form.Focus();
    }
  }
}

用法:

Utilities.CreateOrRestoreForm<AboutForm>();

1
这个很棒,我花了很多时间来尝试弄清楚它。谢谢,我给它投了5分。 - user2180706

0
    [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetWindowRect(IntPtr hWnd, ref wndRect lpRect);
    [DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hWnd);
    [DllImport("user32.dll")] public static extern bool EnumWindows(WNDENUMPROC lpEnumFunc, int lParam);//用来遍历所有窗口 
    [DllImport("user32.dll")] public static extern int GetWindowTextW(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)]StringBuilder lpString, int nMaxCount);//获取窗口Text 
    [DllImport("user32.dll")] public static extern int GetClassNameW(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)]StringBuilder lpString, int nMaxCount);//获取窗口类名 

    public static List<wndInfo> GetAllDesktopWindows(bool? isVisitable_)
    {
        //用来保存窗口对象
        List<wndInfo> wndList = new List<wndInfo>();

        //enum all desktop windows 
        EnumWindows(delegate (IntPtr hWnd, int lParam)
        {
            wndInfo wnd = new wndInfo();
            StringBuilder sb = new StringBuilder(256);
            //get hwnd 
            wnd.hWnd = hWnd;
            if (isVisitable_ == null || IsWindowVisible(wnd.hWnd) == isVisitable_)
            {
                //get window name  
                GetWindowTextW(hWnd, sb, sb.Capacity);
                wnd.szWindowName = sb.ToString();

                //get window class 
                GetClassNameW(hWnd, sb, sb.Capacity);
                wnd.szClassName = sb.ToString();

                wndList.Add(wnd);
            }
            return true;

        }, 0);

        return wndList;
    }

    private void Btn_Test5_Click(object sender, RoutedEventArgs e)
    {
        var ws = WSys.GetAllDesktopWindows(true);
        foreach (var w in ws)
        {
            if (w.szWindowName == "计算器")
            {
                WSys.ShowWindow(w.hWnd, 5);
                WSys.ShowWindow(w.hWnd, 9);
                Log.WriteLine(w.szWindowName);
            }
        }
    }

1
感谢您发布代码,但是您能否在帖子中添加一些说明,解释为什么以及如何工作? - Rob Streeting

-1

上述代码在某些情况下并没有完全适用于我。

在检查标志后,我还必须检查showcmd=3,如果是则最大化,否则恢复。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接