Mein Optimizer hat eine Kopfleiste mit Tabs für Optionen. Ein Tab pro unterstütztem Dateiformat.
Anfang des Monats fiel mir auf, dass die Tabs unter Windows 10 nicht richtig anzeigen:
Wie ihr seht, haben die Tabs einen grauen Hintergrund und einen schmalen weißen Rand. Normalerweise sind Tabs in Windows 10 aber komplett weiß (siehe Dateieigenschaftsdialog):
(Ignoriert, dass das Symbol falsch angezeigt wird. Das kriegt Windows wahrscheinlich 2030 noch nicht richtig hin.)
Also, wie kriege ich das komplett weiß, wie es sein sollte?
Grundlagen
Ich benutze pure Win32-API ohne MFC, WinForms, WPF, oder ähnliche Scheiße. Bloß eine Dialogbox, wie man sie aus dem Ressourcen-Editor kriegen würde, mit den Standard-Common Controls (Buttons, Edits Checkboxes, usw).
Die Tabs sind durch ein Tab Control realisiert. Das wurde vor rund 30 Jahren eingeführt und hat dementsprechend viele Fallstricke. Es ist zum Beispiel kein vollwertiger Container, sondern bloß ein leerer Rahmen, den man im Dialog platziert. Was darin angezeigt wird und was sich ändert, wenn man von einem Tab zum nächsten schaltet – das muss man alles selber machen.
Einen der Fallstricke hat – wie es der Zufall will – Raymond Chen letzte Woche erklärt.
Nämlich ist der Standard-Weg, wie man ein Tab Control befüllt, folgender:
- Alles, was in einzelnen Tabs angezeigt werden soll, in eigene Sub-Dialoge packen. Die Sub-Dialoge bekommen keinen Rahmen und dürfen nicht modal sein.
- Die Sub-Dialoge erzeugen zu einem Kind des Hauptdialogs machen – nicht zu einem Kind des Tab Controls!
- Wann immer das Tab Control meldet, dass jemand draufklickte: Alle Sub-Dialoge unsichtbar schalten und nur den einen sichtbar machen, der zu dem Tab gehört, das gerade ausgewählt wurde.
Naja … fast :( Der Hintergrund hat die korrekte Farbe, aber der Inhalt (also Checkbox, Static) nicht.
Windows XP
Also Google angeschmissen. Problem auf StackOverflow gefunden.
https://stackoverflow.com/questions/776 ... -xp-styles
Selbes Problem. Die markierte Lösung ist falsch. Die mit den meisten Upvotes funktionierte bei mir nicht.
https://stackoverflow.com/questions/270 ... ize-window
Selbes Problem nach Resize. Keine Lösung.
https://stackoverflow.com/questions/104 ... xt-widgets
Selbes Problem. Die markierte Lösung ist falsch. Die mit den meisten Upvotes ist die gleiche wie oben vom selben Autor.
Dazu kamen zahllose MSDN-Foreneinträge.
Der Konsens ist:
- Du rufst bei der Initialisierung des Sub-Dialogs für dein Tab die Funktion EnableThemeDialogTexture(tab, ETDT_ENABLETAB) auf. Dann funktioniert das.
- Falls es dann nicht funktioniert, bist du am Arsch. Mach irgendwas mit WM_ERASEBACKGROUND oder so.
… ich war also am Arsch. Aber zumindest haben mir die Antworten gezeigt: Auf Windows XP sieht man den Unterschied noch viel deutlicher als auf 10! Also habe ich meine VM angeschmissen und das hier bekommen:
Japp. Windows XP mit Luna-Theme gibt Tabs haben einen Farbübergang im Hintergrund, und der ist hier eindeutig kaputt.
Nachdem ich EnableThemeDialogTexture(tab, ETDT_ENABLETAB) eingebaut habe:
Das sieht jetzt ganz genau wie in den StackOverflow-Problemen aus. Insbesondere wie in dem hier:
https://stackoverflow.com/questions/267 ... trol-it-be
Die Antwort ist nicht „straight-forward“ und auch sonst komplett falsch. Aber dazu kommen wir später.
Property Sheets
Der Konsens des Internets ist: Wenn du an diesem Punkt bist, und es funktioniert nicht, kann dir niemand mehr helfen.
Zwischen den Zeilen liest man aber immer, dass man statt Tab Controls sowieso besser Property Sheets benutzen sollte. Was ist das?
Property Sheets wurden mit Windows 95 eingeführt. Sie sind Gottklassen, die einen kompletten Dialog mit Tabs abhandeln. Oder, falls man keine Tabs mag, Assistenten mit mehreren Seiten. Es ist wirklich schwer zu erklären, aber: Die Dateieigenschaften, die ich oben gezeigt habe? Das ist kein Dialog mit einem Tab Control. Das ist in Wirklichkeit ein Property Sheet mit fünf Property Pages Allgemein, Kompatibilität, … Seit Windows XP ist quasi jeder Dialog, an der ihr Tabs seht, in Wirklichkeit ein Property Sheet. Einzige Ausnahme ist der Task Manager unter Windows XP & Windows 7. Dessen Tabs sind ein echtes Tab Control.
Okay. Wenn die Welt das so will … steige ich auf Property Sheets um. Gesagt, (zwei Tage später) getan.
Der Hintergrund ist endlich richtig. Aber alles andere war vernichtend.
Property Sheets sind als Standalone-Dialoge entworfen. Sie in einen bestehenden Dialog mit anderem Kram einzubinden ist eine riesen Qual:
- Die Rahmen müssen abgeschaltet werden, indem man während der Initialisierung in die Binärdaten eines Dialog-Templates greift (Ja, wirklich!!!)
- Die Knöpfe OK und Cancel müssen von Hand unsichtbar geschaltet werden.
- Weil die Klasse Assistenten von Setups ebenso wie Tabs abdeckt, hat man überall spezial-Flags, auf die man höllisch achten muss
- Das absolute No-Go: Man kann sie nicht vergrößern.
Die Dinger sind verschoben weil sie ihre Margin anders berechnen als normale Controls. (Klar, sie sind ja darauf ausgelegt, allein angezeigt zu werden). Die Rahmen passen nicht weil sich die Dinger nicht richtig vergrößern und verkleinern lassen. Das Internet behauptet zwar anderes, aber ich habe es nicht geschafft. Und Gott weiß, dass ich es versucht habe.
Auf dem Weg musste ich außerdem Microsoft melden, dass ihre Property Sheet-Dokumentation völlig zerstört wurde.
Die Lösung
Also zurück zu Tabs. Ich habe in Visual Studio ein simples Win32-Programm zusammengeklickt, das nichts anderes hat als:
- Die Common Controls 6 via Manifest referenzieren.
- InitCommonControlsEx()
- Einen Hauptdialog mit einem Tab-Control.
- Einen Sub-Dialog mit dem Tab-Inhalt (Sibling des Tab-Controls; Child des Hauptdialogs).
- Im Sub-Dialog eine Checkbox und ein Static (deren Rendering geht immer als erstes kaputt).
- Keine zusätzliche Nachrichtenbehandlung (kein WM_PRINTCLIENT oder so).
- Nur EnableThemeDialogTexture() in der WM_INITDIALOG-Nachricht im Sub-Dialog.
Also habe ich es Stück für Stück verändert, in Richtung meiner Anwendung. Die Styles. Die Reihenfolge der Controls. Und so weiter und sofort.
… und alles ging kaputt, sobald der Hauptdialog das Style-Bit WM_CLIPCHILDREN bekam.
Und zwar ging es auf ganz genau die Art und Weise kaputt wie in den ganzen unbeantworteten Fragen auf StackOverflow oder in den MSDN-Foren.
WM_CLIPCHILDREN ist essentiell, um Flackern beim Verschieben und Vergrößern/Verkleinern des Dialogs zu verhindern. Aber dazu schreibe ich später mehr. Jetzt ergötzen wir uns erstmal an dem korrekten Tab-Rendering auf Windows XP …
… bis Windows 10.