Zentrierung eines Common Item Dialog
-
Hallo!
Ich möchte einen Common Item Dialog auf dem Bildschirm zentriert ausgeben.
Eine Positionierung lässt sich ja mittels IOleWindow::GetWindow(HWND*) bewerkstelligen, wobei das Interface über IFileDialog::QueryInterface() mit IID_IOleWindow zu beschaffen ist.Das Problem liegt aber im Zeitpunkt der Positionierung! Allgemein bei modalen Dialogen ist dies der Empfangszeitpunkt der Nachricht WM_INITDIALOG in der Dialogprozedur. Das Fenster ist noch nicht sichtbar, aber doch schon soweit am Leben, dass es verschiebbar ist. Beim anschließenden sichtbar werden steht es dann z.B. an der gewollten Bildschirmposition.
Beim Common Item Dialog gibt es diese Nachricht leider nicht. Vor dem IFileDialog::Show() existiert das Fenster noch nicht und danach ist schon alles gelaufen und der Dialog wieder geschlossen.
Mit IFileDialog::Advise() hänge ich ein Objekt ein, das die FileDialogEvents- und FileDialogControlEvents-Objekte aggregiert. Sie werden von mir ohnehin benötigt. Hier stehen in IFileDialogEvents Events-Methoden zu verschiedenen Zeitpunkten zur Verfügung.
Ich habe festgestellt, dass IFileDialogEvents::OnFolderChange grundsätzlich auch in der Startphase aufgerufen wird. Eine Positionierung zu diesem Zeitpunkt funktioniert aber nicht, weil die Fensterposition noch danach vom System initialisiert (überschrieben) wird.
Ich habe mich jetzt damit beholfen, dass ich an dieser Stelle einen Timer mit 50ms Laufzeit starte. Die TimerProc löst dann die eigentliche Fensterpositionierung aus.
Das kann aber dazu führen, dass man beim Öffnen des Dialogs ihn noch je nach Rechnerleistung kurz an der alten Stelle aufblitzen sieht und dann erst die Zentrierung hat. In manchen älteren Kompatibilitätsfällen ohne Double-Buffering (z.B. bei abgeschaltetem Aero-Stil in Win7) können sogar einige Fensterteile an der alten Stelle stehen bleiben, die dann in der zentrierten Stellung fehlen. Sehr unschön auseinandergerissen!
Wenn die Zeitkonstante größer gewählt wird, passiert das nicht mehr, aber die Fensterversetzung wird noch auffälliger, weil verzögert. Auch unschön.Alle anderen Methoden in diesem Interface sind für diesen Zweck ebenfalls unbrauchbar. Auch PostMessage-Ansätze führen nicht zum Ziel, weil sie zu unkontrollierten Verzögerungen führen.
Habe ich irgendeine Möglichkeit (Trick) übersehen, die den richtigen Zeitpunkt für eine Fensterverschiebung bietet, bevor der Dialog sichtbar ist, aber schon als Fenster existiert, aber vom System auch nicht noch wieder verschoben wird?
Microsoft (das SDK) scheint eine Zentrierung nicht vorgesehen zu haben.
Gruß
Burkhard
-
...ohne hooks wirst nicht weiter kommen...änliche frage:
http://stackoverflow.com/questions/1530561/set-location-of-messagebox
-
Hallo Mr C,
Hooking und Subclassing wende ich sonst häufig erfolgreich an. Leider führt das hier weder mit WH_CALLWNDPROC-, WH_CALLWNDPROCRET- noch mit WH_CBT-Hooks zum Erfolg.
Die explizite Änderung über CBT_CREATEWND und CREATESTRUCT, aber auch über MoveWindow oder SetWindowPos zeigt keine Auswirkung, weil die geometrischen Initialisierungen vom System erst später vorgenommen werden. Und dann gibt es leider keine eindeutige Eingriffsweise mehr. Selbst für die Ermittlung des Dialogfensters über IFileDialog und IOleWindow ist die HCBT_CREATEWND-Phase noch zu früh, sodass keine 100%-ige Absicherung des im Hook manipulierten hWnd möglich wäre.
Die zeitliche Verzögerung ist vermutlich die einzige Möglichkeit.Gruß
Burkhard
-
Subclassen und dann nach WM_INITDIALOG reagieren...
-
Hallo Martin,
für das Subclassing benötige ich aber doch das Fensterhandle. Das lässt sich aber nicht vor dem Aufruf IFileDialog::Show() beschaffen, weil es noch nicht existiert. Und dann fehlt einfach der geeignete Zeitpunkt zur Beschaffung. Das ist das eigentliche Problem!
Die nächste Möglichkeit eines Aufrufs in den Objekten ist IFileDialogEvents::OnFolderChange, wie ich rausgefunden habe. Wenn ich jetzt mit SetWindowLong(hDlg, GWL_WNDPROC, (long)CenterControlSubClassProc) Subclassing durchführe, bringt das leider nichts mehr, weil das WM_INITDIALOG schon durch ist.
Gruß
Burkhard
-
Dann setzt Dir selbst auf ein bekanntes Window auf, dass Du Zugriff hast ein subclass und sende Dir eine selbsdefinierte Nachricht mit PostMessage und verschiebe dann das Fenster.
Da Du die Nachricht erst behandelst wenn die Nachrichteschleife läuft müsste es spät genug sein.
-
Hallo Martin,
bei einem zusätzlich erstellten Fenster, das dann als Owner fungiert, bräuchte man kein Subclassing mehr. Hier steht in der eigenen WindowProc ja alles zur Verfügung.
Das Problem wird dadurch aber nicht gelöst. Das owned window wäre dann das COM-Objektfenster und man kommt da genauso wenig in den Zeitverlauf der Messages rein wie vom Applikationshauptfenster. Ein PostMessage kommt völlig unkontrolliert an. Zu spät würde hier wieder ein Aufblitzen bedeuten, zu früh je nach System ein Auseinanderreißen. Die zeitliche Korrelation zwischen Explorerdialog und Aufrufumgebung fehlt einfach bzw. das erst zu einem relativ späten Zeitpunkt ermittelbare Fensterhandle.Das Problem besteht übrigens für jeden Common Item Dialog Anwender seit es dies gibt. Man findet sich halt mit dem Positionsmanagement von Windows ab. Es ist nur unschön, wenn man eine Multimonitor-Lösung hat, bei der alle Dialoge auch auf dem passenden Monitor erscheinen sollen, und davon kann IFileDialog natürlich nichts wissen.
Es ist eine Feinheit und muss jetzt nicht unbedingt in nächster Zeit gelöst werden. Das Problem besteht für mich seit Jahren. Wenn jemand in Zukunft mal über dieses Thema stolpert und es in tatsächlich programmierter Form gelöst haben sollte, würde ich mich über einen Hinweis sehr freuen.Bis dann
Burkhard
-
... Tipp: am einfachsten was machen kannst, bastle dir selber so einen Dilaog, dann hast keine Probleme mehr.
-
Mr C schrieb:
... Tipp: am einfachsten was machen kannst, bastle dir selber so einen Dilaog, dann hast keine Probleme mehr.
Warum sollte man so etwas wollen? Man will dem Nutzer doch ein einheitliches Look & Fell bieten.
-
Na, dann, viel spaß...
-
Einen Schritt mehr zum "Low Level" machen und OPENFILENAME structure benutzen, da kann man in LPOFNHOOKPROC lpfnHook, eine Adresse für den Hook setzen.
API heißt diese IUnknown-Scheiße nicht zu benutzen, dann kann man auch die MFC oder wenn es ganz schlimm kommt: C# verwenden...