WindowChrome ResizeBorderThickness问题

时间:2013-10-09 18:38:55

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

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

我使用AllowTransparency = true和WindowStyle = None设置WindowChrome。

如果我设置WindowChrome的ResizeBorderThickness< = 7一切都很完美,但如果我这样做

ResizeBorderThickness="8"

或更多,当窗口最大化时,我无法将其从屏幕上边缘附近的最后一个顶部像素拖动,并且对于超过7的每个+1,我必须开始从边缘向下拖动1个像素。 / p>

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

有人能解释一下这种行为吗?

谢谢!

1 个答案:

答案 0 :(得分:13)

窗口没有奇怪的行为。而不是它,窗口有两个奇怪的行为

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

" [...]当窗口最大化时,我无法将其拖离屏幕顶边附近的最后一个顶部像素[...]"

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

解决方案是什么?当窗口最大化时,您需要通知 WindowChrome 必须更改以将 ResizeBorderThickness 属性设置为0。这可以通过在xaml中通过 Trigger 再次设置 WindowChrome 来完成:

<Trigger Property="WindowState" Value="Maximized">
     <Setter Property="WindowChrome.WindowChrome">
          <Setter.Value>
               <WindowChrome ResizeBorderThickness="0" [...] />
          </Setter.Value>
     </Setter>
</Trigger>

注意:这也可以在运行时代码

中执行
  • (B)第二种奇怪行为
  

&#34; [...]如果我设置WindowChrome的ResizeBorderThickness&lt; = 7一切正常[...]并且每超过7的+1我必须开始拖动1个像素以上边缘。 [...]&#34;

保重。实际上这种行为不是由于 ResizeBorderThickness 中设置的值,而是由于设置了属性 WindowStyle = None 。设置此属性后,窗口在最大化时会出现奇怪的行为:

  1. 窗口的左上边缘不位于当前屏幕的点(0,0),而是不规律地变为负值(在您的情况下,在Y轴上,值似乎为-7 )。

  2. 窗口大小取当前屏幕的大小,当正常行为应该是窗口大小占当前工作区的大小时(当前屏幕除了任务)当前屏幕的栏等等。

  3. 这个奇怪的行为占据了窗口,为WindowChrome&#39;保留了7个像素。在当前屏幕中看不到(显然有 ResizeBorderThickness =&#34; 7&#34; ),因此让您感觉属性 ResizeBorderThickness =&#34 ; 7&#34; 在没有的情况下正常工作。事实上,当 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;
    }
    

    最后定义了将钩子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