Правильное увеличение окна WPF с помощью WindowStyle = Нет

Есть две проблемы с окнами WPF, когда используется опция WindowStyle = None.

  • Окно закрывает панель задач при максимальном увеличении.
  • После максимизации окно не может быть перетащено вниз, чтобы немоаксимизировать.

Как можно устранить эти проблемы? Не рекомендуется использовать Windows.Forms.

Ответ 1

Есть другие ответы на эти проблемы в Интернете. Однако ни один из них не учитывает, как решение будет работать при настройках с несколькими мониторами. Особенно, если основной монитор не самый левый в настройке.

Я разработал этот код с учетом настроек одного и нескольких мониторов.

Это решение также делает не в 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;
        }
}

Ответ 2

У меня хорошее и грязное решение. Попробуйте выполнить следующий код, если вы не увеличиваете свое окно без рамки:

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

Трюк состоит в том, чтобы установить WindowStyle в SingleBorderWindow, а затем увеличить окно и вернуть его на None.

Ответ 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);
    }

Надеюсь, что эта помощь...

Ответ 4

Если используется только один монитор, другой простой подход - установить максимальную высоту окна. Класс System.Windows.SystemParameters предоставляет некоторые полезные значения, например. PrimaryScreenHeight или MaximizedPrimaryScreenHeight.

В моем примере кода я использую MaximizedPrimaryScreenHeight и вычитаю ResizeBorderThickness i, установленный в 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;
    }
}