使用 WindowStyle=None 正确最大化 WPF 窗口

问题描述 投票:0回答:7

使用 WindowStyle=None 选项时,WPF 窗口存在两个问题。

  1. 窗口最大化时会覆盖任务栏。
  2. 一旦最大化,窗口将无法向下拖动以取消最大化。

如何解决这些问题? 最好不使用 Windows.Forms。

wpf taskbar maximize
7个回答
41
投票

这些问题网上还有其他答案。然而,他们都没有考虑到该解决方案在具有多个显示器的设置上的执行情况。特别是如果主显示器不是设置中的最左侧。

我设计此代码时考虑了单显示器和多显示器设置。

此解决方案也引入Windows.Forms作为参考,它使用非托管调用。

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>

代码背后

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

14
投票

我有一个快速而肮脏的解决方案。最大化无边框窗口时尝试以下代码:

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

技巧是将

WindowStyle
设置为
SingleBorderWindow
,然后最大化窗口并将其设置回
None


7
投票

多么好的代码leebickmtu!

在 Windows 10 中,我在使用多个显示器时遇到了一个小问题:由于每个屏幕上都有一个任务栏,如果您在辅助屏幕上最大化窗口,他的任务栏就会隐藏。

我只是稍微修改一下这个方法,以便在任何屏幕上都有相对位置:

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

希望这有帮助...


1
投票

如果仅使用一台显示器,另一种简单的方法是设置窗口的最大高度。 System.Windows.SystemParameters 类提供了一些有用的值,例如主屏幕高度或最大化主屏幕高度。

在我的示例代码中,我使用 MaximizedPrimaryScreenHeight 并减去我在 WindowChrome 中设置的 ResizeBorderThickness。

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

1
投票

leebickmtu 的答案基本上是正确的,但有一些无关的代码,并选择基于光标所在位置的监视器,而不是窗口所在位置。如果鼠标位于不同的显示器上并且用户按

Win
+
Up
最大化,则会产生错误的行为。我们应该使用
MonitorFromWindow
来识别要最大化的监视器。

将以下内容放入窗口代码隐藏中:

protected override void OnSourceInitialized(EventArgs e)
{
    base.OnSourceInitialized(e);
    ((HwndSource)PresentationSource.FromVisual(this)).AddHook(HookProc);
}

public static IntPtr HookProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    if (msg == WM_GETMINMAXINFO)
    {
        // We need to tell the system what our size should be when maximized. Otherwise it will
        // cover the whole screen, including the task bar.
        MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));

        // Adjust the maximized size and position to fit the work area of the correct monitor
        IntPtr monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);

        if (monitor != IntPtr.Zero)
        {
            MONITORINFO monitorInfo = new MONITORINFO();
            monitorInfo.cbSize = Marshal.SizeOf(typeof(MONITORINFO));
            GetMonitorInfo(monitor, ref monitorInfo);
            RECT rcWorkArea = monitorInfo.rcWork;
            RECT rcMonitorArea = monitorInfo.rcMonitor;
            mmi.ptMaxPosition.X = Math.Abs(rcWorkArea.Left - rcMonitorArea.Left);
            mmi.ptMaxPosition.Y = Math.Abs(rcWorkArea.Top - rcMonitorArea.Top);
            mmi.ptMaxSize.X = Math.Abs(rcWorkArea.Right - rcWorkArea.Left);
            mmi.ptMaxSize.Y = Math.Abs(rcWorkArea.Bottom - rcWorkArea.Top);
        }

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

    return IntPtr.Zero;
}

private const int WM_GETMINMAXINFO = 0x0024;

private const uint MONITOR_DEFAULTTONEAREST = 0x00000002;

[DllImport("user32.dll")]
private static extern IntPtr MonitorFromWindow(IntPtr handle, uint flags);

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

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

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

[StructLayout(LayoutKind.Sequential)]
public struct MONITORINFO
{
    public int cbSize;
    public RECT rcMonitor;
    public RECT rcWork;
    public uint dwFlags;
}

[Serializable]
[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;
}

1
投票

以 Dennis 的出色解决方案为基础:

private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    HandleWinMaximized();
    StateChanged += MainWindow_StateChanged;
}

private void MainWindow_StateChanged(object sender, EventArgs e)
{
    HandleWinMaximized();
}

private void HandleWinMaximized()
{
    if (WindowState == WindowState.Maximized)
    {
        WindowStyle = WindowStyle.SingleBorderWindow;
        WindowStyle = WindowStyle.None;
    }
}

0
投票

我的博文

我写了一篇博文,你可以查看github项目,简单但有效。最大化不使用不安全代码并且适用于多个屏幕

github

© www.soinside.com 2019 - 2024. All rights reserved.