Properly maximizing WPF window with WindowStyle=None

…衆ロ難τιáo~ 提交于 2019-11-29 02:32:47

问题


There are two problems with WPF windows when the WindowStyle=None option is used.

  1. The window covers the Taskbar when maximized.
  2. Once maximized, the window cannot be dragged down to unmaximize.

How can these problems be corrected? Preferably without using Windows.Forms.


回答1:


There are other answers to these problems online. However none of them take into acount how the solution will perform on setups with multiple monitors. Especially if the primary monitor is not the left-most in the setup.

I designed this code taking into account single and multiple monitors setups.

This solution also does not bring in Windows.Forms as a reference, it uses unmanagaged calls.

XAML

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Background="AliceBlue" WindowStyle="None" Height="350" Width="525" SourceInitialized="Window_SourceInitialized">
    <Grid>
        <Rectangle Name="rctHeader" Height="40" VerticalAlignment="Top" Fill="CadetBlue" PreviewMouseLeftButtonDown="rctHeader_PreviewMouseLeftButtonDown" PreviewMouseLeftButtonUp="rctHeader_PreviewMouseLeftButtonUp" PreviewMouseMove="rctHeader_PreviewMouseMove"/>
    </Grid>
</Window>

Code Behind

using System.Runtime.InteropServices;
using System.Windows.Interop;

private bool mRestoreIfMove = false;


public MainWindow()
{
    InitializeComponent();
}


void Window_SourceInitialized(object sender, EventArgs e)
{
    IntPtr mWindowHandle = (new WindowInteropHelper(this)).Handle;
    HwndSource.FromHwnd(mWindowHandle).AddHook(new HwndSourceHook(WindowProc));
}


private static System.IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
   switch (msg)
     {
        case 0x0024:
        WmGetMinMaxInfo(hwnd, lParam);
        break;
     }

        return IntPtr.Zero;
 }


private static void WmGetMinMaxInfo(System.IntPtr hwnd, System.IntPtr lParam)
{
     POINT lMousePosition;
     GetCursorPos(out lMousePosition);

     IntPtr lPrimaryScreen = MonitorFromPoint(new POINT(0, 0), MonitorOptions.MONITOR_DEFAULTTOPRIMARY);
     MONITORINFO lPrimaryScreenInfo = new MONITORINFO();
     if (GetMonitorInfo(lPrimaryScreen, lPrimaryScreenInfo) == false)
     {
        return;
     }

     IntPtr lCurrentScreen = MonitorFromPoint(lMousePosition, MonitorOptions.MONITOR_DEFAULTTONEAREST);

     MINMAXINFO lMmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));

     if (lPrimaryScreen.Equals(lCurrentScreen) == true)
     {
            lMmi.ptMaxPosition.X = lPrimaryScreenInfo.rcWork.Left;
            lMmi.ptMaxPosition.Y = lPrimaryScreenInfo.rcWork.Top;
            lMmi.ptMaxSize.X = lPrimaryScreenInfo.rcWork.Right - lPrimaryScreenInfo.rcWork.Left;
            lMmi.ptMaxSize.Y = lPrimaryScreenInfo.rcWork.Bottom - lPrimaryScreenInfo.rcWork.Top;
     }
     else
     {
            lMmi.ptMaxPosition.X = lPrimaryScreenInfo.rcMonitor.Left;
            lMmi.ptMaxPosition.Y = lPrimaryScreenInfo.rcMonitor.Top;
            lMmi.ptMaxSize.X = lPrimaryScreenInfo.rcMonitor.Right - lPrimaryScreenInfo.rcMonitor.Left;
            lMmi.ptMaxSize.Y = lPrimaryScreenInfo.rcMonitor.Bottom - lPrimaryScreenInfo.rcMonitor.Top;
     }

     Marshal.StructureToPtr(lMmi, lParam, true);
}


private void SwitchWindowState()
{
   switch (WindowState)
   {
      case WindowState.Normal:
           {
              WindowState = WindowState.Maximized;
              break;
           }
      case WindowState.Maximized:
           {
              WindowState = WindowState.Normal;
              break;
           }
    }
}


private void rctHeader_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    if (e.ClickCount == 2)
    {
       if ((ResizeMode == ResizeMode.CanResize) || (ResizeMode == ResizeMode.CanResizeWithGrip))
        {
           SwitchWindowState();
        }

         return;
     }

     else if (WindowState == WindowState.Maximized)
     {
        mRestoreIfMove = true;
        return;
     }

     DragMove();
}


private void rctHeader_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    mRestoreIfMove = false;
}


