wpf - WindowChrome ResizeBorderThickness 问题

标签 wpf xaml .net-4.0 windows-shell window-chrome

我正在设计一个 Window,但我注意到 WindowChrome 的这种奇怪行为(在 .NET FW 4.0 中,来自 external Microsoft.Windows.Shell dll)。

我将 WindowChrome 设置为 AllowTransparency = true 和 WindowStyle = None。

如果我设置 WindowChrome 的 ResizeBorderThickness <= 7 一切正常,但如果我这样做
ResizeBorderThickness="8"
或更多,当窗口最大化时,我无法从屏幕顶部边缘附近的最后一个顶部像素拖动它,并且对于每个超过 7 的 +1,我必须开始从边缘向下拖动 1 个像素。

这很烦人,因为它在关闭窗口时禁用了常见行为,迫使我将其设置为 7 或更少。

有人可以向我解释这种行为吗?

谢谢!

最佳答案

窗口没有奇怪的行为。取而代之的是 窗口有两个奇怪的行为 .

  • (A) 第一个奇怪的行为 :

  • "[...] when the Window is Maximized I can't drag it from the last top pixel near the top edge of the screen [...]"



    此行为是由于当窗口更改为最大化状态时,要调整大小的边缘仍然处于事件状态。事实上,这个优势总是活跃的。 设置 ResizeBorderThickness 属性,WindowChrome 保留该数量的像素来控制调整窗口大小的行为。鉴于在最大化模式下不允许调整大小事件,那么您会注意到这些像素不允许任何类型的行为。这正是因为 WindowChrome 专门保留了那些控制调整大小行为的像素。

    解决办法是什么? 当窗口最大化时,您需要通知 WindowChrome 必须更改以将 ResizeBorderThickness 属性设置为 0。这可以通过 xaml 中的触发器再次设置 WindowChrome 来完成:
    <Trigger Property="WindowState" Value="Maximized">
         <Setter Property="WindowChrome.WindowChrome">
              <Setter.Value>
                   <WindowChrome ResizeBorderThickness="0" [...] />
              </Setter.Value>
         </Setter>
    </Trigger>
    

    注意:这也可以在运行时代码中这样做
  • (B) 第二个奇怪的行为 :

  • "[...] If I set the WindowChrome's ResizeBorderThickness <= 7 everything works perfectly [...] and for each +1 exceeding 7 I must start dragging 1 pixel more down from the edge. [...]"



    小心。实际上,这种行为不是由于在 ResizeBorderThickness 中设置的值,而是由于设置了属性 WindowStyle=None .设置此属性后,窗口在最大化时会出现奇怪的行为:
  • 窗口的左上边缘不位于当前屏幕的点 (0,0),而是不规则地变为负数(在您的情况下,在 Y 轴上的值似乎是 -7)。
  • 窗口大小取当前屏幕的大小,正常的行为应该是窗口大小取当前屏幕的当前工作区(当前屏幕除任务栏等)的大小。

  • 这种占用窗口的奇怪行为使得为“WindowChrome”保留的 7 个像素在当前屏幕中不可见(显然,ResizeBorderThickness="7"),因此让您感觉属性 ResizeBorderThickness="7"有效正确的,当它不是。事实上,这证明了 ResizeBorderThickness 取值 8 或更大时的行为是正确的。

    解决办法是什么? 当最大化当前屏幕的工作区域的大小和位置时,必须强制窗口。警告:如果您只对主屏幕执行此操作,则最大化事件不适用于多个屏幕。

    我通过调用外部 API 解决了这个问题的代码:
    [DllImport("user32")]
    internal static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);
    [DllImport("user32")]
    internal static extern IntPtr MonitorFromWindow(IntPtr handle, int flags);
    

    定义类和结构:
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)]
    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;
          public int top;
          public int right;
          public int bottom;
    }
    
    [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;
    }
    

    最后定义将钩子(Hook) WndProc 添加到窗口的函数:
    public static void CompatibilityMaximizedNoneWindow(Window window)
    {
          WindowInteropHelper wiHelper = new WindowInteropHelper(window);
          System.IntPtr handle = wiHelper.Handle;
          HwndSource.FromHwnd(handle).AddHook(
                    new HwndSourceHook(CompatibilityMaximizedNoneWindowProc));
    }
    
    private static System.IntPtr CompatibilityMaximizedNoneWindowProc(
        System.IntPtr hwnd,
        int msg,
        System.IntPtr wParam,
        System.IntPtr lParam,
        ref bool handled)
    {
          switch (msg)
          {
          case 0x0024:    // WM_GETMINMAXINFO
                MINMAXINFO mmi =
                    (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));
    
                    // Adjust the maximized size and position
                    // to fit the work area of the correct monitor
                    // int MONITOR_DEFAULTTONEAREST = 0x00000002;
                    System.IntPtr monitor = MonitorFromWindow(hwnd, 0x00000002);
    
                    if (monitor != System.IntPtr.Zero)
                    {
    
                          MONITORINFO monitorInfo = new MONITORINFO();
                          GetMonitorInfo(monitor, 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);
                    handled = true;
                    break;
          }
          return (System.IntPtr)0;
    }
    

    使用 CompatibilityMaximizedNoneWindow API,您只需在窗口的构造函数中调用 API,如下所示:
    public MyWindow
    {
          [...]
          MyNamespace.CompatibilityMaximizedNoneWindow(this);
    }
    

    第二个奇怪的行为必须解决。您会注意到要使代码正常工作,您必须添加引用 PresentationFramework 和命名空间 System.Windows.Interop。

    关于wpf - WindowChrome ResizeBorderThickness 问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19280016/

    相关文章:

    c# - 如何通过按删除按钮从 ListView 中删除选定的项目?

    c# - .NET 4.0 中的表达式树 : Expression. 调用无法在类型 List<T> 中找到方法 "get_Item"

    .net - 如何将此 StackPanel 用作资源

    c# - 在属性上设置绑定(bind)时是否会触发事件?

    c# - 如何使用 StringFormat 舍入绑定(bind)的 double 值

    wpf - 清除XamGrid中的过滤器单元格值

    c# - DTO 加上 UnitOfWork 模式是为 Web 应用程序设计 DAL 的好方法吗?

    c# - 处理异常后发生未处理的异常

    c# - 我将如何以编程方式访问此 WPF XAML 资源?

    WPF 渲染性能缓慢