代码之家  ›  专栏  ›  技术社区  ›  Mark Ingram

如何在复选框中获取复选框和间隙的大小?

  •  13
  • Mark Ingram  · 技术社区  · 15 年前

    我有一个复选框,我想精确测量,这样我可以正确地定位对话框上的控件。我可以很容易地测量控件上文本的大小,但我不知道计算复选框大小的“官方”方法以及文本前后的间距。

    6 回复  |  直到 8 年前
        1
  •  13
  •   Goz    15 年前

    我很确定复选框的宽度等于

    int x = GetSystemMetrics( SM_CXMENUCHECK );
    int y = GetSystemMetrics( SM_CYMENUCHECK );
    

    然后你可以通过减去以下内容来计算出里面的区域…

       int xInner = GetSystemMetrics( SM_CXEDGE );
       int yInner = GetSystemMetrics( SM_CYEDGE );
    

    我在代码中使用它,到目前为止还没有遇到任何问题…

        2
  •  7
  •   Ian Boyd    9 年前

    简短回答:

    enter image description here

    长版

    来自MSDN Layout Specifications: Win32 ,我们有一个复选框的尺寸规格。

    它是 12个对话单元 从控件的左边缘到文本开头:

    enter image description here

    复选框控件的高度为10个对话框单元:

    Surfaces and Controls  Height (DLUs)  Width (DLUs)
    =====================  =============  ===========
    Check box              10             As wide as possible (usually to the margins) to accommodate localization requirements.
    

    首先,我们计算水平和垂直对话框单元的大小:

    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);
    

    使用Handy Helper函数:

    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;
    }
    

    现在我们知道有多少像素( 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;
    

    enter image description here

    注释 :发布到公共域的任何代码。无需归因。

        3
  •  1
  •   c00000fd    12 年前

    很遗憾,微软没有提供一种方法来确定这一点。我也在为同样的问题苦苦挣扎,上面给出的答案并不完整。它的主要问题是,如果将对话框窗口的字体设置为默认大小以外的其他值,则该解决方案将不起作用,因为将调整复选框的大小。

    以下是我如何解决这个问题(这只是一个近似值,似乎对我有用)。代码用于MFC项目。

    1-在窗体上创建两个测试控件、一个复选框和一个单选框:

    enter image description here

    2-定义以下自定义结构:

    struct CHECKBOX_DIMS{
        int nWidthPx;
        int nHeightPx;
        int nSpacePx;       //Space between checkbox and text
    
        CHECKBOX_DIMS()
        {
            nWidthPx = 0;
            nHeightPx = 0;
            nSpacePx = 0;
        }
    };
    

    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-现在,您可以通过调用以下命令轻松调整任何复选框或单选框的大小:

    //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;
    }
    
        4
  •  0
  •   Xoft    13 年前

    此代码不适用于具有缩放用户界面(字体大125%或大150%)的Win7。唯一可行的是:

    int WID = 13 * dc.GetDeviceCaps(LOGPIXELSX) / 96; 
    int HEI = 13 * dc.GetDeviceCaps(LOGPIXELSY) / 96;
    
        5
  •  0
  •   user5078069    9 年前

    好吧,伙计们,我的方法也许不是在运行时使用的禁食,但它对我来说无论如何,我已经测试过了。 在程序开始时,我放入一个函数来获取大小并将其存储在一个全局变量中(是的,我听说这会很糟糕,但我不在乎这个)。

    解释如下:

    1. 创建树视图(如果需要,不可见)
    2. 创建一个图像列表,其中至少有一个图像(大小为16x16)
    3. 将ImageList设置为TreeView(“Tvsil_Normal”)。
    4. 从树视图中获取“tvsil_state”图像列表(您必须在之前创建“tvsil_normal”,否则此操作将失败!)
    5. 使用imagelist_geticonsize(..)并存储大小。哇,复选框和单选按钮的大小与TreeView的状态图标相同。现在你得到了你想要的!
    6. 销毁“tvsil_normal”图像列表
    7. 摧毁树视图

    在我的程序开始时,这段代码只需要几微秒,我每次需要它时都可以使用这个值。

        6
  •  0
  •   Blaze    8 年前

    序言:
    在试图确定给定文本的复选框控件所需的大小时,我遇到了相同的问题,发现现有的答案对我来说并不真正起作用,原因如下:

    • SM_CXMENUCHECK 不能解释这个差距。事实上,我不相信这甚至是常规的复选框,尽管它可能具有相同的值。它还可能取决于启用的视觉样式。
    • 其他的答案过于复杂,感觉有点老土(无意冒犯,这是微软不容易做到的)。
    • 所述的12DLU布局非常有用,尽管在没有系统指标可依赖的情况下,再次感到随意。
    • 我尝试的答案仍然没有产生足够高的像素值来阻止复选框文本换行。

    我的调查:
    我研究了葡萄酒是如何复制这种行为的,发现它也给出了与假设12dlu相同的结果。但是,除非在宽度上增加3个像素,否则文本仍然被包装(即使文本不适合)。我还注意到 GetTextExtentPoint32 生成空字符串的值3(hmmm…)
    关掉 BS_MULTILINE 样式显然停止了文本换行。我的 猜测 那是 DrawTextW 他的文字包装计算是不完美的。
    在这一点上,我决定最简单的解决方案是在 获取文本扩展点32 ,这样肯定会有足够的像素。对几个像素的过度估计对我来说是可以接受的。

    请注意,这一切都假定您的应用程序显示为DPI感知。否则,我发现该复选框在某些Windows7系统上显示得更大(并非全部)。

    我的(主要是葡萄酒)解决方案:

    // 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);