windows 如何获得复选框的大小和间隙?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1164868/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to get size of check and gap in check box?
提问by Mark Ingram
I have a check box that I want to accurately measure so I can position controls on a dialog correctly. I can easily measure the size of the text on the control - but I don't know the "official" way of calculating the size of the check box and the gap before (or after) the text.
我有一个复选框,我想准确测量它,以便我可以正确定位对话框上的控件。我可以轻松测量控件上文本的大小 - 但我不知道计算复选框大小和文本之前(或之后)间隙的“官方”方法。
回答by Goz
I'm pretty sure the width of the checkbox is equal to
我很确定复选框的宽度等于
int x = GetSystemMetrics( SM_CXMENUCHECK );
int y = GetSystemMetrics( SM_CYMENUCHECK );
You can then work out the area inside by subtracting the following ...
然后,您可以通过减去以下内容来计算内部区域...
int xInner = GetSystemMetrics( SM_CXEDGE );
int yInner = GetSystemMetrics( SM_CYEDGE );
I use that in my code and haven't had a problem thus far ...
我在我的代码中使用它,到目前为止还没有问题......
回答by Ian Boyd
Short answer:
简答:
Long Version
长版
From MSDN Layout Specifications: Win32, we have the specifications of the dimensions of a checkbox.
从 MSDN Layout Specifications: Win32,我们有一个复选框的尺寸规范。
It is 12 dialog unitsfrom the left edge of the control to the start of the text:
从控件的左边缘到文本的开头是12 个对话单元:
And a checkbox control is 10 dialog units tall:
一个复选框控件有 10 个对话框单位高:
Surfaces and Controls Height (DLUs) Width (DLUs)
===================== ============= ===========
Check box 10 As wide as possible (usually to the margins) to accommodate localization requirements.
First we calculate the size of a horizontal and a vertical dialog unit:
首先我们计算水平和垂直对话单元的大小:
const dluCheckBoxInternalSpacing = 12; //12 horizontal dlus
const dluCheckboxHeight = 10; //10 vertical dlus
Size dialogUnits = GetAveCharSize(dc);
Integer checkboxSpacing = MulDiv(dluCheckboxSpacing, dialogUnits.Width, 4);
Integer checkboxHeight = MulDiv(dluCheckboxHeight, dialogUnits.Height, 8);
Using the handy helper function:
使用方便的辅助函数:
Size GetAveCharSize(HDC dc)
{
/*
How To Calculate Dialog Base Units with Non-System-Based Font
http://support.microsoft.com/kb/125681
*/
TEXTMETRIC tm;
GetTextMetrics(dc, ref tm);
String buffer = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
Size result;
GetTextExtentPoint32(dc, buffer, 52, out result);
result.Width = (result.X/26 + 1) / 2; //div uses trunc rounding; we want arithmetic rounding
result.Height = tm.tmHeight;
return result;
}
Now that we know how many pixels (checkboxSpacing
) to add, we calculate the label size as normal:
现在我们知道checkboxSpacing
要添加多少像素 ( ),我们可以正常计算标签大小:
textRect = Rect(0,0,0,0);
DrawText(dc, Caption, -1, textRect, DT_CALCRECT or DT_LEFT or DT_SINGLELINE);
chkVerification.Width = checkboxSpacing+textRect.Right;
chkVerification.Height = checkboxHeight;
Note: Any code released into public domain. No attribution required.
注意:任何发布到公共领域的代码。无需归属。
回答by Daniel Flassig
Sorry for resurrecting this old thread. I recently found myself wondering about the exact same question. Currently, none of the answers above give a result consistent with Windows 10 for different fonts and font sizes, especially in high-DPI environments.
很抱歉让这个旧线程复活。我最近发现自己想知道完全相同的问题。目前,对于不同的字体和字体大小,尤其是在高 DPI 环境中,上述答案都没有给出与 Windows 10 一致的结果。
Instead, it seems that the correct result is obtained by
相反,似乎通过以下方式获得了正确的结果
SIZE szCheckBox;
GetThemePartSize(hTheme, hDC, BP_CHECKBOX, CBS_UNCHECKEDNORMAL, &rcBackgroundContent, TS_TRUE, &szCheckBox);
for the size of the checkbox itself. And
对于复选框本身的大小。和
SIZE szZeroCharacter;
GetTextExtentPoint32(hDC, L"0", 1, &szZeroCharacter);
int iGapWidth = szZeroCharacter.cx / 2;
for the width of the gap. After trying a lot of different methods inspired by the posts above, I found L"0"
in the dissembly of comctl32.dll. And while it looks like a joke to me (not necessarily a good one), I suspect it's a holdover from the old days when this might have been a good enough approximation of 2DLU.
对于间隙的宽度。在尝试了许多受上述帖子启发的不同方法后,我L"0"
在 comctl32.dll 的汇编中找到了。虽然对我来说这看起来像是一个笑话(不一定是一个好笑话),但我怀疑这是过去的遗留问题,当时这可能是 2DLU 的一个足够好的近似值。
Disclaimer: While I tested the result with various fonts and different sizes on Windows 10, I have not attempted to verify that it also holds on any other (older) version of the operating system.
免责声明:虽然我在 Windows 10 上使用各种字体和不同大小测试了结果,但我没有尝试验证它是否也适用于任何其他(旧)版本的操作系统。
回答by c00000fd
It is a shame that Microsoft did not provide a way to know this for sure. I was struggling with the same question and the answer provided above is not complete. The main problem with it is that if the font of the dialog window is set to something other than the default size, that solution will not work because checkboxes will be resized.
遗憾的是,Microsoft 没有提供一种方法来确定地了解这一点。我在同一个问题上苦苦挣扎,上面提供的答案不完整。它的主要问题是,如果对话框窗口的字体设置为默认大小以外的字体,则该解决方案将不起作用,因为复选框将被调整大小。
Here's how I solved this issue (it is just an approximation that seems to have worked for me). The code is for MFC project.
这是我解决这个问题的方法(这只是一个对我有用的近似值)。该代码用于 MFC 项目。
1 - Create two test controls on your form, a checkbox and a radio box:
1 - 在表单上创建两个测试控件,一个复选框和一个单选框:
2 - Define the following custom struct:
2 - 定义以下自定义结构:
struct CHECKBOX_DIMS{
int nWidthPx;
int nHeightPx;
int nSpacePx; //Space between checkbox and text
CHECKBOX_DIMS()
{
nWidthPx = 0;
nHeightPx = 0;
nSpacePx = 0;
}
};
3 - Call the following code when form initializes for each of the test controls (that will measure them and remove them so that end-users don't seem them):
3 - 当表单为每个测试控件初始化时调用以下代码(将测量它们并删除它们,以便最终用户看不到它们):
BOOL OnInitDialog()
{
CDialog::OnInitDialog();
//Calculate the size of a checkbox & radio box
VERIFY(GetInitialCheckBoxSize(IDC_CHECK_TEST, &dimsCheckBox, TRUE));
VERIFY(GetInitialCheckBoxSize(IDC_RADIO_TEST, &dimsRadioBox, TRUE));
//Continue with form initialization ...
}
BOOL GetInitialCheckBoxSize(UINT nCtrlID, CHECKBOX_DIMS* pOutCD, BOOL bRemoveCtrl)
{
//Must be called initially to calculate the size of a checkbox/radiobox
//'nCtrlID' = control ID to measure
//'pOutCD' = if not NULL, receives the dimensitions
//'bRemoveCtrl' = TRUE to delete control
//RETURN:
// = TRUE if success
BOOL bRes = FALSE;
//Get size of a check (not exactly what we need)
int nCheckW = GetSystemMetrics(SM_CXMENUCHECK);
int nCheckH = GetSystemMetrics(SM_CYMENUCHECK);
//3D border spacer (not exactly what we need either)
int nSpacerW = GetSystemMetrics(SM_CXEDGE);
//Get test checkbox
CButton* pChkWnd = (CButton*)GetDlgItem(nCtrlID);
ASSERT(pChkWnd);
if(pChkWnd)
{
CRect rcCheckBx;
pChkWnd->GetWindowRect(&rcCheckBx);
//We need only the height
//INFO: The reason why we can't use the width is because there's
// an arbitrary text followed by a spacer...
int h = rcCheckBx.Height();
CDC* pDc = pChkWnd->GetDC();
if(pDc)
{
//Get horizontal DPI setting
int dpiX = pDc->GetDeviceCaps(LOGPIXELSX);
//Calculate
if(pOutCD)
{
//Use height as-is
pOutCD->nHeightPx = h;
//Use height for the width
pOutCD->nWidthPx = (int)(h * ((double)nCheckW / nCheckH));
//Spacer is the hardest
//INFO: Assume twice and a half the size of 3D border &
// take into account DPI setting for the window
// (It will give some extra space, but it's better than less space.)
// (This number is purely experimental.)
// (96 is Windows DPI setting for 100% resolution setting.)
pOutCD->nSpacePx = (int)(nSpacerW * 2.5 * dpiX / 96.0);
}
//Release DC
pChkWnd->ReleaseDC(pDc);
if(bRemoveCtrl)
{
//Delete window
bRes = pChkWnd->DestroyWindow();
}
else
{
//Keep the window
bRes = TRUE;
}
}
}
return bRes;
}
4 - Now you can easily resize any checkbox or radio box by calling this:
4 - 现在您可以通过调用以下命令轻松调整任何复选框或单选框的大小:
//Set checkbox size & new text
VERIFY(SetCheckBoxTextAndSize(this, IDC_CHECK_ID, &dimsCheckBox, L"New text") > 0);
//Just resize radio box
VERIFY(SetCheckBoxTextAndSize(this, IDC_RADIO_ID, &dimsRadioBox, NULL) > 0);
int SetCheckBoxTextAndSize(CWnd* pParWnd, UINT nCheckBoxID, CHECKBOX_DIMS* pDims, LPCTSTR pNewText)
{
//Set size of the checkbox/radio to 'pNewText' and update its size according to its text
//'pParWnd' = parent dialog window
//'nCheckBoxID' = control ID to resize (checkbox or radio box)
//'pDims' = pointer to the struct with checkbox/radiobox dimensions
//'pNewText' = text to set, or NULL not to change the text
//RETURN:
// = New width of the control in pixels, or
// = 0 if error
int nRes = 0;
ASSERT(pParWnd);
ASSERT(pDims);
CButton* pChkWnd = (CButton*)pParWnd->GetDlgItem(nCheckBoxID);
ASSERT(pChkWnd);
if(pChkWnd)
{
CDC* pDc = pChkWnd->GetDC();
CFont* pFont = pChkWnd->GetFont();
if(pDc)
{
if(pFont)
{
//Make logfont
LOGFONT lf = {0};
if(pFont->GetLogFont(&lf))
{
//Make new font
CFont font;
if(font.CreateFontIndirect(&lf))
{
//Get font from control
CFont* pOldFont = pDc->SelectObject(&font);
//Get text to set
CString strCheck;
if(pNewText)
{
//Use new text
strCheck = pNewText;
}
else
{
//Keep old text
pChkWnd->GetWindowText(strCheck);
}
//Calculate size
RECT rc = {0, 0, 0, 0};
::DrawText(pDc->GetSafeHdc(), strCheck, strCheck.GetLength(), &rc, DT_CALCRECT | DT_NOPREFIX | DT_SINGLELINE);
//Get text width
int nTextWidth = abs(rc.right - rc.left);
//See if it's valid
if(nTextWidth > 0 ||
(nTextWidth == 0 && strCheck.GetLength() == 0))
{
//Get location of checkbox
CRect rcChk;
pChkWnd->GetWindowRect(&rcChk);
pParWnd->ScreenToClient(rcChk);
//Update its size
rcChk.right = rcChk.left + pDims->nWidthPx + pDims->nSpacePx + nTextWidth;
//Use this line if you want to change the height as well
//rcChk.bottom = rcChk.top + pDims->nHeightPx;
//Move the control
pChkWnd->MoveWindow(rcChk);
//Setting new text?
if(pNewText)
{
pChkWnd->SetWindowText(pNewText);
}
//Done
nRes = abs(rcChk.right - rcChk.left);
}
//Set font back
pDc->SelectObject(pOldFont);
}
}
}
//Release DC
pChkWnd->ReleaseDC(pDc);
}
}
return nRes;
}
回答by Xoft
This code doesn't work on Win7 with scaled UI (fonts 125% larger or 150% larger). The only thing that seems to work is:
此代码不适用于具有缩放 UI(字体大 125% 或 150%)的 Win7。唯一似乎有效的是:
int WID = 13 * dc.GetDeviceCaps(LOGPIXELSX) / 96;
int HEI = 13 * dc.GetDeviceCaps(LOGPIXELSY) / 96;
回答by user5078069
Ok dudes my way is maybe not the fastes to use in runtime, but it works for me in any case i have tested so far. In the beginnin of my proggys i put in a function to get the size and store it in a global variable (yeah i have heard this would be bad, but i dont care about this)
好吧,伙计们,我的方式可能不是在运行时使用的禁食,但无论如何我已经测试过了,它对我有用。在我的 proggys 开始时,我输入了一个函数来获取大小并将其存储在一个全局变量中(是的,我听说这会很糟糕,但我不在乎这个)
here the explanation:
这里的解释:
- Create a treeview (invisible if u want)
- Create an imagelist with atleast 1 image inside (size 16x16)
- Set the imagelist to the treeview ("TVSIL_NORMAL")
- Get the "TVSIL_STATE" imagelist from the treeview (u have to create "TVSIL_NORMAL" before, otherwise this one will fail!)
- Use ImageList_GetIconSize(..) and store the size. Wow, the checkboxs and the radio-buttons have the same size as the state icons of the treeview. Now u have what u want!
- Destroy the "TVSIL_NORMAL" imagelist
- Destroy the treeview
- 创建一个树状视图(如果你想不可见)
- 创建一个包含至少 1 个图像的图像列表(大小 16x16)
- 将图像列表设置为树视图(“TVSIL_NORMAL”)
- 从树视图中获取“TVSIL_STATE”图像列表(你必须先创建“TVSIL_NORMAL”,否则这个会失败!)
- 使用 ImageList_GetIconSize(..) 并存储大小。哇,复选框和单选按钮的大小与树视图的状态图标相同。现在你有你想要的!
- 销毁“TVSIL_NORMAL”图像列表
- 销毁树状视图
this code needs only a few microseconds at the beginning of my proggies and i can use the value everytime i need it.
这段代码在我的 proggies 开始时只需要几微秒,我可以在每次需要时使用该值。
回答by Blaze
Preamble:
I had the same question while trying to determine the needed size of the checkbox control for a given text and found that the existing answers didn't really work for me, for several reasons:
序言:
我在尝试确定给定文本所需的复选框控件大小时遇到了同样的问题,发现现有答案对我来说并不真正适用,原因有几个:
SM_CXMENUCHECK
doesn't account for the gap. In fact, I'm not convinced this is even for regular checkboxes, although it may have the same value. It may also be dependent on visual styles being enabled.- The other answers were overly complicated and felt a bit hacky (no disrespect intended, it is MS that don't make this easy).
- The stated 12DLU layout was very helpful, although again feels arbitrary without a system metric to rely on.
- The answers I tried still didn't yield a high enough pixel value to stop the checkbox text from wrapping.
SM_CXMENUCHECK
不考虑差距。事实上,我不相信这甚至适用于常规复选框,尽管它可能具有相同的值。它也可能取决于启用的视觉样式。- 其他答案过于复杂,感觉有点老套(没有不尊重的意思,MS 不会让这变得容易)。
- 所述的 12DLU 布局非常有用,尽管在没有可依赖的系统指标的情况下再次让人感觉随意。
- 我尝试的答案仍然没有产生足够高的像素值来阻止复选框文本换行。
My investigation:
I looked at how Wine reproduces the behavior and found that it also gives the same results as simply assuming 12DLU. However, the text still wrapped unless I added an extra 3 pixels to the width (even though the text should fit fine without). I also noticed that GetTextExtentPoint32
yields a value of 3 for an empty string (hmmm...)
Turning off the BS_MULTILINE
style obviously stopped the text wrapping. My guessis that DrawTextW
's word wrapping calculations are imperfect.
At this point I decided that the simplest solution was to just add 1 extra space to GetTextExtentPoint32
, so that there would definitely be enough pixels. The over-estimate of a couple of pixels was acceptable to me.
我的调查:
我查看了 Wine 如何重现该行为,发现它也给出了与简单假设 12DLU 相同的结果。但是,除非我在宽度上添加了额外的 3 个像素,否则文本仍然会换行(即使没有文本应该可以很好地适应)。我还注意到,GetTextExtentPoint32
对于空字符串(嗯...)的产生值为 3
关闭BS_MULTILINE
样式显然会停止文本换行。我的猜测是 thatDrawTextW
的自动换行计算是不完美的。
在这一点上,我决定最简单的解决方案是在 中添加 1 个额外的空间GetTextExtentPoint32
,这样肯定会有足够的像素。对几个像素的高估对我来说是可以接受的。
Note that this all assumes your application is manifested as DPI aware. Otherwise I found the checkbox appeared much larger on some Windows 7 systems (not all though).
请注意,这一切都假设您的应用程序显示为 DPI 感知。否则我发现复选框在某些 Windows 7 系统上显得更大(虽然不是全部)。
My (mostly Wine's) solution:
我的(主要是 Wine 的)解决方案:
// This code gets the size of a piece of text and adds the size of a
// checkbox and gap. Note that this is very rough code with no error handling.
BOOL isCheckbox = TRUE;
HWND dialog = ... // Your control or dialog
HFONT font = ... // The font your control will use if it hasn't been set yet
PTCHAR text = ... // Your text
HFONT currentFont;
SIZE size;
HDC dc = GetDC(dialog);
if (!font) {
font = (HFONT)SendMessage(dialog, WM_GETFONT, 0, 0);
}
currentFont = (HFONT)SelectObject(dc, font); // NB: You should add error handling here
if (isCheckbox) {
// Or you can disable BS_MULTILINE
_tcscat(text, TEXT(" ")); // NB: This assumes text is allocated for +1 char
}
GetTextExtentPoint32(dc, text, _tcslen(text), &size); // NB: You should add error handling here
if (isCheckbox) {
int checkBoxWidth = 12 * GetDeviceCaps(dc, LOGPIXELSX ) / 96 + 1;
int checkBoxHeight = 12 * GetDeviceCaps(dc, LOGPIXELSY ) / 96 + 1;
int textOffset;
GetCharWidthW(dc, '0', '0', &textOffset);
textOffset /= 2;
size->cx += checkBoxWidth + textOffset;
if (size->cy < checkBoxHeight) {
size->cy = checkBoxHeight;
}
}
if (currentFont) {
SelectObject(dc, currentFont);
}
ReleaseDC(dialog, dc);