private void rctHeader_PreviewMouseMove(object sender, MouseEventArgs e)
{
   if (mRestoreIfMove)
   {
            mRestoreIfMove = false;

            double percentHorizontal = e.GetPosition(this).X / ActualWidth;
            double targetHorizontal = RestoreBounds.Width * percentHorizontal;

            double percentVertical = e.GetPosition(this).Y / ActualHeight;
            double targetVertical = RestoreBounds.Height * percentVertical;

            WindowState = WindowState.Normal;

            POINT lMousePosition;
            GetCursorPos(out lMousePosition);

            Left = lMousePosition.X - targetHorizontal;
            Top = lMousePosition.Y - targetVertical;

            DragMove();
    }
}

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetCursorPos(out POINT lpPoint);


[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr MonitorFromPoint(POINT pt, MonitorOptions dwFlags);

enum MonitorOptions : uint
{
        MONITOR_DEFAULTTONULL = 0x00000000,
        MONITOR_DEFAULTTOPRIMARY = 0x00000001,
        MONITOR_DEFAULTTONEAREST = 0x00000002
}


[DllImport("user32.dll")]
static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);


[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
        public int X;
        public int Y;

        public POINT(int x, int y)
        {
            this.X = x;
            this.Y = y;
        }
}


[StructLayout(LayoutKind.Sequential)]
public struct MINMAXINFO
{
        public POINT ptReserved;
        public POINT ptMaxSize;
        public POINT ptMaxPosition;
        public POINT ptMinTrackSize;
        public POINT ptMaxTrackSize;
};


[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class MONITORINFO
{
        public int cbSize = Marshal.SizeOf(typeof(MONITORINFO));
        public RECT rcMonitor = new RECT();
        public RECT rcWork = new RECT();
        public int dwFlags = 0;
}


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

        public RECT(int left, int top, int right, int bottom)
        {
            this.Left = left;
            this.Top = top;
            this.Right = right;
            this.Bottom = bottom;
        }
}



回答2:


I have a nice quick and dirty solution. Try following code when maximize your none bordered window:

if (WindowState == WindowState.Normal)
{
      WindowStyle = WindowStyle.SingleBorderWindow;
      WindowState = WindowState.Maximized;
      WindowStyle = WindowStyle.None;
}

The trick is to set the WindowStyle to SingleBorderWindow then maximize the window and set it back to None.




回答3:


Such a nice code leebickmtu!

I had a little issue with multiple monitors, in windows 10 : since there is a taskBar on each screen, if you maximize your window on a secondary screen his taskbar become hidden.

I just modify a bit this method, in order to have relative position from any screen :

private static void WmGetMinMaxInfo(System.IntPtr hwnd, System.IntPtr lParam)
    {
        POINT lMousePosition;
        GetCursorPos(out lMousePosition);

        IntPtr lCurrentScreen = MonitorFromPoint(lMousePosition, MonitorOptions.MONITOR_DEFAULTTONEAREST);


        MINMAXINFO lMmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));

        MONITORINFO lCurrentScreenInfo = new MONITORINFO();
        if (GetMonitorInfo(lCurrentScreen, lCurrentScreenInfo) == false)
        {
            return;
        }

        //Position relative pour notre fenêtre
        lMmi.ptMaxPosition.X = lCurrentScreenInfo.rcWork.Left - lCurrentScreenInfo.rcMonitor.Left;
        lMmi.ptMaxPosition.Y = lCurrentScreenInfo.rcWork.Top - lCurrentScreenInfo.rcMonitor.Top;
        lMmi.ptMaxSize.X = lCurrentScreenInfo.rcWork.Right - lCurrentScreenInfo.rcWork.Left;
        lMmi.ptMaxSize.Y = lCurrentScreenInfo.rcWork.Bottom - lCurrentScreenInfo.rcWork.Top;

        Marshal.StructureToPtr(lMmi, lParam, true);
    }

Hope this help...




回答4:


If only one monitor is used another simple approach is to set the maximum height of the window. The System.Windows.SystemParameters class provides some usefull values e.g. PrimaryScreenHeight or MaximizedPrimaryScreenHeight.

In my sample code i use MaximizedPrimaryScreenHeight and subtract the ResizeBorderThickness i set in WindowChrome.

using System.Windows;
using System.Windows.Shell;

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        Thickness resizeBorderThickness = WindowChrome.GetWindowChrome(this).ResizeBorderThickness;
        this.MaxHeight = SystemParameters.MaximizedPrimaryScreenHeight - resizeBorderThickness.Top - resizeBorderThickness.Bottom;
    }
}


来源:https://stackoverflow.com/questions/20941443/properly-maximizing-wpf-window-with-windowstyle-none

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!