【问题标题】:Dialog shifted to the left and upward in Windows 8对话框在 Windows 8 中向左和向上移动
【发布时间】:2015-04-11 09:15:27
【问题描述】:

此对话框正好显示在按钮下方,但在 Windows 8 中,对话框向左并向上移动。如何在所有 Windows 版本中获得相同的结果?

procedure TForm1.Button3Click(Sender: TObject);
var p: TPoint;
begin
  p := Button3.ClientToScreen(Point(0, Button3.Height));
  MessageDlgPos('', mtInformation, [mbOK], 0, p.X, p.Y);
end;

更新: 如果我们打开 Form 而不是 Dialog,并且该 Form 具有 BorderStyle bsSizeable 或 bsSizeToolWin,那么一切正常。否则(bsDialog、bsSingle、bsToolWindow),Form 打开时作为上例中的 Dialog 打开。

【问题讨论】:

  • 听起来像一个 Delphi 库错误。通过判断 ClientToScreen 是否损坏或 MessageDlgPos 是否损坏来诊断它。通过检查坐标来测试前者。如果 ClientToScreen 有效,则 MessageDlgPos 已损坏。
  • @David,我认为这不是其中任何一个错误。例如,在坐标 (0,0) 的情况下,Dialog 或 Form 以相同的方式移动。似乎问题在于 Windows 8 看到位于窗口框架内几个像素的点作为起点。如果是这样,问题是如何以适当的方式进行必要的更正。
  • 哦。好吧,我想你需要在问题中说清楚。一张图片就可以做到这一点。
  • VCL 在 dialogs.pas 中的 TTaskMessageDialog.DoOnDialogCreated 中使用正确的坐标调用 SetWindowPos。这似乎是一个操作系统问题。使用诸如“GetWindowRect 错误值”之类的内容进行搜索,您将找到诸如 stackoverflow.com/questions/3192232/… 之类的示例。请注意,中间和最右侧图片中的对话框也偏离了“1”像素。我提到 GetWindowRect 是因为据报道按钮和对话框的窗口矩形在 W7 上具有相同的“左侧”。您可以对 W8 进行相同的测试并验证是否是操作系统问题。
  • 不仅如此,Aero 还让GetWindowRect() 之类的东西报告“错误”值以说明 DWM 的影响和边界,以免破坏与不考虑 DWM 的代码的向后兼容性。这就是添加DwmGetWindowAttribute(DWMWA_EXTENDED_FRAME_BOUNDS) 以获得“实际”窗口矩形的原因。

标签: delphi winapi windows-8 dialog position


【解决方案1】:

运行您在 Windows 7 上显示的确切代码,我无法重现您在 Windows 7 屏幕截图中显示的相同对话框定位。 MessageDlgPos 窗口以与 Windows 8 屏幕截图相同的方式向上和向左偏移:

话虽如此,我注意到您将MessageDlg 窗口相对于按钮的客户区定位:

如果您希望对话框相对于其实际底部边缘定位,您需要在按钮的Parent 上调用ClientToScreen() 而不是按钮本身:

p := Button3.Parent.ClientToScreen(Point(Button3.Left, Button3.Top+Button3.Height));

但最终结果大致相同:

现在,为什么首先会发生重叠?因为窗口的定位使得其非客户区的左上角落在指定的坐标处:

您可以调整窗口坐标来解决这个问题:

p := Button3.Parent.ClientToScreen(Point(Button3.Left, Button3.Top + Button3.Height));
Inc(p.X, GetSystemMetrics(SM_CXFIXEDFRAME) + GetSystemMetrics(SM_CXBORDER));
Inc(p.Y, GetSystemMetrics(SM_CYFIXEDFRAME) + GetSystemMetrics(SM_CYBORDER));

这会让你更接近想要的位置:

请注意,Aero 会稍微“调整”系统指标,因此您可能需要使用 DwmGetWindowAttribute(DWMWA_EXTENDED_FRAME_BOUNDS) 和/或 GetThemeSysSize() 之类的东西来获得更准确的指标。

【讨论】:

  • - "虽然最终结果大致相同" - 按钮控件没有非客户区,这就是原因。使用边界矩形没有意义。
【解决方案2】:

在您的回答和 cmets 以及一些额外的研究之后,我得出了这个解决方案。在 Windows 8、带 Aero 的 7、不带 Aero 的 7 和 XP 上进行了测试。我希望有一些更简单和稳定的东西,但是......

uses DwmApi;

type
  TNonClientMetricsX = packed record
    cbSize: UINT;
    iBorderWidth: Integer;       iScrollWidth: Integer;
    iScrollHeight: Integer;      iCaptionWidth: Integer;
    iCaptionHeight: Integer;     lfCaptionFont: TLogFontA;
    iSmCaptionWidth: Integer;    iSmCaptionHeight: Integer;
    lfSmCaptionFont: TLogFontA;  iMenuWidth: Integer;
    iMenuHeight: Integer;        lfMenuFont: TLogFontA;
    lfStatusFont: TLogFontA;     lfMessageFont: TLogFontA;
    iPaddedBorderWidth: Integer; // not defined in Delphi 2007
  end;

function GetExtendedFrameOffset(BorderStyle: TFormBorderStyle): integer;
var
  IsEnabled: BOOL;
  NCM: TNonClientMetricsX;
begin
  Result := 0;
  if (DwmIsCompositionEnabled(IsEnabled) = S_OK) and IsEnabled and
     (BorderStyle in [bsdialog, bsSingle, bsToolWindow]) then
  begin
    NCM.cbSize := SizeOf(NCM);
    SystemParametersInfo(SPI_GETNONCLIENTMETRICS, SizeOf(NCM), @NCM, 0);
    Result := NCM.iBorderWidth + NCM.iPaddedBorderWidth;
  end;
end;

procedure TForm1.Button3Click(Sender: TObject);
var p: TPoint; offset: integer;
begin
  p := Button3.ClientToScreen(Point(0, Button3.Height));
  offset := GetExtendedFrameOffset(bsDialog);
  MessageDlgPos('', mtInformation, [mbOK], 0, p.X + offset, p.Y + offset);
end;

更新:D2007 包含 DwmApi,因此不需要复杂的 LoadLibrary

【讨论】:

  • 我会让 windows 将对话框放置在默认位置。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多