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

时间:2014-01-06 01:14:03

标签: wpf taskbar maximize

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

  1. 最大化时窗口覆盖任务栏。
  2. 一旦最大化,窗口就无法拖动到取消最大化。
  3. 如何纠正这些问题? 最好不要使用Windows.Forms。

6 个答案:

答案 0 :(得分:30)

网上有这些问题的其他答案。但是,他们都没有考虑解决方案如何在具有多个监视器的设置上执行。特别是如果主监视器不是设置中最左侧的。

我设计此代码时考虑了单个和多个监视器设置。

此解决方案也引入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;
        }
}

答案 1 :(得分:8)

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

select uh.*
from Upgrade_History Uh
where Release_ID>= All( Select Release_ID from Uh2
                        where Uh.Merchant_ID=Uh2.Merchant_ID)

诀窍是将if (WindowState == WindowState.Normal) { WindowStyle = WindowStyle.SingleBorderWindow; WindowState = WindowState.Maximized; WindowStyle = WindowStyle.None; } 设置为WindowStyle,然后最大化窗口并将其设置回SingleBorderWindow

答案 2 :(得分:3)

这么好的代码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);
    }

希望这有帮助...

答案 3 :(得分:0)

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

在我的示例代码中,我使用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;
    }
}

答案 4 :(得分:0)

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

答案 5 :(得分:0)

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