Как конвертировать DLU в пиксели?

Microsoft использует единицы измерения длины диалогов (DLU) в своих рекомендациях для пользовательского интерфейса. Как я могу конвертировать их в пиксели?

Как я знаю, DLU зависит от размера системного шрифта. Можете ли вы посоветовать какой-нибудь простой способ такого преобразования в Delphi для Win32?

4 ответа

Решение

Вы должны использовать MapDialogRect() функция.

Пройти в RECT в диалоговых единицах и аналогичных RECT в пикселях возвращается. Обратите внимание, что вам нужен дескриптор для диалога, чтобы дать MapDialogRect() достаточный контекст. Функция должна знать шрифт, чтобы выполнить преобразование.


В случае, если вы склонны использовать GetDialogBaseUnits() Помните, что сказал Раймонд Чен, GetDialogBaseUnits - это черепок.

Как вы можете догадаться из названия этой записи, GetDialogBaseUnits - это черепок. Поскольку в GetDialogBaseUnits отсутствует параметр HWND, он не знает, какие DLU диалогового окна вы хотите получить. Так что это догадки.

И всегда угадывает неправильно.

GetDialogBaseUnits возвращает базовые единицы диалога для диалоговых окон, которые используют системный шрифт по умолчанию. Но никто больше не использует системный шрифт по умолчанию. Кричит "старый и тупой". Но это остается значением по умолчанию для совместимости. (И поэтому то же самое делает GetDialogBaseUnits.)

Если вам нужно вычислить размеры в пикселях из DLU, и у вас нет дескриптора для диалога, то вы должны использовать метод, описанный здесь: Как рассчитать единицы базы диалога с несистемным шрифтом


Тем не менее, в комментариях вы четко указали, что для вашей проблемы вам на самом деле не нужно конвертировать из DLU в пиксели. Вы можете использовать встроенное в Delphi масштабирование форм, чтобы убедиться, что ваши формы имеют соответствующий размер для преобладающего масштабирования шрифтов.

Сначала мы начнем с того, что такое диалоговое устройство.

Для этого я процитирую один из моих собственных неотвеченных вопросов:

Что такое блок диалога?

Диалог - это единица измерения, основанная на предпочтительном размере шрифта пользователя. Диалоговая единица определяется так, что средний символ составляет 4 единицы ширины и 8 единиц высоты:

Это означает, что диалоговые единицы:

  • изменить с выбранным шрифтом
  • изменено с выбранной настройкой DPI
  • не квадратные

Я также процитирую еще один из моих вопросов без ответов:

Вы можете проверить Руководство по Windows UX, чтобы увидеть, откуда берутся эти измерения. Краткая версия:

  • dlu = диалоговое окно
  • dlu зависит от размера шрифта (элементы меняются в зависимости от размера шрифта пользователя)
  • горизонтальный dlu отличается от вертикального dlu (dlu не являются квадратными)

Это происходит из определения диалоговой единицы: средний символ имеет высоту 8dlus и ширину 4dlus.

Грузия 14pt:

Если вы используете шрифт меньшего размера (например, стихи из стиха Tapt 10pt 14pt из Грузии), dlus становится меньше:

Segoe UI 9pt:

Примечание. Вы заметите, что разрешение (т.е. dpi) не влияет на обсуждение.

Итак, что вам нужно, это средний размер персонажа. У Microsoft есть официальная методика расчета среднего размера символов.

  • средний рост:

    GetTextMetrics(dc, {var}textMetrics);
    averageHeight := textMetrics.tmHeight;
    
  • средняя ширина:

    Измерьте строку ABCDEFGHIJLKMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz, используя GetTextExtentPoint32 и разделите на 52:

    GetTextExtentPoint32(dc,
          PChar('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'), 52, Size));
    averageWidth := size.cx / 52.0;
    

Так что теперь вам нужен размер горизонтального и вертикального диалоговых блоков. Помните, что горизонтальная диалоговая единица составляет 1/4 средней ширины символа, а вертикальная длина равна 1/8 средней высоты символа:

procedure GetDlus(dc: HDC; out HorizontalDluSize, VerticalDluSize: Real);
var
   tm: TTextMetric; 
   size: TSize;
begin
   GetTextMetric(dc, tm);
   VerticalDluSize := tm.tmHeight / 8.0;

   GetTextExtentPoint32(dc,
         PChar('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'), 52,
         size);
   HorizontalDluSize := size.cx / 52.0;
end;

Примечание. Любой код публикуется в открытом доступе. Атрибуция не требуется.

Вот код C для преобразования DLU ↔ пикселей:

      HWND hDlg = ...;                   // The handle to the dialog

LPDLGTEMPLATE *dlgTemplate = ...;  // The template for the same dialog
SIZE dlgSize;                      // Only needed for converting DLU -> pixels
if (dlgTemplate->style == 0xFFFF0001)
{
    dlgSize.cx = ((DLGTEMPLATEEX *)dlgTemplate)->cx;
    dlgSize.cy = ((DLGTEMPLATEEX *)dlgTemplate)->cy;
}
else
{
    dlgSize.cx = dlgTemplate->cx;
    dlgSize.cy = dlgTemplate->cy;
}

RECT rc = { 0, 0, 4, 8 };
MapDialogRect(hDlg, &rc);

// To convert dlgSize to pixels, use:
SIZE wndSize = { dlgSize.cx * rc.right / 4, dlgSize.cy * rc.bottom / 8 };

// To convert wndSize to DLUs, use:
SIZE dlgSize2 = { size.cx * 4 / rc.right, size.cy * 8 / rc.bottom };
assert(dlgSize1 == dlgSize2);

Для базового значения (и, естественно, системного шрифта) вызов GetDialogBaseUnits, Смотрите также remarks абзац там для альтернативного метода перевода диалоговых единиц <-> пикселей с GetTextMetrics и / или GetTextExtentPoint32 без диалога HWND.

Другие вопросы по тегам