Delphi 对话框在Windows 8中向左和向上移动

Delphi 对话框在Windows 8中向左和向上移动,delphi,winapi,windows-8,dialog,position,Delphi,Winapi,Windows 8,Dialog,Position,此对话框正好显示在按钮下方,但在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;

此对话框正好显示在按钮下方,但在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;
更新:
如果我们打开窗体而不是对话框,并且该窗体具有BorderStyle BSSizeTable或bsSizeToolWin,则一切正常。否则,bsDialog、bsSingle、BSoolWindow、Form将作为上述示例中的对话框打开。

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

也就是说,我注意到您正在相对于按钮的客户端区域定位MessageDlg窗口:

如果希望对话框相对于其实际底部边缘定位,则需要在按钮的父级而不是按钮本身上调用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稍微调整了系统指标,因此您可能需要使用和/或之类的方法来获得更精确的指标。

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

也就是说,我注意到您正在相对于按钮的客户端区域定位MessageDlg窗口:

如果希望对话框相对于其实际底部边缘定位,则需要在按钮的父级而不是按钮本身上调用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稍微调整了系统指标,因此您可能需要使用和/或之类的方法来获得更准确的指标。

在您的回答和评论以及一些额外的研究之后,我选择了此解决方案。在Windows8上测试,7带有Aero,7没有Aero和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

在您的回答和评论以及一些额外的研究之后,我选择了这个解决方案。在Windows8上测试,7带有Aero,7没有Aero和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

听起来像是Delphi库错误。通过确定ClientToScreen是否损坏或MessageDlgPos是否损坏来诊断。通过检查坐标来测试前者。如果ClientToScreen正常工作,那么MessageDlgPos就坏了。@David,我想这不是任何一个错误。例如,对话框或窗体在坐标0,0的情况下以相同的方式移动。问题似乎在于Windows 8将位于窗口框架内几个像素处的点作为起点。如果是这样,问题是如何以适当的方式进行必要的纠正。嗯,我想你需要在问题中说清楚。一个映像可以做到这一点。VCL正在调用SetWindowPos,在dialogs.pas中创建的TTaskMessageDialog.Doondialog中使用正确的坐标。这似乎是操作系统的问题。使用“GetWindowRect错误值”之类的搜索,您会遇到类似的示例。请注意,最中间和最右边图片中的对话框也以“1”像素关闭。我提到GetWindowRect是因为按钮的窗口矩形和对话框的窗口矩形在W7上报告为相同的“左”。您可以对W8执行相同的测试,并验证它是否是操作系统问题。除此之外,Aero还让GetWindowRect等报告错误的值,以说明DWM影响和边界,从而不会破坏与不说明DWM的代码的向后兼容性。这就是为什么添加了DwmGetWindowAttributeDWMWA_EXTENDED_FRAME_BOUNDS来获取实际的窗口矩形。听起来像是Delphi库的错误。通过确定ClientToScreen是否损坏或MessageDlgPos是否损坏来诊断。通过检查坐标来测试前者。如果ClientToScreen正常工作,那么MessageDlgPos就坏了。@David,我想这不是任何一个错误。例如,对话框或窗体在坐标0,0的情况下以相同的方式移动。问题似乎是Windows 8看到了所定位的点
窗口框架内的两个像素作为起点。如果是这样,问题是如何以适当的方式进行必要的纠正。嗯,我想你需要在问题中说清楚。一个映像可以做到这一点。VCL正在调用SetWindowPos,在dialogs.pas中创建的TTaskMessageDialog.Doondialog中使用正确的坐标。这似乎是操作系统的问题。使用“GetWindowRect错误值”之类的搜索,您会遇到类似的示例。请注意,最中间和最右边图片中的对话框也以“1”像素关闭。我提到GetWindowRect是因为按钮的窗口矩形和对话框的窗口矩形在W7上报告为相同的“左”。您可以对W8执行相同的测试,并验证它是否是操作系统问题。除此之外,Aero还让GetWindowRect等报告错误的值,以说明DWM影响和边界,从而不会破坏与不说明DWM的代码的向后兼容性。这就是为什么添加DwmGetWindowAttributeDWMWA_EXTENDED_FRAME_BOUNDS以获得实际窗口矩形。-尽管最终结果大致相同-按钮控件没有非客户端区域,这就是为什么。使用bounds rect没有意义。-尽管最终结果大致相同-按钮控件没有非客户端区域,这就是为什么。使用bounds rect没有意义。我让windows将对话框放置在默认位置。我让windows将对话框放置在默认位置。