AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein GUI-Design mit VCL / FireMonkey / Common Controls Fensterposition von TOpenDialog und TSaveDialog - Lösung
Thema durchsuchen
Ansicht
Themen-Optionen

Fensterposition von TOpenDialog und TSaveDialog - Lösung

Ein Thema von awk · begonnen am 28. Feb 2017 · letzter Beitrag vom 28. Feb 2017
Antwort Antwort
awk

Registriert seit: 12. Sep 2007
3 Beiträge
 
#1

Fensterposition von TOpenDialog und TSaveDialog - Lösung

  Alt 28. Feb 2017, 19:22
Hallo liebe Delphianer,

da ich selbst eine ganze Weile gebraucht habe eine Lösung zu o.g. Thema zu finden, möchte ich euch an meiner nun teilhaben lassen. Das Problem ist bekannt:

Ein TOpenDialog bzw. TSaveDialog läßt sich nicht positionieren und erscheint z.T. meilenweit entfernt von der aufrufenden Applikation. Das Event OnShow, das zunächst für diesen Zweck als hilfreich erscheint, liefert keine Möglichkeit die Position und Größe des Dialogs zu beeinflussen. Es fehlen einfach die entsprechenden Objekteigenschaften.

Nach langen (erfolglosen) Recherchen bin ich nun auf eine Idee gekommen, die die Positionierung nicht über das Fenster-Handle, also SetWindowPos(...) macht. Dies ist nämlich zum Scheitern verurteilt, weil Windows die jeweils letzte Position und Größe in der Registry je Applikation speichert und diese, nach dem Zeitpunkt zu dem das OnShow-Event aufgerufen wird wiederherstellt. Alle Versuche mit SetWindowPos und Co. sind daher zum Scheitern verurteilt.

Die Lösung für das Problem lautet: Direkt nach TOpenDialog.Create setzen wir den entsprechenden Registry-Wert so wie wir es wünschen.

Folgende Procedure stellt einen TOpenDialog und TSaveDialog immer zentriert über das Application.MainForm, der Parameter "nVersatz" bestimmt, um wie viele Pixel das Fenster an jeder Ecke Kleiner sein soll, als das Application.MainForm:

Delphi-Quellcode:
uses Registry,Classes,Forms;

procedure SetRegPos(nVersatz: integer);
var reg: TRegistry;
    buffer: pByteArray;
    buflen: integer;
    fn: WideString;
    p,s,e: PWideChar;
    appname: string;
    tmplen: integer;

    aValues: tstringlist;
    n: integer;
    r: TRect;
    
    // little endian
    procedure setInt(nValue,nOffset,nLen: integer);
    var i,calcValue: integer;
        nByte: byte;
    begin
     calcValue:=nValue;

     for i:=0 to nLen-1 do
     begin
      nByte:=calcValue and 255;
      buffer[nOffset+i]:=nByte;
      calcValue:=calcValue shr 8;
     end;

    end;

begin
 
 reg:=TRegistry.Create;

 try
  reg.RootKey:= HKEY_CURRENT_USER;
  if not reg.OpenKey('Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\CIDSizeMRU',False) then exit;

  aValues:=tstringlist.create;
  reg.GetValueNames(aValues);

  for n:=0 to aValues.count-1 do
  begin

   buflen:=reg.GetDataSize(aValues.strings[n]);

   if buflen>0 then
   begin
    GetMem(buffer,buflen);

    try
     reg.ReadBinaryData(aValues.strings[n],pByte(buffer)^,buflen);

     s:=PWideChar(buffer);
     p:=s;
     e:=s+buflen;
     tmplen:=0;
     while p<=e do
     begin

      if p^=#0 then
      begin
       tmplen:=(p-s);
       Break;
      end;

      inc(p);
     end;

     if tmplen>0 then
     begin
      SetLength(fn,tmplen);
      copymemory(@fn[1],s,tmplen*SizeOf(WideChar));
     end;

     appname:=WideCharToString(@fn[1]);
     if appname=ExtractFilename(Application.Exename) then
     begin
      // Koordinaten unseres MainForms
      r:=Rect(application.MainForm.Left,application.MainForm.Top,
              application.MainForm.Left+application.MainForm.Width,application.MainForm.Top+application.MainForm.Height);

      // jetzt Koordinaten des Dialogs setzen

      // Left-X
      setInt(r.Left+nVersatz,$218,4);

      // Left-Y
      setInt(r.Top+nVersatz,$218+4,4);

      // Bottom-X
      setInt(r.Right-nVersatz,$220,4);

      // Bottom-Y
      setInt(r.Bottom-nVersatz,$220+4,4);

      reg.WriteBinaryData(aValues.strings[n],pByte(buffer)^,buflen);

      break;
     end;

    finally
     FreeMem(buffer,buflen);
    end;
    
   end;

  end;

  reg.CloseKey;
 finally
  aValues.Free;
  reg.free;
 end;

