42
votes

Is there an easy method to restore a minimized form to its previous state, either Normal or Maximized? I'm expecting the same functionality as clicking the taskbar (or right-clicking and choosing restore).

So far, I have this, but if the form was previously maximized, it still comes back as a normal window.

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

Do I have to handle the state change in the form to remember the previous state?

10

10 Answers

61
votes

I use the following extension method:

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);
            }
        }
    }
}

Then call form.Restore() in my code.

13
votes

The easiest way to restore a form to normal state is:

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

You could simulate clicking on the taskbar button like this:

SendMessage(docView.Handle, WM_SYSCOMMAND, SC_RESTORE, 0);
4
votes

For me, the code above does NOT work.

But at last I found the working code. Here it is:

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;

I guess, you can find all the needed "imported" functions by simple googling.

3
votes

If anybody wonders how to do that with other apps windows,this code works for me:

    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);
        }
    }

Stuff is here:

[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);
        }
    }
}
2
votes

Using MainWindow.WindowState = WindowState.Normal; isn't enough

Next approach works for my WPF applcation:

MainWindow.WindowState = WindowState.Normal;
MainWindow.Show();
MainWindow.Activate();
0
votes
    [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);
            }
        }
    }
0
votes

I just added one more piece to generify the solution given by @Mesmo. It will create the instance if not created or restore and focus the form if the instance is already created from anywhere in the application. My requirement was that I didn't want to open multiple forms for some of the functionality in the application.

Utilities Class:

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();
    }
  }
}

Usage:

Utilities.CreateOrRestoreForm<AboutForm>();
0
votes

This is simple and works if you don't want to use any PInvoke or API trickery. Keep track of when the form has been resized, ignoring when it is minimised.

    FormWindowState _PreviousWindowState;

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

Later when you want to restore it -- for example if a tray icon is clicked:

    private void Tray_MouseClick(object sender, MouseEventArgs e)
    {
        Activate();
        if (WindowState == FormWindowState.Minimized)
            WindowState = _PreviousWindowState; // former glory
    }
-1
votes

The above code did not quite work for me in all situations

After checking the flags I also have to check showcmd=3 and if so maximise else restore