我正在尝试编写一个通用函数来根据当前标题/文本内容自动计算几种标准类型的WinAPI控件的宽度。此代码非常适合获取文本的宽度:
HWND hWnd = GetDlgItem(GetWindowHandle(), GetControlID());
int textLen = GetWindowTextLengthW(hWnd);
std::wstring ctrlText(textLen, 0);
GetWindowTextW(hWnd, ctrlText.data(), static_cast<int>(ctrlText.size()));
HDC hdc = GetDC(hWnd);
HFONT hFont = (HFONT)SendMessage(hWnd, WM_GETFONT, 0, 0);
if (!hFont)
hFont = (HFONT)GetStockObject(SYSTEM_FONT);
HFONT hOldFont = (HFONT)SelectObject(hdc, hFont);
SIZE textSize{};
GetTextExtentPoint32W(hdc, ctrlText.data(), static_cast<int>(ctrlText.size()), &textSize);
尽管文档说
textSize.cx
中的结果是“逻辑单位”,但结果似乎是窗口水平像素。如果我假设这个比例,我就能够在我的 dpi 感知应用程序中得出正确的像素数。
问题在于如何处理我用于单选按钮和复选框的
WC_BUTTON
控件。有没有正确的方法来计算单选按钮或复选框的附加宽度?目前,我正在对每个控件类型的附加值进行硬编码。但这似乎是一个糟糕的选择,尽管 ChatGPT 也建议对其进行硬编码。我正在寻找更好的解决方案。
编辑:根据上面的代码(现在包括分配字体),我添加了以下代码以尝试按照建议使用主题:
HTHEME hTheme = OpenThemeData(hWnd, L"BUTTON");
if (hTheme)
{
RECT rcContent{}, rcBounds{};
HRESULT rslt = GetThemeBackgroundContentRect(hTheme, hdc, partID, contentID, &rcBounds, &rcContent);
if (rslt != S_OK)
{
LONG errCode = GetLastError();
}
MARGINS margins{};
rslt = GetThemeMargins(hTheme, hdc, partID, contentID, TMT_CONTENTMARGINS, NULL, &margins);
if (rslt != S_OK)
{
LONG errCode = GetLastError();
}
CloseThemeData(hTheme);
LONG width = (rcBounds.right - rcBounds.left) + margins.cxLeftWidth + margins.cxRightWidth;
}
SelectObject(hdc, hOldFont);
ReleaseDC(hWnd, hdc); // release hdc moved here (compared to above)
我已经在调试器中验证没有发生错误。 (也就是说,没有采用
!= S_OK
路径。)但是返回的矩形(和边距)全为零。有人看出我哪里误入歧途了吗?
GetThemePartSize可以检索参考Parts and States的尺寸数据,@JonathanPotter也指出了这一点。
HTHEME hTheme = OpenThemeData(NULL, L"BUTTON");
if (hTheme)
{
SIZE size{};
HRESULT hr = GetThemePartSize(hTheme,NULL, BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL,NULL, TS_DRAW,&size);//13*13
hr = GetThemePartSize(hTheme,NULL, BP_CHECKBOX, CBS_UNCHECKEDNORMAL,NULL, TS_DRAW,&size);//13*13
}