Hoe de grootte van de controle en gap in vakje te krijgen?

stemmen
12

Ik heb een selectievakje die ik wil om nauwkeurig te meten, zodat ik kan controles correct te positioneren op een dialoog. Ik kan gemakkelijk meten van de grootte van de tekst op de controle - maar ik weet niet de officiële manier van het berekenen van de grootte van het selectievakje en de kloof voor (of na) de tekst.

De vraag is gesteld op 22/07/2009 om 13:18
bron van user
In andere talen...                            


7 antwoorden

stemmen
12

Ik ben er vrij zeker van de breedte van het selectievakje is gelijk aan

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

U kunt dan werken het gebied binnen door het aftrekken van de volgende ...

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

Ik gebruik dat in mijn code en hebben geen probleem tot nu toe gehad ...

antwoordde op 22/07/2009 om 13:53
bron van user

stemmen
0

Deze code werkt niet op Win7 met geschaald UI (lettertypen 125% groter of 150% groter). Het enige dat lijkt te werken is:

int WID = 13 * dc.GetDeviceCaps(LOGPIXELSX) / 96; 
int HEI = 13 * dc.GetDeviceCaps(LOGPIXELSY) / 96;
antwoordde op 20/12/2011 om 11:02
bron van user

stemmen
1

Het is een schande dat Microsoft een manier om dit zeker weten niet heeft verstrekt. Ik was worstelen met dezelfde vraag en het antwoord hierboven gegeven is niet compleet. Het grootste probleem met het is dat als het lettertype van het dialoogvenster is ingesteld op iets anders dan de standaardgrootte, zal die oplossing niet werken omdat vakjes worden verkleind.

Hier is hoe ik dit probleem opgelost (het is slechts een benadering die lijkt te hebben gewerkt voor mij). De code is voor MFC project.

1 - Maak twee test-besturingselementen op uw formulier, een checkbox en een radio doos:

voer image beschrijving hier

2 - Definieer de volgende aangepaste structuur:

struct CHECKBOX_DIMS{
    int nWidthPx;
    int nHeightPx;
    int nSpacePx;       //Space between checkbox and text

    CHECKBOX_DIMS()
    {
        nWidthPx = 0;
        nHeightPx = 0;
        nSpacePx = 0;
    }
};

3 - Bel het onderstaande code bij het formulier initialiseert voor elk van de test controles (dat zal hen te meten en te verwijderen, zodat eindgebruikers ze niet lijken):

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 - Nu kunt u eenvoudig het formaat van elke checkbox of radio-box door te bellen naar dit:

//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;
}
antwoordde op 09/06/2013 om 03:06
bron van user

stemmen
7

Kort antwoord:

voer image beschrijving hier

Lange versie

Van MSDN Layout Specificaties: Win32 , hebben we de specificaties van de afmetingen van een checkbox.

Het is 12 dialoog eenheden van de linkerrand van de controle aan het begin van de tekst:

voer image beschrijving hier

En een checkbox control is 10 dialoog eenheden tall:

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

Eerst berekenen we de grootte van een horizontale en een verticale dialoog eenheid:

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

Met behulp van de handige helpfunctie:

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

Nu we weten hoeveel pixels ( checkboxSpacing) toe te voegen, berekenen we het label zo groot als normaal:

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;

voer image beschrijving hier

Opmerking : Elke code vrijgegeven in het publieke domein. Geen toeschrijving vereist.

antwoordde op 04/01/2014 om 21:27
bron van user

stemmen
0

Ok dudes mijn weg is misschien niet de fastes om te gebruiken in runtime, maar het werkt voor mij in ieder geval heb ik tot nu toe getest. In de beginnin van mijn proggys zet ik in een functie om de maat te krijgen en op te slaan in een globale variabele (ja ik heb gehoord dit slecht zou zijn, maar i dont care over dit)

hier de uitleg:

  1. Maak een boomstructuur (onzichtbaar als u wilt)
  2. Maak een ImageList met tenminste 1 afbeelding binnen (formaat 16x16)
  3. Stel de ImageList aan de boomstructuur ( "TVSIL_NORMAL")
  4. Klik hier voor de "TVSIL_STATE" imagelist van de boomstructuur (u hoeft te maken "TVSIL_NORMAL" voor, anders zal dit niet!)
  5. Gebruik ImageList_GetIconSize (..) en opslaan van de grootte. Wow, de checkboxs en de radio-knoppen hebben dezelfde grootte als de iconen staat van de boomstructuur. Nu moet u wat u wilt!
  6. Vernietig de "TVSIL_NORMAL" ImageList
  7. Vernietig de boomstructuur

