Как конвертировать 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.