![]() |
Fensterposition von TOpenDialog und TSaveDialog - Lösung
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:
undsoweiter...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]; 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. |
AW: Fensterposition von TOpenDialog und TSaveDialog - Lösung
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. |
AW: Fensterposition von TOpenDialog und TSaveDialog - Lösung
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. |
AW: Fensterposition von TOpenDialog und TSaveDialog - Lösung
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.
Delphi-Quellcode:
oder
r:=Application.MainForm.BoundsRect;
Delphi-Quellcode:
appname:=PWideChar(buffer);
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 20:51 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz