Jak zdobyć rozmiar czeku i luki w polu wyboru?

głosy
12

Mam pole wyboru, że chcę, aby dokładnie zmierzyć, więc mogę umieścić kontroli na dialog poprawnie. Mogę łatwo zmierzyć rozmiar tekstu na kontrolę - ale ja nie wiem „oficjalny” sposób obliczania wielkości pole wyboru i luki przed (lub po) tekstu.

Utwórz 22/07/2009 o 13:18
źródło użytkownik
W innych językach...                            


7 odpowiedzi

głosy
12

Jestem prawie pewien, szerokość wyboru jest równa

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

Następnie można wypracować obszar wewnątrz poprzez odjęcie następujących ...

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

Używam że w moim kodu i nie miałem problemu do tej pory ...

Odpowiedział 22/07/2009 o 13:53
źródło użytkownik

głosy
0

Ten kod nie działa na Win7 z skalowane czcionki UI (125% lub 150% większe powiększenie). Jedyną rzeczą, która wydaje się działać to:

int WID = 13 * dc.GetDeviceCaps(LOGPIXELSX) / 96; 
int HEI = 13 * dc.GetDeviceCaps(LOGPIXELSY) / 96;
Odpowiedział 20/12/2011 o 11:02
źródło użytkownik

głosy
1

To wstyd, że Microsoft nie zapewnia sposób to wiedzieć na pewno. Miałem problemy z tym samym pytaniem i odpowiedzią podane powyżej nie jest kompletna. Głównym problemem jest to, że jeśli czcionka okna dialogowego jest ustawiony na coś innego niż domyślny rozmiar, że rozwiązanie to nie będzie działać, ponieważ pola wyboru zostaną przeskalowane.

Oto jak rozwiązać ten problem (jest tylko przybliżeniem, że wydaje się, że pracował dla mnie). Kod jest dla projektu MFC.

1 - Utwórz dwie kontrole testów na formularzu, pole wyboru oraz pole radiowej:

wprowadzić opis obrazu tutaj

2 - Definiowanie niestandardowego struct następujące:

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

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

3 - Call następujący kod gdy forma inicjuje dla każdego z testów kontrolnych (które będą je mierzyć i usunąć je tak, aby użytkownicy końcowi nie wydają je):

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 - Teraz można łatwo zmienić rozmiar dowolnego wyboru lub pola radiowego, wywołując w ten sposób:

//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;
}
Odpowiedział 09/06/2013 o 03:06
źródło użytkownik

głosy
7

Krótka odpowiedź:

wprowadzić opis obrazu tutaj

Długa wersja

Od MSDN specyfikacji układ: Win32 , mamy specyfikacje wymiarów wyboru.

Jest to 12 jednostek dialogowych od lewej krawędzi kontroli na początku tekstu:

wprowadzić opis obrazu tutaj

I kontrola wyboru jest 10 jednostek dialogowych wysoki:

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

Najpierw musimy obliczyć wielkość poziomej i pionowej jednostki dialogowym:

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

Korzystanie z funkcji pomocnika poręczny:

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

Teraz, gdy wiemy, ile pikseli ( checkboxSpacing), aby dodać, możemy obliczyć wielkość etykiety jako normalny:

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;

wprowadzić opis obrazu tutaj

Uwaga : każdy kod wydany w domenie publicznej. Nie wymaga przypisanie.

Odpowiedział 04/01/2014 o 21:27
źródło użytkownik

głosy
0

Ok kolesie mój sposób nie jest być może fastes do wykorzystania w czasie wykonywania, ale to działa na mnie w każdym razie ja testowałem do tej pory. W beginnin moich proggys i umieścić w funkcji, aby uzyskać rozmiar i przechowywać ją w zmiennej globalnej (tak słyszałem to byłoby złe, ale ja nie dbają o to)

