如何将Delphi窗体的宽度和高度像素转换为对话框单位?

6
我正在尝试将Delphi窗体的宽度和高度像素计算为准确的对话框单位,以创建使用DIALOGEX语句的.rc(资源脚本)文件。到目前为止,我无法使用Microsoft在此处描述的公式计算正确的对话框单位:https://support.microsoft.com/en-us/help/145994/how-to-calculate-dialog-box-units-based-on-the-current-font-in-visual 上述链接使用GetDialogBaseUnits Win32 API调用,但这不起作用,因为该API函数使用系统字体。所以这是无用的。即使Microsoft也说我们应该改用MapDialogRect。因此,使用此处提供的公式计算非系统字体的对话框基本单位https://support.microsoft.com/en-us/help/125681/how-to-calculate-dialog-base-units-with-non-system-based-font

1个水平对话框基本单位==(2 *平均字符宽度对话框字体/平均字符宽度系统字体)像素

1个垂直对话框基本单位==(2 *平均字符高度对话框字体/平均字符高度系统字体)像素

我尝试了类似于以下内容:
  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);

请注意, handle 来自创建的表单字体句柄。我不确定如何获得 avgSysWidth avgSysHeight 的值。
我已经看了两天了,似乎找不到最简单的方法来获取对话框单位。 我完全错了吗? 更新2: 我的更新函数:
// 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;

给定一个400x200像素的表单,上述函数生成了269x123个DLU。下面是该表单的图片。左边是设计好的400x200像素表单,右边是根据以下内容生成的表单:

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


1
如果您考虑到对话框资源指定了客户端尺寸,而Delphi表单提供了窗口坐标,则该图片看起来是正确的。 - Rob Kennedy
啊,我完全没有想到这个简单的问题。谢谢Rob。我简直不敢相信我竟然忽略了这么小的细节。太棒了。我使用了表单的ClientWidth和ClientHeight,现在它完美地转换了。你们真是太厉害了! - Thomas Jaeger
2个回答

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

DLU(对话框单元)是基于对话框字体大小的。水平 DLU 是对话框字体的平均宽度除以四。垂直 DLU 是字体高度除以八。

另请参见“GetDialogBaseUnits function”中的备注部分,建议使用 MulDiv 函数。


我不得不删除水平和垂直数字的Rounds,因为它产生了错误的结果。尽管如此,我不得不将垂直对话框基本单位乘以10。我已经更新了我的问题,以查看函数的更新示例。 - Thomas Jaeger
你计算平均高度的公式有误。不要除以8,tm.tmHeight是字体高度。 如果你使用整数,应该使用比DLU更大的单位,因为存在较高的舍入误差。使用基本对话框单位,它等于4 x 8 dlu。基本对话框单位等于字体的平均大小。我已编辑了我的回答。 - Daniel Sęk
@Dainiel,使用您更新的公式后,创建的对话框仍然比原始窗体稍微大一些。我的示例窗体为800像素乘以400像素,产生了539 DLU乘以246 DLU。从视觉上来看,我猜测该窗体在宽度和高度上大约大了40-50个像素。仍然有些不对劲。 - Thomas Jaeger
1
对话框模板中的宽度和高度指定了客户端大小(不包括边框)。 - Daniel Sęk
啊,我完全忽略了这个简单的事实。谢谢丹尼尔和罗布。我简直不敢相信我忽略了这个小细节。太棒了。我使用了窗体的ClientWidth和ClientHeight,现在它是一个完美的转换。 - Thomas Jaeger
我认为你仍然会少几个DLU。我的四舍五入方法是错误的。我找到了我的旧源代码(用C++编写),avgWidth被四舍五入为整数“int avg_width = (int)((size.cx + 26) / 52);”,因此avgWidth应该被四舍五入为完整的整数。 - Daniel Sęk

2

GetDialogBaseUnits 并不是那么“无用”的。它会告诉你系统字体的平均高度和宽度(以像素为单位):

avgSysWidth := LoWord(GetDialogBaseUnits);
avgSysHeight := HiWord(GetDialogBaseUnits);

谢谢Rob。我之前确实使用了GetDialogBasedUnits,但它对DLU的计算没有影响。 - Thomas Jaeger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接