WindowChrome ResizeBorderThickness问题

Ser*_*gio 4 wpf xaml windows-shell .net-4.0 window-chrome

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

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

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

ResizeBorderThickness="8"

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

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

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

谢谢!

Noi*_*oir 13

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

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

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

此行为是由于当窗口更改为其最大化状态时,要调整大小的边缘仍处于活动状态.实际上,这种优势始终是活跃的.设置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>
Run Code Online (Sandbox Code Playgroud)

注意:这也可以在运行时代码中执行此操作

  • (B)第二个奇怪的行为:

"[...]如果我设置WindowChrome的ResizeBorderThickness <= 7,一切都完美无缺[...]并且每超过7的+1我必须开始从边缘向下拖动1个像素.[...]"

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

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

  2. 窗口的大小对当前屏幕的大小,当正常的行为应该是窗口的大小需要电流的大小,工作区(不包括任务栏,等等......当前画面)当前屏幕.

采用窗口的这种奇怪的行为使得为"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);
Run Code Online (Sandbox Code Playgroud)

定义类和结构:

[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;
}
Run Code Online (Sandbox Code Playgroud)

最后定义了将钩子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;
}
Run Code Online (Sandbox Code Playgroud)

使用CompatibilityMaximizedNoneWindow API,您只需在窗口的构造函数中调用API,如下所示:

public MyWindow
{
      [...]
      MyNamespace.CompatibilityMaximizedNoneWindow(this);
}
Run Code Online (Sandbox Code Playgroud)

并且必须解决第二个奇怪的行为.您会注意到要运行的代码,您必须添加引用PresentationFramework和命名空间System.Windows.Interop.

  • 救了我的一天!多谢,伙计 (2认同)