Come ottenere la dimensione del controllo e gap nella casella di controllo?

voti
12

Ho una casella di controllo che voglio misurare con precisione in modo da poter posizionare i controlli su una finestra di dialogo in modo corretto. Posso facilmente misurare la dimensione del testo sul controllo - ma non so il modo ufficiale di calcolo della dimensione della casella di controllo e il divario prima (o dopo) il testo.

È pubblicato 22/07/2009 alle 13:18
fonte dall'utente
In altre lingue...                            


7 risposte

voti
12

Sono abbastanza sicuro che la larghezza della casella di controllo è pari a

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

È quindi possibile calcolare l'area all'interno sottraendo il seguente ...

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

Io uso che nel mio codice e non ho avuto un problema finora ...

Risposto il 22/07/2009 a 13:53
fonte dall'utente

voti
0

Questo codice non funziona su Win7 con scala UI (font 125% più grande o 150% più grande). L'unica cosa che sembra funzionare è:

int WID = 13 * dc.GetDeviceCaps(LOGPIXELSX) / 96; 
int HEI = 13 * dc.GetDeviceCaps(LOGPIXELSY) / 96;
Risposto il 20/12/2011 a 11:02
fonte dall'utente

voti
1

E 'un peccato che Microsoft non ha fornito un modo per sapere questo di sicuro. Stavo lottando con la stessa domanda e la risposta fornita in precedenza non è completa. Il problema principale con esso è che se il font della finestra di dialogo è impostata a qualcosa di diverso la dimensione predefinita, tale soluzione non funzionerà perché le caselle di controllo verranno ridimensionate.

Ecco come ho risolto questo problema (è solo un'approssimazione che sembra aver funzionato per me). Il codice è per il progetto MFC.

1 - Creare due controlli di test sul modulo, una casella di controllo e una scatola della radio:

entrare descrizione dell'immagine qui

2 - Definire quanto segue struct personalizzato:

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

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

3 - chiamare il seguente codice quando modulo inizializza per ciascuno dei controlli di test (che li misurerà e rimuoverli in modo che gli utenti finali non sembrano loro):

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 - Ora si può facilmente ridimensionare qualsiasi casella di controllo o radio box chiamando questo:

//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;
}
Risposto il 09/06/2013 a 03:06
fonte dall'utente

voti
7

Risposta breve:

entrare descrizione dell'immagine qui

Long Version

Da MSDN layout Specifiche: Win32 , abbiamo le specifiche delle dimensioni di una casella di controllo.

Si tratta di 12 unità di dialogo dal bordo sinistro del controllo per l'inizio del testo:

entrare descrizione dell'immagine qui

E un controllo casella di controllo è alto 10 unità di dialogo:

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

In primo luogo si calcola la dimensione di una orizzontale e un'unità di finestra verticale:

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

Utilizzando la funzione di supporto a portata di mano:

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

Ora che sappiamo il numero di pixel ( checkboxSpacing) per aggiungere, calcoliamo le dimensioni dell'etichetta come normale:

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;

entrare descrizione dell'immagine qui

Nota : Qualsiasi codice rilasciata nel pubblico dominio. No attribuzione richiesto.

Risposto il 04/01/2014 a 21:27
fonte dall'utente

voti
0

tizi Ok la mia strada non è forse di rapido da utilizzare in fase di esecuzione, ma funziona per me, in ogni caso ho provato finora. Nel beginnin dei miei proggys ho messo in una funzione per ottenere la dimensione e memorizzarlo in una variabile globale (sì ho sentito che questo sarebbe male, ma io non la cura di questo)