Deze code heeft slechts enkele microseconden aan het begin van mijn proggies en ik kan de waarde iedere keer als ik het nodig hebben te gebruiken.

antwoordde op 29/11/2015 om 18:09
bron van user

stemmen
0

Inleiding:
Ik had dezelfde vraag terwijl het proberen om de gewenste grootte van de checkbox controle voor een bepaalde tekst vast te stellen en vond dat de bestaande antwoorden niet echt werken voor mij, om verschillende redenen:

  • SM_CXMENUCHECKhoudt geen rekening met de spleet. Sterker nog, ik ben er niet van overtuigd is dit zelfs voor de reguliere vakjes, hoewel het dezelfde waarde kunnen hebben. Het kan ook afhankelijk zijn van visuele stijlen worden ingeschakeld.
  • De andere antwoorden waren te ingewikkeld en voelde me een beetje hacky (geen disrespect bedoeld, is het MS dat dit niet eenvoudig niet te maken).
  • De vermelde 12DLU lay-out was zeer behulpzaam, maar ook weer voelt willekeurig zonder een systeem metrische te vertrouwen op.
  • De antwoorden Ik probeerde nog steeds niet op een voldoende hoge pixel waarde voor de checkbox tekst uit verpakking te stoppen.

Mijn onderzoek:
Ik keek naar hoe de wijn reproduceert het gedrag en vond dat het geeft ook dezelfde resultaten als gewoon in de veronderstelling 12DLU. De tekst toch gewikkeld tenzij ik een extra 3 pixels de breedte (dat hoewel het prima zonder moeten passen) toegevoegd. Ik heb ook gemerkt dat GetTextExtentPoint32een waarde van 3 voor een lege string (hmmm ...) levert
Uitschakelen van de BS_MULTILINEstijl uiteraard gestopt met de tekst wrapping. Mijn gok is dat DrawTextW's woord wrapping berekeningen zijn onvolmaakt.
Op dit punt heb ik besloten dat de eenvoudigste oplossing was om gewoon toe te voegen 1 extra ruimte GetTextExtentPoint32, zodat er zeker zou zijn genoeg pixels. De over-schatting van een paar pixels aanvaardbaar was om mij.

Merk op dat dit alles veronderstelt uw aanvraag wordt gemanifesteerd als DPI bewust. Anders Ik vond de checkbox bleek veel groter op sommige Windows 7-systemen (niet alles).

Mijn (vooral wijn) -oplossing:

// 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);
antwoordde op 14/12/2016 om 17:46
bron van user

stemmen
1

Sorry voor het doen herleven van deze oude draad. Ik heb onlangs vroeg me af over de exact dezelfde vraag. Momenteel heeft geen van de bovenstaande antwoorden geven een resultaat in overeenstemming is met Windows 10 voor verschillende lettertypen en lettergroottes, vooral in high-DPI-omgevingen.

In plaats daarvan lijkt het erop dat het juiste resultaat wordt verkregen door

SIZE szCheckBox;
GetThemePartSize(hTheme, hDC, BP_CHECKBOX, CBS_UNCHECKEDNORMAL, &rcBackgroundContent, TS_TRUE, &szCheckBox);

voor de grootte van de checkbox zelf. En

SIZE szZeroCharacter;
GetTextExtentPoint32(hDC, L"0", 1, &szZeroCharacter);
int iGapWidth = szZeroCharacter.cx / 2;

de breedte van de spleet. Na het uitproberen van een heleboel verschillende manieren geïnspireerd door de berichten hierboven, vond ik L"0"in de dissembly van comctl32.dll. En hoewel het ziet eruit als een grap voor mij (niet per se een goede één), ik vermoed dat het een overblijfsel van de oude dagen, wanneer dit een goed genoeg onderlinge aanpassing van 2DLU zou kunnen geweest zijn.

Disclaimer: Hoewel ik het resultaat getest met verschillende lettertypen en verschillende grootte op Windows 10, heb ik niet geprobeerd na te gaan die zij houdt ook op een andere (oudere) versie van het besturingssysteem.

antwoordde op 17/12/2019 om 18:00
bron van user

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more