如何在WPF中计算非客户端窗口大小?

Mar*_*age 33 .net c# windows wpf winapi

WPF有SystemParameters暴露系统指标的大量.在我的电脑上,我注意到普通窗口的标题高度为30像素,边框宽度为8像素.这是在Windows 7上启用了Aero主题:

非客户区 -  Aero

但是,SystemParameters返回以下值:

SystemParameters.BorderWidth = 5
SystemParameters.CaptionHeight = 21
Run Code Online (Sandbox Code Playgroud)

在这里,我禁用了Aero主题:

非客户区 - 经典

现在,SystemParameters返回以下值:

SystemParameters.BorderWidth = 1
SystemParameters.CaptionHeight = 18
Run Code Online (Sandbox Code Playgroud)

如何使用SystemParameters?计算实际观测值?

Tim*_*Tim 31

对于可调整大小的窗口,您需要使用一组不同的参数来计算大小:

var titleHeight = SystemParameters.WindowCaptionHeight
  + SystemParameters.ResizeFrameHorizontalBorderHeight;
var verticalBorderWidth = SystemParameters.ResizeFrameVerticalBorderWidth;
Run Code Online (Sandbox Code Playgroud)

修改主题时,这些大小会发生变化.

  • 在Windows 8.1上,`ResizeFrameVerticalBorderWidth`给出了4,但实际边框宽度为7. (2认同)
  • @kol`SystemParameters.ResizeFrameVerticalBorderWidth + SystemParameters.FixedFrameVerticalBorderWidth // + SystemParameters.BorderWidth`给了我正确的值(我用java得到8,但我不确定它是8还是7,所以idk如果BorderWidth很重要,我认为它的内在灰线. (2认同)

Cod*_*ray 8

我很确定GetSystemMetrics函数(SystemParameters类在内部使用适当的参数调用)为系统返回正确的值,只是在Aero主题被禁用的情况下返回正确的值.通过启用Aero,您可以获得更强大的边框和更高的窗口标题,这些都是多汁图形优点的名称.

如果您想获得这些窗口元素的正确大小,无论用户当前的主题如何(请记住,您可以使用经典主题,Aero Basic主题或完整的Aero主题运行Windows Vista及更高版本,所有这些都将进行要使用不同大小的UI元素,您需要使用Vista及更高版本中提供的其他方法.

您需要向窗口发送WM_GETTITLEBARINFOEX消息以请求扩展标题栏信息.在wParam未使用,应该为零.该lParam包含一个指向一个TITLEBARINFOEX结构将接收所有的信息.调用者负责为此结构分配内存并设置其cbSize成员.

要从.NET应用程序执行所有这些操作,您显然需要执行一些P/Invoke.首先定义您需要的常量以及TITLEBARINFOEX结构:

internal const int WM_GETTITLEBARINFOEX = 0x033F;
internal const int CCHILDREN_TITLEBAR = 5;

[StructLayout(LayoutKind.Sequential)]
internal struct TITLEBARINFOEX
{
    public int cbSize;
    public Rectangle rcTitleBar;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = CCHILDREN_TITLEBAR + 1)]
    public int[] rgstate;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = CCHILDREN_TITLEBAR + 1)]
    public Rectangle[] rgrect;
}
Run Code Online (Sandbox Code Playgroud)

然后相应地定义SendMessage函数:

[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr SendMessage(
                                          IntPtr hWnd,
                                          int uMsg,
                                          IntPtr wParam,
                                          ref TITLEBARINFOEX lParam);
Run Code Online (Sandbox Code Playgroud)

最后,您可以使用以下代码调用所有混乱:

internal static TITLEBARINFOEX GetTitleBarInfoEx(IntPtr hWnd)
{
    // Create and initialize the structure
    TITLEBARINFOEX tbi = new TITLEBARINFOEX();
    tbi.cbSize = Marshal.SizeOf(typeof(TITLEBARINFOEX));

    // Send the WM_GETTITLEBARINFOEX message
    SendMessage(hWnd, WM_GETTITLEBARINFOEX, IntPtr.Zero, ref tbi);

    // Return the filled-in structure
    return tbi;
}
Run Code Online (Sandbox Code Playgroud)

编辑:现在测试并在我的笔记本电脑上运行Windows 7.