Aufgerufen wird die Procedure beispielhaft dann wie folgt:

Delphi-Quellcode:

    with TOpenDialog.Create(Application) do
    try
      SetRegPos(80); // an allen Ecken 80 Pixel kleiner als das MainForm

      DefaultExt := GetControlPanel.DefaultExt;
      Filter := FilterString;

      Options := [ofHideReadOnly, ofPathMustExist, ofFileMustExist, ofEnableSizing];
undsoweiter...

Hoffe das erspart einigen unter Euch das stundenlange Suchen nach einer entsprechenden Lösung.

Anzumerken wäre noch, daß beim ersten Aufruf eines solchen Dialogs noch keine Wirkung zu sehen ist - der Dialog wird auf Monitor 0 zentriert dargestellt, weil es ja noch keinen Registry-Eintrag gibt. Dies könnte man natürlich noch ergänzen. Für mich ist dies momentan nur ein Schönheitsfehler.

PS: Einen Teil des Codes habe ich von einer chinesischen Seite deren URL ich leider nicht gespeichert habe und dessen Autor vermutlich in chinesischer Schrift genannt war. Dort wurde ein Beitrag zur MRU-Verwaltung mit der Funktion "GetLastVisitedMRU" gelistet. Einen Dank an den unbekannten chinesischen Autor.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.142 Beiträge
 
Delphi 12 Athens
 
#2

AW: Fensterposition von TOpenDialog und TSaveDialog - Lösung

  Alt 28. Feb 2017, 19:39
Die neue IFileOpen-API kann man bestimmt auch hooken und da beim Start direkt die Fensterposition setzen.

Alternativ setzt man die ClientGuid für seine Dialoge.
Für jede GUID werden eigene Einstellungen gespeichert und schon kann der User jeden Dialog selber einstellen, wo er ihn haben will.
(logisch zusammengehörige Dialoge an verschiedenen Programmstellen haben bei mit die selbe GUID)

Ach, ja, man sollte direkt die VistaDialoge verwenden.
TFileOpenDialog statt TOpenDialog

Embarcadero hat da teilweise bissl Schrott produziert, womit man über die TOpenDialog-Krücke keinen ordentlichen Zugriff hat, obwohl da intern der TFileOpenDialog verwendet wird, wenn das Windows aktuell genug ist.
Aber da ja keiner mehr WinXP/Vista nutzt, gibt es keinen Grund mehr, die alte API ins Programm zu holen und diese kranke Krücke zu nutzen.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
awk

Registriert seit: 12. Sep 2007
3 Beiträge
 
#3

AW: Fensterposition von TOpenDialog und TSaveDialog - Lösung

  Alt 28. Feb 2017, 19:53
Aha, ich lese aus deinen Ausführungen, daß dies bislang eigentlich gar kein Problem darstellte und die simple Lösung nur meiner Aufmerksamkeit entgangen war. Wenn dies tatsächlich so ist, dann lege ich mich jetzt einfach wieder aufs Ohr.

Vielen Dank für Deine superschnelle Reaktion.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.142 Beiträge
 
Delphi 12 Athens
 
#4

AW: Fensterposition von TOpenDialog und TSaveDialog - Lösung

  Alt 28. Feb 2017, 22:20
Natürlich mag der Code funktionieren.

Aber es ist meistens besser sich auf "offizielle" Schnittstellen zu stüzen.
Gut, hier mag wenigstens nichts kaputt gehen, wenn Microsoft mal diese Funktion ändert und z.B. den Speicherort ändert.
Knallen könnte es nur, wenn sie das Datenformat ändern, der schreibende Code nicht prüft, ob das Format stimmen könnte und dann was Komisches passiert, wenn Windows später versucht das Geschriebene auszuwerten.

PS: Zum komischen Verhalten ein Beispiel.
Wenn man dem FileDialog einen "ungültigen" InitialPath gibt, weil man ihn vorher nicht geprüft hat, dann zeigt Windows garnicht erst den Dialog an, aber Execute behauptet, dass der User auf "OK" geklickt hätte.

Tipp: Ich hab das Gefühl der Code wurde vor längerer Zeit mal von C++ übersetzt.
Delphi hat da ein paar nette Property und automatische Konvertierungen, welche man nutzen könnte, um den Code stark zu verkürzen.
z.B. r:=Application.MainForm.BoundsRect; oder appname:=PWideChar(buffer);
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests

Geändert von himitsu (28. Feb 2017 um 22:34 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:26 Uhr.
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