Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   TFileOpenDialog öffnet immer auf MainMonitor (https://www.delphipraxis.net/205418-tfileopendialog-oeffnet-immer-auf-mainmonitor.html)

user69 7. Sep 2020 21:50


TFileOpenDialog öffnet immer auf MainMonitor
 
Hallo,

hat jemand eine Idee, wie ich den TFileOpenDialog bei PerMonitorDPIV2 immer auf dem Monitor meiner App öffne?
Bei mir öffnet der immer auf dem MainMonitor, egal ob er zuvor beim letzten Öffnen auf den Zweitmonitor verschoben wurde oder meine App auf dem Zweitmonitor ist.
Ergänzung: Dies passiert so, wenn die DPI des Monitore verschieden sind. Bei gleicher DPI öffnet der Dialog immer dort wo er zuvor geschlossen wurde (also auch nicht immer auf dem Moitor meiner App).
Dies ist besonders ärgerlich wenn meine App auf dem Zweitmonitor ist und das TFileOpenDialog Fenster einfach auf dem Hauptbildschirm aufgeht, wo man es nicht erwartet.

Ich weiß das Windows das automatisch speichert, aber Apps wie Chrome und Notepad funktionieren da ok, obwohl die auch PerMonitorDPIV2 sind.
In [HKEY_CURRENT_USER\Software\Microsoft\Windows\Curre ntVersion\Explorer\ComDlg32\CIDSizeMRU] sind die Positionen binär gespeichert, aber wie die kodiert sind ist mir unklar und eine Manipulation daran finde ich recht gewagt.
BTW: Ich nutze Delphi Sydney.

Hier finde ich die Windows Intelligenz nicht sinnvoll. Notepad macht es hier recht gut. Die Öffnen den Dialog immer relativ zur App (oder verschieben ihn, wenn er nicht auf den Bildschirm passt).

Ich hoffe ihr könnt mir da helfen.

himitsu 7. Sep 2020 22:47

AW: TFileOpenDialog öffnet immer auf MainMonitor
 
Hast du mal versucht deinem Dialog eine GUID zu geben?

user69 7. Sep 2020 23:31

AW: TFileOpenDialog öffnet immer auf MainMonitor
 
Ja, das ändert nichts an dem beschriebenen Verhalten.

himitsu 8. Sep 2020 03:17

AW: TFileOpenDialog öffnet immer auf MainMonitor
 
Dann fallen mir nur noch die Kompatibilitätsmodi ein.
Ohne Kennzeichnung geht Windows davon aus, dein Programm ist alt, bzw. total unfähig, und macht dann blöde Dinge.

supportedOS
dpiAware
dpiAwareness per Monitor
usw.

https://docs.microsoft.com/en-us/win...-for-a-process
https://docs.microsoft.com/en-us/win...tion-manifests

Dalai 8. Sep 2020 09:18

AW: TFileOpenDialog öffnet immer auf MainMonitor
 
Wird denn das richtige Manifest verwendet (also steht wirklich die erwartete PerMonitorDPIV2 im richtigen Abschnitt/Tag), und ist das Manifest syntaktisch korrekt?

Grüße
Dalai

user69 8. Sep 2020 09:46

AW: TFileOpenDialog öffnet immer auf MainMonitor
 
Liste der Anhänge anzeigen (Anzahl: 1)
Mein Manifest sollte auch ok sein.
Alternativ hab ich das Delphi Standard Manifest für MonitorDPIV2 verwendet => Keine Änderung.

Ich habe mal ein Demo angehängt (mit Source):
Einfach die Form nach dem Start auf einen Zweitmonitor (mit unterschiedlicher DPI Skalierung) schieben und "FileOpen" klicken.
Der Dialog geht immer auf dem Hauptmonitor auf.

KodeZwerg 8. Sep 2020 11:22

AW: TFileOpenDialog öffnet immer auf MainMonitor
 
Warum nicht einfach die Position vom Formular auslesen und den Dialog "dahinführen" ?
Wie ich neulich gelernt habe gibts ja das Screen und das Desktop für x/y angaben mit den man wunderbar rumspielen kann.
Ich lade mal Projekt und passe es an wie beschrieben, vielleicht ist es ja genau das was Du wolltest.

user69 8. Sep 2020 11:46

AW: TFileOpenDialog öffnet immer auf MainMonitor
 
Ja, das wäre, was ich will.
Aber den Dialog zu verschieben ist tricky und mir bisher nicht optimal gelungen.

KodeZwerg 9. Sep 2020 07:32

AW: TFileOpenDialog öffnet immer auf MainMonitor
 
Liste der Anhänge anzeigen (Anzahl: 1)
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.

dummzeuch 9. Sep 2020 08:40

AW: TFileOpenDialog öffnet immer auf MainMonitor
 
+1 :thumb:


Alle Zeitangaben in WEZ +1. Es ist jetzt 07:37 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz