Einzelnen Beitrag anzeigen

awk

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

Fensterposition von TOpenDialog und TSaveDialog - Lösung

  Alt 28. Feb 2017, 20: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