Einzelnen Beitrag anzeigen

Benutzerbild von KodeZwerg
KodeZwerg

Registriert seit: 1. Feb 2018
3.691 Beiträge
 
Delphi 11 Alexandria
 
#9

AW: TFileOpenDialog öffnet immer auf MainMonitor

  Alt 9. Sep 2020, 08:32
Ich habe Dein Projekt "repariert".
Im Anhang ist Projekt Quelltext plus Kompilat zum sofort Testen, bei mir klappt es mit allen vier Knöpfe das der Dialog sich auf das Aufrufer-Formular legt.

3 x standard methoden, ich musste lediglich dem Execute() noch ein Handle mitgeben, schon war das Ziel erreicht.
Das war mir noch nicht genug, also forstete ich in meiner Sammlung nach brauchbaren und bin fündig geworden.
Ich war so frei TOpen-/SaveDialog zu intercepten, so das man nun auch X und Y koordinaten angeben kann.

Hier die Quelltexte falls der Anhang flöten geht:

Delphi-Quellcode:
unit TestForm;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Themes;

type
  TForm1 = class(TForm)
    FileOpenDialog1: TFileOpenDialog;
    Button1: TButton;
    Label1: TLabel;
    Button2: TButton;
    Label2: TLabel;
    Button3: TButton;
    Label3: TLabel;
    Label4: TLabel;
    Button4: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

(*
  Aufgabe: Dialog soll auf dem Formular plaziert sein.
  Lösung: Nutzung der Window-Handle im Execute() Aufruf.
  Notlösung: Nutzung einer frei wählbaren Position per x und y
  Alternativ: Auf passende Message reagieren?
              (evtl über WM_NOTIFY tiefer rein
              oder über WM_SHOWWINDOW eher global?
              dies sind nur vermutungen
              da die erste änderung bereits zum ziel führte.)
Bemerkungen: Alle varianten haben den gleichen Nachteil.
  Die positionierung funktioniert nur beim ersten Aufruf wie erwartet.
  Ab da an übernimmt BigBrother die automatik bis man das HauptFormular
  Fenster etwas verschiebt, dann geht das Spiel von vorne los.
*)


uses
// Winapi.CommDlg // for hacking into dialogs; bind and use this unit
  dlgPos // Positionierbarer Dialog rein-intercepted
  ;


// der hier benötigt eine handle angabe damit er sich gut dazu positionieren kann
procedure TForm1.Button1Click(Sender: TObject);
begin
// if FileOpenDialog1.Execute() then
  if FileOpenDialog1.Execute(Application.ActiveFormHandle) then
    Label1.Caption := FileOpenDialog1.FileName
    else
    Label1.Caption := 'canceled';
end;


(* Dynamisches Testen von Dialogen *)

// der hier erscheint immer gut positioniert bei mir ohne handle angabe
procedure TForm1.Button2Click(Sender: TObject);
var
  Dlg: TOpenDialog; // maintype probe
begin
  Dlg := TOpenDialog.Create(nil);
  // da Dein FileOpenDialog1 über keinerlei Grundeinstellungen verfügt
  // setze ich auch keine :-)
  // ansonsten hier so filter, folder, labels usw setzen
  // um letzendlich das hier machen zu dürfen:
  if Dlg.Execute() then
    Label2.Caption := Dlg.FileName
    else
    Label2.Caption := 'canceled';
  Dlg.Free;
end;


// der hier benötigt eine handle angabe damit er sich gut dazu positionieren kann
procedure TForm1.Button3Click(Sender: TObject);
var
  Dlg: TFileOpenDialog; // subtype testing
begin
  Dlg := TFileOpenDialog.Create(nil);
  if Dlg.Execute(Application.ActiveFormHandle) then
    Label3.Caption := Dlg.FileName
    else
    Label3.Caption := 'canceled';
  Dlg.Free;
end;


// dies hier arbeitet mit x und y
// und passt sich notfalls selbst an
procedure TForm1.Button4Click(Sender: TObject);
var
  Dlg: TOpenDialog;
begin
  Dlg := TOpenDialog.Create(Self);
  if Dlg.Execute((Left + (Width div 2)), (Top + (Height div 2))) then
    Label4.Caption := Dlg.FileName
    else
    Label4.Caption := 'canceled';
  Dlg.Free
end;

end.
Hier die Interceptor Unit:
Delphi-Quellcode:
unit dlgPos;

