[英]How do I compute the non-client window size in WPF?
WPF 具有公开大量系统指标的SystemParameters
类。 在我的计算机上,我注意到一个普通窗口的标题高 30 像素,边框宽 8 像素。 这是在启用 Aero 主题的 Windows 7 上:
但是, SystemParameters
返回以下值:
SystemParameters.BorderWidth = 5
SystemParameters.CaptionHeight = 21
在这里,我禁用了 Aero 主题:
现在, SystemParameters
返回以下值:
SystemParameters.BorderWidth = 1
SystemParameters.CaptionHeight = 18
如何使用SystemParameters
计算实际观察值?
对于可调整大小的窗口,您需要使用一组不同的参数来计算大小:
var titleHeight = SystemParameters.WindowCaptionHeight
+ SystemParameters.ResizeFrameHorizontalBorderHeight;
var verticalBorderWidth = SystemParameters.ResizeFrameVerticalBorderWidth;
当您修改主题时,这些尺寸会发生变化。
我很确定GetSystemMetrics
函数( SystemParameters
类使用适当的参数在内部调用)正在为您的系统返回正确的值,它只是在 Aero 主题是否被禁用的情况下返回正确的值。 通过打开 Aero,你可以获得更强大的边框和更高的窗口标题,所有这些都是多汁的图形优点。
如果您想获得这些窗口元素的正确大小,无论用户当前的主题如何(请记住,您可以使用 Classic 主题、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;
}
然后相应地定义SendMessage
函数:
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr SendMessage(
IntPtr hWnd,
int uMsg,
IntPtr wParam,
ref TITLEBARINFOEX lParam);
最后,您可以使用以下代码调用所有这些混乱:
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;
}
编辑:现在在我的运行 Windows 7 的笔记本上进行测试和工作。
请参阅以下内容:
我想您正在尝试计算必须使应用程序窗口的大小,以便提供适量的客户区以完全显示某些 WPF 内容?
如果是这样,那么请记住 WPF 的像素为 96dpi,并且您的显示器可能以不同的 dpi 运行……正如其他答案所述,主题会影响您必须调整主窗口的大小以获得所需的大小客户区。
或者,您可以在 Window 的子控件上使用 MinWidth/MinHeight。
这是一个 C++/CLI 答案,它没有使用SystemParameters
,但我认为这是解决这个问题的更好方法,因为它应该适用于任何窗口。
事实上,其他答案仅对可调整大小的窗口有效,并且必须为每个可用的WindowStyle
创建不同的案例。
由于这些计算所需的每个SystemParameters
都有一个记录在案的 SM_CX* 或 SM_CY* 值,我认为与其重新发明轮子, AdjustWindowRectEx
简单地使用 WinAPI AdjustWindowRectEx
函数。
bool SetWindowClientArea(System::Windows::Window^ win, int width, int height) {
System::Windows::Interop::WindowInteropHelper^ wi = gcnew System::Windows::Interop::WindowInteropHelper(win);
wi->EnsureHandle();
HWND win_HWND = (HWND)(wi->Handle.ToPointer());
LONG winStyle = GetWindowLong(win_HWND, GWL_STYLE);
LONG winExStyle = GetWindowLong(win_HWND, GWL_EXSTYLE);
RECT r = { 0 };
r.right = width;
r.bottom = height;
BOOL bres = AdjustWindowRectEx(&r, winStyle, FALSE, winExStyle);
if (bres) {
Double w = r.right - r.left;
Double h = r.bottom - r.top;
win->Width = w;
win->Height = h;
}
return bres;
}
可以使用更多的DllImport
轻松将上述代码转换为 C#,或者如果您的项目已经在使用,则可以将其放入 C++/CLI 程序集。
对于重新调整大小的窗口
NON_CLIENT_AREA_HEIGHT = SystemParameters.WindowNonClientFrameThickness.Top +
SystemParameters.WindowNonClientFrameThickness.Bottom +
SystemParameters.WindowResizeBorderThickness.Top +
SystemParameters.WindowResizeBorderThickness.Bottom;
NON_CLIENT_AREA_WIDTH = SystemParameters.WindowNonClientFrameThickness.Left +
SystemParameters.WindowNonClientFrameThickness.Right +
SystemParameters.WindowResizeBorderThickness.Left +
SystemParameters.WindowResizeBorderThickness.Right;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.