开发者

How to convert DLU into pixels?

Microsoft uses dialog length units (DLU) in their guidelines for UI. How can I convert them into pixels?

As I know, DLU depe开发者_如何学编程nding on system font size. Can you advise some simple method of such conversion in Delphi for Win32?


First we start with what a dialog unit is.

For that i'll quote one of my own un-answered questions:

What's a dialog unit?

A dialog is a unit of measure based on the user's preferred font size. A dialog unit is defined such that the average character is 4 dialog units wide by 8 dialog units high:

How to convert DLU into pixels?

This means that dialog units:

  • change with selected font
  • changed with selected DPI setting
  • are not square

i'll also quote another of my own un-answered questions:

You can check the Windows UX Guidelines to see where these measurements come from. The short version is:

  • dlu = dialog unit
  • dlu is based on the font size (items change with user's font size)
  • a horizontal dlu is different from a vertical dlu (dlu's are not square)

This comes from the definition of a dialog unit: the average character is 8dlus high by 4dlus wide.

Georgia 14pt:

How to convert DLU into pixels?

If you use a smaller font (i.e. 8pt Tahoma verses 14pt Georgia), the dlus get smaller:

Segoe UI 9pt:

How to convert DLU into pixels?

Note: You'll notice that resolution (i.e. dpi) has no impact on the discussion.

So what you need is the average size of a character. Microsoft has an official technique for calculating the average character size.

  • average height:

    GetTextMetrics(dc, {var}textMetrics);
    averageHeight := textMetrics.tmHeight;
    
  • average width:

    Measure the string ABCDEFGHIJLKMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz using GetTextExtentPoint32, and divide by 52:

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

So now you need the the size of a horizontal and a vertical dialog units. Remember that a horizontal dialog unit is 1/4 the average character width, and a vertical dlu is 1/8 the average character height:

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;

Note: Any code is released into the public domain. No attribution required.


You should use the MapDialogRect() function.

Pass in a RECT in dialog units, and the equivalent RECT in pixel units is returned. Note that you need a handle to a dialog in order to give MapDialogRect() sufficient context. The function needs to know the font in order to perform the conversion.


In case you are tempted to use GetDialogBaseUnits(), remember what Raymond Chen said, GetDialogBaseUnits is a crock.

As you can guess from the title of this entry, GetDialogBaseUnits is a crock. Since there is no HWND parameter to GetDialogBaseUnits, it doesn't know which dialog box's DLUs you want to retrieve. So it guesses.

And it always guesses wrong.

GetDialogBaseUnits returns the dialog base units for dialog boxes that use the default system font. But nobody uses the default system font any more. It screams "old and dorky". But it remains the default for compatibility reasons. (And therefore so too does GetDialogBaseUnits.)

If you have to calculate pixel dimensions from DLUs, and you don't have a handle to a dialog, then you must use the method outlined here: How To Calculate Dialog Base Units with Non-System-Based Font


However, you made it clear in the comments that, for your problem, you do not actually need to convert from DLUs to pixels. You can use Delphi's built in form scaling to ensure that your forms are sized appropriately for the prevailing font scaling.


Here's C code for converting DLU ↔ pixels:

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


For base value (and naturally, system font) call GetDialogBaseUnits. See also remarks paragraph there for the alternate method of translating dialog units <-> pixels with GetTextMetrics and/or GetTextExtentPoint32 without dialog HWND.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