Windows Standarddialoge anpassen
Auch wenn es darüber schon etliche Artikel im Netz gibt, möchte ich hier dennoch eine Technik präsentieren, wie man Standarddialoge, wie z.B. die MessageBox – Dialoge modifizieren und für seine Zwecke anpassen kann.
Das Ganze hat folgenden Hintergrund: Ich habe in der Arbeit ein Programm geschrieben, welches eine Erfolgsmeldung ausgibt, wenn es abgeschlossen ist: “Generierung erfolgreich abgeschlossen” [OK]
Nun wollte der Programmauftraggeber aber auch die Möglichkeit haben, die generierte Datei aus diesem Dialog heraus zu öffnen, also selbigen Dialog, nur neben dem [OK] sollte ein [Datei öffnen] stehen.
Nun gibt es mehrere Ansätze, das Problem zu Lösen.
1) Der naheliegendste Ansatz: OK – Dialog im Resource-Editor nachbauen, entsprechendes Icon reinladen und Dialog entsprechend umgestalten.
Dies bringt allerdings den Nachteil einer zusätzlichen Resource und sollte sich der Standard-Dialog einmal ändern, hat man immer noch seinen fix definierten Dialog
2) Statt als Resource würde sich der Dialog auch “on the fly” nachbauen. Raymond Chen beschreibt dies recht gut in seinem Blog.
3) Man nutzt die bestehende Dialogbox und baut sie nach seinen Wünschen um.
Ich habe mich hier für Ansatz 3 entschieden, da er meines Erachtens nach die einfachste Möglichkeit darstellt und sicherstellt, dass immer die Standard-Dialogbox von Windows verwendet wird.
Um also oben genannte Dialogbox zu realisieren, verwende ich einfach eine OK/Abbrechen (MB_OKCANCEL
) Dialogbox. Diese hat bereits 2 Buttons und ich brauche somit eigentlich nur den Abbrechen-Button neu beschriften.
Um dies zu erreichen, muss man einen WindowHook setzen, um in das System eingreifen zu können. Dies geschieht mittels der Funktion SetWindowsHookEx
.
Von allen möglichen Hook-Möglichkeiten nehmen wir hier den WH_CBT
, da dieser am wenigsten Overhead generiert und unsere Hook-Procedure damit möglichst wenig oft in Anspruch nimmt (siehe auch CBTProc
).
In der Hook-Procedure müssen wir darauf achten, immer mittels CallNextHookEx
an den nächsten Haken weiterzugeben.
Der Rest sollte dann ansich nur noch eine Formalität sein:
SetDlgItemText ((HWND)wParam, IDCANCEL, pszText);
Das funktioniert zwar soweit ganz gut, kann jedoch je nach Text und verwendeter Windows-Sprachversion schnell ins Auge gehen. Die Sache hat nämlich einen Haken: Die Größe der Buttons und deren Ausrichtung wird schon bei der Erstellung die Dialogbox kalkuliert und ist dementsprechend an den originalen Texten der Buttons ausgerichtet. Im Englischen ist der Text "Abort" z.B. denkbar kurz, sodass man keinen vernünftigen Text auf diesem Button unterbringt. Die einzige Abhilfe hier scheint zu sein, dass man die Buttons selbst nochmals neu ausrichtet. Zur Berechnung der Buttongröße kann man die Defaultgröße des verwendeten Fonts heranziehen. Das wäre:
LOWORD(GetDialogBaseUnits())
Daraus lässt sich dann ungefähr eine Buttongröße berechnen. Sehr genau ist dies aber leider nicht, da der verwendete Font meistens keine gleichmäßige Zeichengröße besitzt. Dennoch geht es sich meistens mit der unten gezeigten Methode gut aus. Nach der Größenberechnung des 2. Buttons werden die beiden Knöpfe kurzerhand verschoben. Und das war's dann auch schon.