【问题标题】:Properly maximizing WPF window with WindowStyle=None使用 WindowStyle=None 正确最大化 WPF 窗口
【发布时间】:2014-01-23 08:29:27
【问题描述】:

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

  1. 窗口最大化时会覆盖任务栏。
  2. 一旦最大化,窗口就不能向下拖动来取消最大化。

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

【问题讨论】:

    标签: wpf taskbar maximize


    【解决方案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;
            }
    }
    

    【讨论】:

    • 很好,谢谢,我使用相同的代码让我的 Window 正常工作,遗憾的是 Microsoft 无法添加自动执行此操作的选项。 +1
    • 这种方法使 MinWidth 和 MinHeigth 无用。有什么解决方法吗?
    • +1 作为我能找到的唯一仍然适用的代码。顺便说一句,MInHeight 和 MinWidth 仍然对我有用。
    • 虽然是一个很好的答案,但它不处理用户将任务栏设置为自动隐藏的情况。
    • @Richardissimo:您有针对该特定案例的解决方案吗?另外 - 当我使用这个解决方案时,我的窗口会被放到背景或某个不再可见的地方......(我有两个显示器......)
    【解决方案2】:

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

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

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

    【讨论】:

    • 这很棒,只要你不使用 AllowTransparency = true
    • 太好了,窗口大小在最大化时还可以,我确实将它与BorderThickness = new Thickness(WindowState == WindowState.Maximized ? 8 : 0);结合使用
    【解决方案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】:

      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】:

        如果只使用一个显示器,另一种简单的方法是设置窗口的最大高度。 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;
            }
        }
        

        【讨论】:

        • 现在我发现 MaxHeight 的最佳值不取决于 ResizeBorderThickness。在我的场景中,MaxHeight 的最佳值为 MaximizedPrimaryScreenHeight - 10。
        【解决方案6】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-02-25
          • 1970-01-01
          • 2019-04-24
          • 2010-12-06
          • 2013-04-13
          • 1970-01-01
          相关资源
          最近更新 更多