我有一个使用TOpenDialog让用户选择文件的Delphi应用程序。默认情况下,打开的对话框显示在当前监视器的中央,如今,它距应用程序窗口“英里”。我希望对话框以TOpenDialog的所有者控件为中心显示,否则,我将选择应用程序的主窗口。

以下代码类型的作品是从TJvOpenDialog派生的,它给了我一些有关如何执行的提示:

type
  TMyOpenDialog = class(TJvOpenDialog)
  private
    procedure SetPosition;
  protected
    procedure DoFolderChange; override;
    procedure WndProc(var Msg: TMessage); override;
  end;

procedure TMyOpenDialog.SetPosition;
begin
var
  Monitor: TMonitor;
  ParentControl: TWinControl;
  Res: LongBool;
begin
  if (Assigned(Owner)) and (Owner is TWinControl) then
    ParentControl := (Owner as TWinControl)
  else if Application.MainForm <> nil then
    ParentControl := Application.MainForm
  else begin
    // this code was already in TJvOpenDialog
    Monitor := Screen.Monitors[0];
    Res := SetWindowPos(ParentWnd, 0,
      Monitor.Left + ((Monitor.Width - Width) div 2),
      Monitor.Top + ((Monitor.Height - Height) div 3),
      Width, Height,
      SWP_NOACTIVATE or SWP_NOZORDER);
    exit; // =>
  end;
  // this is new
  Res := SetWindowPos(GetParent(Handle), 0,
    ParentControl.Left + ((ParentControl.Width - Width) div 2),
    ParentControl.Top + ((ParentControl.Height - Height) div 3),
    Width, Height,
    SWP_NOACTIVATE or SWP_NOZORDER);
end;

procedure TMyOpenDialog.DoFolderChange
begin
  inherited DoFolderChange;  // call inherited first, it sets the dialog style etc.
  SetPosition;
end;

procedure TMyOpenDialog.WndProc(var Msg: TMessage);
begin
  case Msg.Msg of
    WM_ENTERIDLE: begin
      // This has never been called in my tests, but since TJVOpenDialog
      // does it I figured there may be some fringe case which requires
      // SetPosition being called from here.
      inherited; // call inherited first, it sets the dialog style etc.
      SetPosition;
      exit;
    end;
  end;
  inherited;
end;


“种类繁多的作品”是指第一次打开对话框时,它将在所有者表单上居中显示。但是,如果我随后关闭对话框,移动窗口并再次打开对话框,即使SetWindowPos返回true,也似乎没有任何作用。对话框在与第一次相同的位置打开。

这是在Windows XP上运行的Delphi 2007中,而目标框也在Windows XP上运行。

最佳答案

TJvOpenDialogTOpenDialog的后代,因此您应该在VCL居中对话框之后运行放置调用。 VCL响应CDN_INITDONE通知执行此操作。响应WM_SHOWWINDOW消息为时过早,在我的测试中,窗口过程从未收到WM_ENTERIDLE消息。

uses
  commdlg;

[...]

procedure TJvOpenDialog.DoFolderChange;
begin
  inherited DoFolderChange;
//  SetPosition; // shouldn't be needing this, only place the dialog once
end;

procedure TJvOpenDialog.WndProc(var Msg: TMessage);
begin
  case Msg.Msg of
    WM_NOTIFY: begin
      if POFNotify(Msg.LParam)^.hdr.code = CDN_INITDONE then begin
        inherited;    // VCL centers the dialog here
        SetPosition;  // we don't like it ;)
        Exit;
      end;
  end;
  inherited;
end;


要么,

procedure TJvOpenDialog.WndProc(var Msg: TMessage);
begin
  case Msg.Msg of
    WM_NOTIFY: if POFNotify(Msg.LParam)^.hdr.code = CDN_INITDONE then
                 Exit;
  end;
  inherited;
end;


在操作系统放置对话框的地方,这确实很有意义。

07-26 09:25