WPF无边框窗口问题:Aero Snap和最大化

我通过在XAML中设置以下窗口属性创建了一个无边框WPF窗口:

... WindowStyle="None" AllowsTransparency="True" ... 

这会导致许多问题:

1)已解决:它不再具有任何内置的resizefunction

2)已解决:它不再具有任何内置拖动function

3)已解决:没有顶部工具栏,它不再具有最小化/最大化/恢复/关闭按钮

4)已解决:通过aero快照最大化或设置WindowState可防止它被取消。

5)通过aero snap最大化或设置WindowState将使用整个屏幕作为边界,与窗口工具栏重叠。

6)通过aero快照最大化或设置WindowState似乎包含-7边距,使窗口的每边7个像素超出窗口边缘。

通过制作xaml窗口模板来解决1-3。 我使用了不可见的矩形作为句柄区域,后面的一些代码通过覆盖OnApplyTemplate()来应用,通过user32.dll SendMessage(…)附加function,用于resize/移动/最小化/最大化/恢复/关闭。

我在这里找到了#4的答案

我尝试通过WndProc截取最大化消息并手动设置大小/位置来解决5-6,但是这有一个问题,即将RestoreRegion覆盖到最大化的大小/位置,从而无法恢复窗口。

真正奇怪的是,从顶部边框调整窗口大小到屏幕顶部会触发aero full height snap,没有任何问题。

所以,我已经走了很长的路,但5-6仍然是一个问题……有没有办法手动指定最大化区域? 或者,有没有办法设置窗口大小而不影响restoreregion属性?

最简单的完整解决方案

您好,以下解决方案以最简单的方式修复了您的问题中详述的所有问题,并使用WPF和最新版本的C#语言和.NET框架在Windows 10上运行。 这是截至2017年3月15日。 如果它停止工作,请告诉我。

步骤1:要解决问题1,2和4,在应用程序的XAML中的 标记内,将其粘贴到顶部或底部:

    

CaptionHeight是窗口拖动区域的所需高度。

第2步:要解决问题3,您需要创建标题栏和标题以及窗口控件。 要做到这一点,你只需要给每个VerticalAlignment of Top提供所需的标题栏元素,或者将它们放入一个网格,其VerticalAlignment设置为Top,这将为所有这些元素执行,但要确保它们的高度不是大于在XAML中声明的WindowChrome元素上的CaptionHeight属性,从步骤1.对于所有按钮,您必须为它们或其容器分配属性WindowChrome.IsHitTestVisibleInChrome="True" 。 这是一个例子:

         

现在,要向窗口控件按钮添加适当的function,在代码隐藏的MainWindow()构造函数中,应用程序的C#源代码, 调用InitializeComponent(); 之后粘贴以下内容InitializeComponent();

 CloseButton.Click += (s, e) => Close(); MaximizeButton.Click += (s, e) => WindowState = WindowState == WindowState.Normal ? WindowState.Maximized : WindowState.Normal; MinimizeButton.Click += (s, e) => WindowState = WindowState.Minimized; 

第3步:要解决问题5和6,您需要挂钩到WmGetMinMaxInfo。 为此,请转到代码隐藏,然后将此Pastebin中的所有内容复制并粘贴到Window类中。 现在,在您的MainWindow()构造函数中,粘贴:

 SourceInitialized += (s, e) => { IntPtr handle = (new WindowInteropHelper(this)).Handle; HwndSource.FromHwnd(handle).AddHook(new HwndSourceHook(WindowProc)); }; 

通过文件菜单中的Project > Add References ,请务必引用:

 System.Management System.Windows.Interop System.Security.Principal System.Runtime.InteropServices Microsoft.Win32 

检查的最佳方法是单击左上角的Assemblies选项卡,然后选择Framework ,然后使用窗口右上角的搜索框。 现在将所有这些使用(名称空间)添加到代码隐藏的顶部:

 using System.Management; using System.Windows.Interop; using System.Security.Principal; using System.Runtime.InteropServices; using Microsoft.Win32; 

这应该涵盖一切。 我希望这有帮助!

对于第5点,请使用:

 public WindowName() // Constructor for your window { this.MaxHeight = SystemParameters.WorkArea.Height; this.MaxWidth = SystemParameters.WorkArea.Width; } 

这将确保窗口在最大化时不会与任务栏重叠。

您可以通过处理WM_GETMINMAXINFO Win32消息来指定最大化区域。 这里的代码显示了如何做到这一点。 它将解决问题#5和#6。

请注意,我会做一些不同的事情,例如在WindowProc中返回IntPtr.Zero而不是(System.IntPtr)0并使MONITOR_DEFAULTTONEAREST为常量。 但这只是编码风格的变化,并不影响净结果。

还要确保注意在SourceInitialized事件期间而不是OnApplyTemplate挂接WindowProc的更新。 这是更好的地方。 如果您正在实现从Window派生的类,那么另一个选项是覆盖OnSourceInitialized以挂钩WindowProc而不是附加到事件。 这就是我通常做的事情。

对于所有这些问题,我只能推荐这个:

MahApps.Metro: http ://mahapps.com/MahApps.Metro/

源代码: https : //github.com/MahApps/MahApps.Metro

这是一个很好的图书馆,主题很好,易于使用!