Oto wyjaśnienie:

  1. Tworzenie katalogów (niewidoczny jeśli u chcą)
  2. Załóż imagelist z conajmniej 1 obraz wewnątrz (rozmiar 16x16)
  3. Ustaw imagelist do katalogów ( „TVSIL_NORMAL”)
  4. Uzyskać „TVSIL_STATE” imagelist z katalogów (u trzeba tworzyć „TVSIL_NORMAL” przed, w przeciwnym razie ten jeden zawiedzie!)
  5. Użyj ImageList_GetIconSize (..) i przechowywać rozmiar. Wow, to checkboxs a radio-przyciski mają ten sam rozmiar co ikon państwowych katalogów. Teraz u mają co u chcą!
  6. Zniszczyć „TVSIL_NORMAL” imagelist
  7. Zniszczyć TreeView

Ten kod wymaga tylko kilka mikrosekund na początku moich proggies i mogę użyć wartości za każdym razem jest mi potrzebny.

Odpowiedział 29/11/2015 o 18:09
źródło użytkownik

głosy
0

Preambuła:
Miałem to samo pytanie, starając się ustalić potrzebną wielkość kontroli pole wyboru dla danego tekstu i okazało się, że istniejące odpowiedzi tak naprawdę nie działa na mnie, z kilku powodów:

  • SM_CXMENUCHECKnie uwzględnia szczeliny. W rzeczywistości, nie jestem przekonany, to nawet dla zwykłych wyboru, chociaż może mieć taką samą wartość. Może być również uzależniona od ich włączony style wizualne.
  • Pozostałe odpowiedzi były zbyt skomplikowane i czuł się nieco hacky (brak szacunku przeznaczony jest MS, że nie robią to łatwe).
  • Podany układ 12DLU był bardzo pomocny, chociaż znowu czuje arbitralny bez systemu metrycznego, aby polegać.
  • Odpowiedzi próbowałem jeszcze nie dały wystarczająco wysoką wartość piksela w celu zatrzymania tekstu pole wyboru z opakowania.

Moje dochodzenie:
Spojrzałem na jak wino powiela zachowanie i stwierdził, że to także daje takie same wyniki jak po prostu zakładając 12DLU. Jednak tekst nadal owinięte chyba dodałem dodatkowe 3 pikseli szerokości (chociaż tekst powinien pasować dobrze bez). Zauważyłem również, że GetTextExtentPoint32daje wartość 3 dla pusty ciąg (hmmm ...)
Wyłączanie BS_MULTILINEstyl oczywiście zatrzymał zawijanie tekstu. Moje przypuszczenie jest to, że DrawTextW„obliczenia zawijania s słowne są niedoskonałe.
W tym momencie zdecydowałem, że najprostszym rozwiązaniem było po prostu dodać 1 dodatkowe miejsce do GetTextExtentPoint32, tak, że na pewno będzie wystarczająca pikseli. Nadmierne oszacowanie kilka pikseli był do zaakceptowania dla mnie.

Zauważ, że to wszystko zakłada aplikacja objawia się jako DPI świadomy. Inaczej uważam, że o wiele większe pole pojawiła się na niektórych systemach Windows 7 (choć nie wszystkie).

My (głównie Wine) rozwiązanie:

// 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);
Odpowiedział 14/12/2016 o 17:46
źródło użytkownik

głosy
1

Niestety dla wskrzesza ten stary wątek. Niedawno znalazłem się zastanawiać, o dokładnie tej samej kwestii. Obecnie żadna z odpowiedzi powyżej dają wynik zgodny z systemem Windows 10 różnych czcionek i rozmiarów czcionek, zwłaszcza w środowiskach o wysokiej rozdzielczości.

Zamiast tego wydaje się, że prawidłowy wynik uzyskuje się poprzez

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

dla rozmiaru samego wyboru. I

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

dla szerokości szczeliny. Po wypróbowaniu wielu różnych metod zainspirowanych powyższych stanowiskach, znalazłem L"0"w dissembly z pliku COMCTL32.DLL. I choć wygląda jak żart do mnie (nie koniecznie jeden dobry), podejrzewam, że to pozostałość po dawnych czasach, kiedy może to być wystarczająco dobrym przybliżeniem 2DLU.

Zastrzeżenie: A ja testowałem wynik z różnymi czcionkami i różnych rozmiarach w systemie Windows 10, nie próbowali sprawdzić, czy posiada ona również na każdej innej (starszej) wersji systemu operacyjnego.

Odpowiedział 17/12/2019 o 18:00
źródło użytkownik

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