Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert a delphi form's width and height pixels into dialog units?

Tags:

winapi

delphi

I'm trying to calculate a Delphi form's width and height pixels into accurate dialog units to create a .rc (resource script) file that uses a DIALOGEX statement. So far, I have not been able to calculate the correct dialog units using Microsoft's formula described here: https://support.microsoft.com/en-us/help/145994/how-to-calculate-dialog-box-units-based-on-the-current-font-in-visual

The above link uses the GetDialogBaseUnits Win32 API call but this does not work since that API function uses the system font. So this is useless. Even Microsoft says that we should use MapDialogRect instead. So, using the formula provided here on how to calculate dialog base units with non-system-based fonts https://support.microsoft.com/en-us/help/125681/how-to-calculate-dialog-base-units-with-non-system-based-font

1 horz dialog base unit == (2 * average char width dialog font / average char width system font) pixels

1 vert dialog base unit == (2 * average char height dialog font / average char height system font) pixels

I have tried something like this:

  xPixels := 200
  yPixels := 50;

  dc := GetDC(0);

  SelectObject(dc,handle);  // handle := f.Font.Handle  (f := TForm created)
  if not GetTextMetrics(dc, tm) then
    ShowMessage('Error');
  avgHeight := tm.tmHeight / 8.0;

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

  avgSysWidth := 5;   // Made up value, how to calculate?
  avgSysHeight := 1;  // Made up value, how to calculate?

  horzPixels := Round(2 * 1 * (avgWidth / avgSysWidth));
  vertPixels := Round(2 * 1 * (avgHeight / avgSysHeight));

  HorizontalDialogBaseUnit := Round(xPixels / horzPixels);
  VerticalDialogBaseUnit := Round(yPixels / vertPixels);

Note that handle is from the created forms Font handle. The avgSysWidth and avgSysHeight I'm not sure how to get to those values.

I've been looking at this for two days and can't seem to see the simplest way to get to the dialog units. Am I completely off?

Update 2: My updated function:

// xPixesl := 400
// yPixels := 200
procedure Tdm.GetDlgBaseUnits(handle: HWND; xPixels, yPixels: integer; out HorizontalDLUs, VerticalDLUs: integer);
var
  dc: HDC;
  tm: TTextMetric;
  size: TSize;
  avgWidth, avgHeight: real;
  VerticalDlu, HorizontalDlu: real;
//  DialogUnits: Cardinal;
begin
//  DialogUnits := GetDialogBaseUnits;
  dc := GetDC(0);

  SelectObject(dc,handle);
  GetTextMetrics(dc, tm);
  avgHeight := tm.tmHeight;

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

  HorizontalDLUs := Round( (4 * xPixels) / avgWidth );
  VerticalDLUs := Round( (8 * yPixels) / avgHeight );

//  HorizontalDLUs := Round( (xPixels / (avgWidth * LOWORD(DialogUnits))));
//  VerticalDLUs := Round( (yPixels / (avgHeight * HIWORD(DialogUnits))));
end;

Given a 400z200 pixel form, the above function produces 269 by 123 DLUs. Here is a picture of the form. The left one is the designed form in 400x200 pixels and the right one is produced and created form based on:

DesignForm DIALOGEX 0, 0, 269, 123
STYLE  WS_CAPTION | WS_SIZEBOX | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP
CAPTION "DesignForm"
CLASS "DLGCLASS"
FONT 8, "Tahoma"
{
} 

enter image description here

like image 774
Thomas Jaeger Avatar asked Sep 29 '17 15:09

Thomas Jaeger


2 Answers

GetTextMetrics(dc, tm);
avgHeight := tm.tmHeight;

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

HorizontalDLUs := Round( (4 * xPixels) / avgWidth );
VerticalDLUs := Round( (8 * yPixels) / avgHeight );

A DLU is based on the size of the dialog box font. A horizontal DLU is the average width of the dialog box font divided by four. A vertical DLU is the height of the font divided by eight.

Also see remarks section in GetDialogBaseUnits function with recomendation to use MulDiv function.

like image 88
Daniel Sęk Avatar answered Oct 11 '22 07:10

Daniel Sęk


GetDialogBaseUnits isn't actually so "useless." It will tell you the average height and width of the system font in pixels:

avgSysWidth := LoWord(GetDialogBaseUnits);
avgSysHeight := HiWord(GetDialogBaseUnits);
like image 39
Rob Kennedy Avatar answered Oct 11 '22 07:10

Rob Kennedy