希望有所帮助

我自己就完成了这件事。 这是一项真正的苦差事,因为你必须手动解释这么多。 有趣的是,这些天我们非常理所当然地使用基本窗口的操作方式。 但是看看我提供的这个示例代码可以很好地说明这个问题到底有多少。

我希望这会有所帮助,因为我花了一点时间来到这里。

MainWindow.Xaml

                                                                                                          

MainWindow.xaml.cs

 using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace WpfApp1 { public partial class MainWindow : Window { #region --- Declarations --- private Rect _location { get; set; } #endregion #region --- Constructors --- public MainWindow() { InitializeComponent(); } #endregion #region --- Properties --- private Rect DesktopArea { get { var c = System.Windows.Forms.Cursor.Position; var s = System.Windows.Forms.Screen.FromPoint(c); var a = s.WorkingArea; return new Rect(a.Left, a.Top, a.Width, a.Height); } } #endregion #region --- Dependency Properties --- public static readonly DependencyProperty InternalWindowStateProperty = DependencyProperty.Register("InternalWindowState", typeof(WindowState), typeof(MainWindow), new PropertyMetadata(WindowState.Normal, new PropertyChangedCallback(OnInternalWindowStateChanged))); public WindowState InternalWindowState { get { return (WindowState)GetValue(InternalWindowStateProperty); } set { SetValue(InternalWindowStateProperty, value); } } private static void OnInternalWindowStateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { MainWindow instance = (MainWindow)d; instance.SetInternalWindowState((WindowState)e.NewValue); } #endregion #region --- Private Methods --- private void StoreLocation() { _location = new Rect(this.Left, this.Top, this.Width, this.Height); } private void RestoreLocation() { this.Width = _location.Width; this.Height = _location.Height; this.Top = _location.Top >= 0 ? _location.Top : 0; this.Left = _location.Left; } private void SetMaximizedState() { this.Width = DesktopArea.Width; this.Height = DesktopArea.Height; this.Top = DesktopArea.Top; this.Left = DesktopArea.Left; } private void SetInternalWindowState(WindowState state) { InternalWindowState = state; switch (InternalWindowState) { case WindowState.Normal: this.WindowState = WindowState.Normal; RestoreLocation(); break; case WindowState.Maximized: this.WindowState = WindowState.Normal; SetMaximizedState(); break; case WindowState.Minimized: this.WindowState = WindowState.Minimized; break; } } #endregion #region --- Sizing Routines --- private void Thumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e) { Thumb thumb = (Thumb)sender; int tag = Convert.ToInt32(thumb.Tag); if (thumb.Cursor == Cursors.SizeWE) HandleSizeWE(tag, e); if (thumb.Cursor == Cursors.SizeNS) HandleSizeNS(tag, e); if (thumb.Cursor == Cursors.SizeNESW) HandleSizeNESW(tag, e); if (thumb.Cursor == Cursors.SizeNWSE) HandleSizeNWSE(tag, e); } private void HandleSizeNWSE(int tag, DragDeltaEventArgs e) { if (tag == 0) { this.Top += e.VerticalChange; this.Height -= e.VerticalChange; this.Left += e.HorizontalChange; this.Width -= e.HorizontalChange; } else { this.Width += e.HorizontalChange; this.Height += e.VerticalChange; } } private void HandleSizeNESW(int tag, DragDeltaEventArgs e) { if (tag == 0) { this.Top += e.VerticalChange; this.Height -= e.VerticalChange; this.Width += e.HorizontalChange; } else { this.Left += e.HorizontalChange; this.Width -= e.HorizontalChange; this.Height += e.VerticalChange; } } private void HandleSizeNS(int tag, DragDeltaEventArgs e) { if (tag == 0) { this.Top += e.VerticalChange; this.Height -= e.VerticalChange; } else this.Height += e.VerticalChange; } private void HandleSizeWE(int tag, DragDeltaEventArgs e) { if (tag == 0) { this.Left += e.HorizontalChange; this.Width -= e.HorizontalChange; } else this.Width += e.HorizontalChange; } #endregion #region --- Event Handlers --- private void OnDragMoveWindow(Object sender, MouseButtonEventArgs e) { if (this.InternalWindowState == WindowState.Maximized) { var c = System.Windows.Forms.Cursor.Position; this.InternalWindowState = WindowState.Normal; this.Height = _location.Height; this.Width = _location.Width; this.Top = cY - (titleBar.ActualHeight / 2); this.Left = cX - (_location.Width / 2); } this.DragMove(); } private void OnMaximizeWindow(Object sender, MouseButtonEventArgs e) { if (this.InternalWindowState == WindowState.Maximized) this.InternalWindowState = WindowState.Normal; else this.InternalWindowState = WindowState.Maximized; } private void OnMinimizeWindow(Object sender, MouseButtonEventArgs e) { this.InternalWindowState = WindowState.Minimized; } private void OnCloseWindow(Object sender, MouseButtonEventArgs e) { Application.Current.Shutdown(); } private void Window_StateChanged(object sender, EventArgs e) { if (this.WindowState == WindowState.Maximized) { this.InternalWindowState = WindowState.Maximized; } } private void Window_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if (this.InternalWindowState != WindowState.Maximized) StoreLocation(); } #endregion } }