У меня есть флажок, который я хочу точно измерить, чтобы я мог правильно позиционировать элементы управления в диалоговом окне. Я могу легко измерить размер текста в элементе управления, но я не знаю "официального" способа расчета размера флажка и пробела до (или после) текста.
Как получить размер проверки и пробел в флажке?
Ответ 1
Я уверен, что ширина флажка равна
int x = GetSystemMetrics( SM_CXMENUCHECK );
int y = GetSystemMetrics( SM_CYMENUCHECK );
Затем вы можете выработать область внутри, вычитая следующее...
int xInner = GetSystemMetrics( SM_CXEDGE );
int yInner = GetSystemMetrics( SM_CYEDGE );
Я использую это в своем коде и до сих пор не имел проблемы...
Ответ 2
Короткий ответ:
Длинная версия
Из MSDN Спецификации макета: Win32, у нас есть спецификации размеров флажка.
От левого края элемента управления до начала текста 12 диалоговых блоков:
И элемент управления флажком имеет 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);
Использование удобной вспомогательной функции:
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;
Примечание. Любой код, выпущенный в общественное достояние. Не требуется атрибуция.
Ответ 3
Стыдно, что Microsoft не предоставила возможность точно знать это. Я боролся с тем же вопросом, и приведенный выше ответ не был полным. Основная проблема заключается в том, что если шрифт диалогового окна имеет значение, отличное от размера по умолчанию, это решение не будет работать, потому что флажки будут изменены.
Вот как я решил эту проблему (это просто приближение, которое, похоже, сработало для меня). Код предназначен для проекта MFC.
1 - Создайте два тестовых элемента управления в форме, флажок и поле для радио:
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 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 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
Этот код не работает на Win7 с масштабированным пользовательским интерфейсом (шрифты на 125% больше или на 150% больше). Единственное, что работает, это:
int WID = 13 * dc.GetDeviceCaps(LOGPIXELSX) / 96;
int HEI = 13 * dc.GetDeviceCaps(LOGPIXELSY) / 96;
Ответ 5
Ok dudes мой путь - это, возможно, не тот пост, который нужно использовать во время исполнения, но он работает для меня в любом случае, который я тестировал до сих пор. В начале моих proggys я помещаю функцию, чтобы получить размер и сохранить его в глобальной переменной (да, я слышал, что это будет плохо, но я не забочусь об этом)
здесь объяснение:
- Создайте древовидное представление (невидимое, если хотите)
- Создайте imagelist с наименьшим 1 изображением внутри (размер 16x16).
- Установите образчик в дерево ( "TVSIL_NORMAL" )
- Получите "imagelist" TVSIL_STATE из древовидного изображения (вам нужно создать "TVSIL_NORMAL" раньше, иначе это не удастся!)
- Используйте ImageList_GetIconSize (..) и сохраните размер. Ничего себе, флажки и радиокнопки имеют тот же размер, что и значки состояния дерева. Теперь у вас есть то, что вам нужно!
- Уничтожьте имидж "TVSIL_NORMAL"
- Уничтожить древовидную структуру
этот код нуждается только в нескольких микросекундах в начале моих программ, и я могу использовать значение каждый раз, когда мне это нужно.
Ответ 6
Преамбула:
У меня был тот же вопрос, пытаясь определить необходимый размер элемента управления флажком для данного текста и обнаружил, что существующие ответы на самом деле не работают для меня по нескольким причинам:
-
SM_CXMENUCHECK
не учитывает пробел. На самом деле, я не уверен, что это даже для обычных флажков, хотя может иметь такое же значение. Он также может зависеть от того, включены ли визуальные стили. - Другие ответы были чрезмерно сложными и были немного взломаны (никакого неуважения не было, MS это не делает это легко).
- Указанный макет 12DLU был очень полезен, хотя он снова чувствует себя произвольно без использования метрики системы.
- Ответы, которые я пробовал, еще не дали достаточно высокого значения пикселей, чтобы остановить обертку текста флажка.
Мое расследование:
Я посмотрел, как Wine воспроизводит поведение и обнаружил, что он также дает те же результаты, что и просто предположить 12DLU. Тем не менее, текст по-прежнему завернут, если я не добавил дополнительные 3 пикселя к ширине (даже если текст должен соответствовать штрафу без). Я также заметил, что GetTextExtentPoint32
дает значение 3 для пустой строки (hmmm...)
Отключение стиля BS_MULTILINE
явно остановило перенос текста. Мое предположение состоит в том, что вычисления DrawTextW
переноса слов несовершенны.
На этом этапе я решил, что самым простым решением было просто добавить 1 дополнительное пространство в GetTextExtentPoint32
, так что определенно будет достаточно пикселей. Для меня приемлема переоценка пары пикселей.
Обратите внимание, что это все предполагает, что ваше приложение проявляется как DPI. В противном случае я обнаружил, что флажок был намного больше на некоторых системах Windows 7 (не все).
Решение My (главным образом Wine):
// 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);