(*
Diese Unit erweitert den originalen TOpenDialog und TSaveDialog der Vcl.
Nun kann man auch X und Y Koordinaten im Execute() Aufruf verwenden.
Wer keinen Title setzt muss sich erstmal mit simplified English zufrieden geben.

Benutzung:
  Diese als letzte Unit einbinden und dynamisch einen Dialog erzeugen
  oder per Designer auf Form droppen und bei Ausführung halt die X/Y variante nutzen.
  Mit letzter Unit ist (falls überhaupt vorhanden) nach Unit Vcl.Dialogs gemeint.

Methodik:
  Diese Variante erzeugt einen Thread der wiederum auf ein Handle wartet.
  Sobald ein Handle vorliegt wird das Fenster vom Handle verschoben.

Beispiel dynamisch:
procedure TForm1.Button1Click(Sender: TObject);
var
  Dlg: TOpenDialog;
begin
  Dlg := TOpenDialog.Create(Self);
  if Dlg.Execute((Left + (Width div 2)), (Top + (Height div 2))) then
    Label1.Caption := Dlg.FileName
    else
    Label1.Caption := 'canceled';
  Dlg.Free
end;

Beispiel per Designer:
procedure TForm1.Button1Click(Sender: TObject);
begin
  if OpenDialog1.Execute((Left + (Width div 2)), (Top + (Height div 2))) then
    Label1.Caption := OpenDialog1.FileName
    else
    Label1.Caption := 'canceled';
  Dlg.Free
end;


gefunden auf stackoverflow.com
Autor und/oder Uploader: Peter Kapas
Lizenz: keine Angabe
Änderungen: code formatierung.
            methoden argumente sind nun const.
            umgeschrieben zu einer intercepted variante.
            bezeichner angepasst.

Kommentare und Änderungen von KodeZwerg.
*)


interface

uses
  Winapi.Windows, System.Classes, Vcl.Dialogs;

type
  TSetDialogPosition = class(TThread)
  private
    Title: string;
    XPos: Integer;
    YPos: Integer;
  protected
    procedure Execute; override;
  end;

  TOpenDialog = class(Vcl.Dialogs.TOpenDialog)
  private
    SDP: TSetDialogPosition;
  public
    function Execute(const X, Y: Integer): Boolean; overload;
  end;

  TSaveDialog = class(Vcl.Dialogs.TSaveDialog)
  private
    SDP: TSetDialogPosition;
  public
    function Execute(const X,Y: Integer): Boolean; overload;
  end;

implementation


// dieser thread wartet auf ein gefundenes handle.
// nach 5000 versuchen wird abgebrochen.
// wenn ein handle gefunden wurde,
// verschiebe das fenster.
// terminiere thread.
procedure TSetDialogPosition.Execute;
var
  hDlg: HWND; // handle des dialogs
  rDlg: TRect; // dimension des dialogs
  fuse: Integer; // endlos schleifen killer
begin
  hDlg := 0;
  fuse := 0;
  while ((hDlg = 0) and (fuse < 5000)) do
    begin
      hDlg := FindWindow(nil, PChar(Title));
      Inc(fuse, 1);
    end;
  if (hDlg <> 0) then
    if GetWindowRect(hDlg, rDlg) then
      begin
        XPos := (XPos - (rDlg.Right - rDlg.Left) div 2);
        YPos := (YPos - (rDlg.Bottom - rDlg.Top) div 2);
        if (MoveWindow(hDlg, XPos, YPos, (rDlg.Right - rDlg.Left), (rDlg.Bottom - rDlg.Top), True)) then
          SetWindowPos(hDlg, HWND_TOP, XPos, YPos, 0, 0, SWP_NOSIZE);
      end;
  DoTerminate;
end;


// erzeuge den wartethread
// übermittel aktuelle x und y werte
// setze title property
// rufe original dialog auf
// gebe wartethread wieder frei
function TOpenDialog.Execute(const X, Y : Integer): Boolean;
begin
  SDP := TSetDialogPosition.Create(False);
  SDP.XPos := X;
  SDP.YPos := Y;
  if Self.Title <> 'then
    SDP.Title := Self.Title
  else
  begin
    Self.Title := 'Open';
    SDP.Title := Self.Title;
  end;
  Result := inherited Execute;
  SDP.Free;
end;


// erzeuge den wartethread
// übermittel aktuelle x und y werte
// setze title property
// rufe original dialog auf
// gebe wartethread wieder frei
function TSaveDialog.Execute(const X, Y : Integer): Boolean;
begin
  SDP := TSetDialogPosition.Create(False);
  SDP.XPos := X;
  SDP.YPos := Y;
  if Self.Title <> 'then
    SDP.Title := Self.Title
  else
  begin
    Self.Title := 'Save';
    SDP.Title := Self.Title;
  end;
  Result := inherited Execute;
  SDP.Free;
end;

end.
Viel Spass damit.
Angehängte Dateien
Dateityp: 7z ComboBoxIssue.7z (765,5 KB, 5x aufgerufen)
Gruß vom KodeZwerg
  Mit Zitat antworten Zitat