Ecco la spiegazione:

  1. Creare una vista ad albero (invisibile se volete)
  2. Crea un ImageList con atleast 1 immagine all'interno (formato 16x16)
  3. Impostare l'imagelist a TreeView ( "TVSIL_NORMAL")
  4. Prendi l'imagelist "TVSIL_STATE" dalla vista ad albero (u avere per creare "TVSIL_NORMAL" prima, altrimenti questo fallirà!)
  5. Utilizzare ImageList_GetIconSize (..) e memorizzare le dimensioni. Wow, i checkboxs ei pulsanti radio hanno la stessa dimensione come le icone di stato della vista ad albero. Ora u avere ciò che u want!
  6. Distruggere l'imagelist "TVSIL_NORMAL"
  7. Distruggere la vista ad albero

questo codice ha bisogno solo pochi microsecondi, all'inizio dei miei proggies e posso utilizzare il valore di ogni ne ho bisogno.

Risposto il 29/11/2015 a 18:09
fonte dall'utente

voti
0

Premessa:
ho avuto la stessa domanda durante il tentativo di determinare la dimensione necessaria del controllo casella di controllo per un determinato testo e ha scoperto che le risposte esistenti non hanno funzionato veramente per me, per diversi motivi:

  • SM_CXMENUCHECKnon tiene conto del gap. In realtà, io non sono convinto che questo sia anche per le caselle di controllo regolari, anche se può avere lo stesso valore. Può anche essere dipendente da stili visivi di essere abilitati.
  • Le altre risposte erano troppo complicate e sembrava un po 'hacky (senza mancare di rispetto prevista, è MS che non fanno questo facile).
  • Il layout 12DLU dichiarato è stato molto utile, anche se ancora una volta si sente arbitrario senza un sistema metrico su cui contare.
  • Le risposte che ho provato ancora non ha dato un valore di pixel abbastanza alto per fermare il testo casella di controllo da imballaggio.

La mia indagine:
Ho guardato come il vino riproduce il comportamento e ha scoperto che dà anche gli stessi risultati semplicemente assumendo 12DLU. Tuttavia, il testo ancora avvolta a meno che non ho aggiunto un extra di 3 pixel per la larghezza (anche se il testo dovrebbe adattarsi bene anche senza). Ho anche notato che GetTextExtentPoint32produce un valore di 3 per una stringa vuota (hmmm ...)
Spegnimento del BS_MULTILINEstile ovviamente fermato la disposizione del testo. La mia ipotesi è che DrawTextW's parola calcoli di avvolgimento sono imperfetti.
A questo punto ho deciso che la soluzione più semplice era quella di aggiungere solo 1 spazio extra per GetTextExtentPoint32, in modo che non ci sarebbe sicuramente sufficiente di pixel. L'eccessiva stima di un paio di pixel era accettabile per me.

Si noti che tutto questo presuppone l'applicazione si manifesta come DPI a conoscenza. In caso contrario, ho trovato la casella di controllo è apparso molto più grande su alcuni sistemi Windows 7 (non tutti però).

La mia (per lo più del vino) Soluzione:

// 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);
Risposto il 14/12/2016 a 17:46
fonte dall'utente

voti
1

Ci scusiamo per resuscitare questo vecchio thread. Recentemente mi sono trovato chiedendo circa la stessa identica domanda. Attualmente, nessuna delle risposte sopra danno un risultato coerente con Windows 10 per i diversi caratteri e dei corpi, soprattutto in ambienti ad alta DPI.

Invece, sembra che il risultato corretto si ottiene

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

per le dimensioni della casella stessa. E

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

per la larghezza della fessura. Dopo aver provato un sacco di diversi metodi ispirati dai posti di cui sopra, ho trovato L"0"nel dissembly di Comctl32.dll. E mentre sembra uno scherzo per me (non necessariamente una buona), ho il sospetto che sia un retaggio dei vecchi tempi quando questo avrebbe potuto essere una buona approssimazione abbastanza di 2DLU.

Disclaimer: Mentre ho provato il risultato con diversi font e dimensioni diverse su Windows 10, non ho cercato di verificare che essa detiene anche su qualsiasi altra versione (più vecchia) del sistema operativo.

Risposto il 17/12/2019 a 18:00
fonte dall'utente

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