Hvordan få størrelsen på sjekk og gapet i boksen?

stemmer
13

Jeg har en i boksen som jeg ønsker å måle slik at jeg kan plassere kontrollene på en dialog på riktig måte. Jeg kan lett måle størrelsen på teksten på kontroll - men jeg vet ikke offisielle måte å beregne størrelsen på boksen og gapet før (eller etter) teksten.

Publisert på 22/07/2009 klokken 12:18
kilden bruker
På andre språk...                            


7 svar

stemmer
13

Jeg er ganske sikker på at bredden på boksen er lik

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

Du kan deretter jobbe ut området inne ved å trekke følgende ...

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

Jeg bruker det i min kode og har ikke hatt et problem så langt ...

Svarte 22/07/2009 kl. 12:53
kilden bruker

stemmer
0

Denne koden virker ikke på Win7 med skalert UI (fonter 125% større eller 150% større). Det eneste som ser ut til å fungere er:

int WID = 13 * dc.GetDeviceCaps(LOGPIXELSX) / 96; 
int HEI = 13 * dc.GetDeviceCaps(LOGPIXELSY) / 96;
Svarte 20/12/2011 kl. 10:02
kilden bruker

stemmer
1

Det er en skam at Microsoft ikke gir en måte å vite dette sikkert. Jeg strevde med det samme spørsmålet og svaret ovenfor er ikke komplett. Hovedproblemet med det er at hvis skriften av dialogvinduet er satt til noe annet enn standardstørrelsen, vil den løsningen ikke fungerer fordi boksene vil bli endret.

Her er hvordan jeg løste dette problemet (det er bare en tilnærming som ser ut til å ha fungert for meg). Koden er for MFC-prosjektet.

1 - Lag to test kontrollene på skjemaet, en boksen og en radio boks:

skriv bildebeskrivelse her

2 - Definer følgende definert struct:

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

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

3 - Ring følgende kode når skjemaet initialiserer for hver av testkontroller (som vil måle dem og fjerne dem, slik at sluttbrukerne ikke synes dem):

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 - Nå kan du enkelt endre størrelse på boksen eller radio boksen ved å ringe dette:

//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;
}
Svarte 09/06/2013 kl. 02:06
kilden bruker

stemmer
7

Kort svar:

skriv bildebeskrivelse her

lang versjon

Fra MSDN layoutspesifikasjoner: Win32 , har vi spesifikasjonene til dimensjonene på en sjekkheftet.

Det er 12 dialog enheter fra venstre kant av kontrollen til starten av teksten:

skriv bildebeskrivelse her

Og en avkrysnings kontroll er 10 dialog enheter tall:

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

Først beregner vi på størrelse med en horisontal og en vertikal dialog enhet:

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

Ved hjelp av den hendige hjelpefunksjonen:

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

Nå som vi vet hvor mange piksler ( checkboxSpacing) for å legge til, beregner vi etikettstørrelsen som normalt:

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;

skriv bildebeskrivelse her

Merk : Enhver kode utgitt i public domain. Ingen attribusjon nødvendig.

Svarte 04/01/2014 kl. 20:27
kilden bruker

stemmer
0

Ok dudes min vei er kanskje ikke de fortest å bruke i runtime, men det fungerer for meg i alle fall jeg har testet så langt. I beginnin av mine proggys jeg satt i en funksjon for å få størrelsen og lagre den i en global variabel (ja jeg har hørt dette ville være dårlig, men jeg ikke bryr seg om dette)

Her forklaring:

  1. Lag en Utforsker (usynlig hvis u vil)
  2. Lag en imagelist med ihvertfall en bilde innsiden (størrelse 16x16)
  3. Sett imagelist til Utforsker ( "TVSIL_NORMAL")
  4. Få "TVSIL_STATE" imagelist fra Utforsker (u må skape "TVSIL_NORMAL" før, ellers vil mislykkes!)
  5. Bruk ImageList_GetIconSize (..) og lagre den størrelsen. Wow, checkboxs og radio-knappene har samme størrelse som de statlige ikoner av Utforsker. Nå u har hva u vil!
  6. Ødelegge "TVSIL_NORMAL" imagelist
  7. Ødelegge Utforsker

denne koden trenger bare noen få mikrosekunder på begynnelsen av mine proggies og jeg kan bruke den verdien hver gang jeg trenger det.

Svarte 29/11/2015 kl. 18:09
kilden bruker

stemmer
0

Innledningen:
Jeg hadde det samme spørsmålet mens du prøver å finne den nødvendige størrelsen på boksen kontroll for en gitt tekst og funnet ut at de eksisterende svarene ikke virkelig fungerer for meg, av flere grunner:

  • SM_CXMENUCHECKikke står for gapet. Faktisk, jeg er ikke overbevist om at dette er enda for vanlige boksene, selv om det kan ha samme verdi. Det kan også være avhengig av visuelle stiler blir aktivert.
  • De andre svarene var altfor komplisert og følte meg litt Hacky (ingen aktelse ment, er det MS som ikke gjør dette enkelt).
  • Oppgitt 12DLU oppsettet var veldig nyttig, selv om igjen føles vilkårlig uten et system beregning for å stole på.
  • Svarene jeg prøvde fortsatt ikke gi en høy nok pikselverdi for å stoppe boksen teksten fra innpakning.

Min undersøkelse:
Jeg så på hvordan Wine reproduserer atferd og funnet ut at det også gir de samme resultatene som bare antar 12DLU. Men teksten fortsatt innpakket med mindre jeg lagt en ekstra 3 piksler til bredden (selv om teksten skal passe fint uten). Jeg la også merke at GetTextExtentPoint32gir en verdi på 3 for en tom streng (hmmm ...)
Slå av BS_MULTILINEstilen åpenbart stoppet tekstbryting. Min gjetning er at DrawTextWOrd innpakning beregninger er ufullkomne.
På dette tidspunktet bestemte jeg meg for at den enkleste løsningen var å bare legge en ekstra plass til GetTextExtentPoint32, slik at det ville definitivt være nok piksler. Den over-estimat av et par piksler var akseptabelt for meg.

Legg merke til at alt dette forutsetter søknaden er manifestert som DPI klar. Ellers fant jeg boksen dukket opp mye større på enkelte Windows 7-systemer (ikke alle skjønt).

Min (for det meste Wine er) løsning:

// 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);
Svarte 14/12/2016 kl. 17:46
kilden bruker

stemmer
1

Sorry for å gjenopplive denne gamle tråden. Jeg har nylig funnet meg selv lurer på nøyaktig samme spørsmålet. Foreløpig har ingen av svarene ovenfor gir et resultat i tråd med Windows 10 for ulike fonter og fontstørrelser, spesielt i høy DPI miljøer.

I stedet ser det ut til at riktig resultat oppnås ved

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

for størrelsen på boksen selv. Og

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

for bredden av gapet. Etter å ha prøvd mange forskjellige metoder inspirert av innleggene ovenfor, fant jeg L"0"i demontering av Comctl32.dll. Og mens det ser ut som en vits for meg (ikke nødvendigvis en god en), jeg mistenker at det er en etterlevning fra gamle dager da dette kan ha vært en god nok tilnærming til 2DLU.

Disclaimer: Mens jeg testet resultatet med ulike fonter og størrelser på Windows 10, jeg har ikke forsøkt å verifisere at den holder også på noen andre (eldre) versjon av operativsystemet.

Svarte 17/12/2019 kl. 18:00
kilden bruker

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