Dieses und weitere WPF-Tutorials auf SeriTools.de.
Wichtig: Der Inhalt dieses Artikels funktioniert nur ab .NET 4 aufwärts.
Wie Sie sicher wissen, ist WPF vektorbasiert und auflösungsunabhängig. Dies wird dadurch erreicht, dass die Größeneinheit in WPF nicht aus “normalen” Gerätepixeln besteht, sondern aus einer logischen Einheit, die 1/96 Zoll groß ist. Das bedeutet, bei der in Windows standardmäßig eingestellten Auflösung von 96 dpi (dots per inch) eine logische Einheit genau ein Pixel groß ist, bei höheren Auflösungen dementsprechend mehr, bei niedrigeren weniger. Die Werte müssen daher keine ganzen Zahlen sein.
Testbild für das Layout-Rounding
Liegt nun ein Wert in der logischen Einheit umgerechnet zwischen zwei Gerätepixeln, müssen diese per Anti-Aliasing verrechnet werden, was in Fällen, in welchen es auf pixelgenaue Positionierung ankommt, zu mehr oder minder starker Unschärfe und Farbveränderungen kommt. Als Beispiel habe ich ein Bild mit pixeldünnen schwarzen und weißen Linien genommen. Liegt dieses auch nur minimal neben einem Gerätepixel, verwaschen die Linien ins Graue. Wenn dies in WPF geschieht ‒ in diesem Fall genau zwischen zwei Pixeln, also 0,5 zu viel oder zu wenig ‒ sieht es aus wie auf dem Bild unten.
Von einem Muster, geschweige denn von Linien kann man hier nicht mehr sprechen. Schwarz und weiß werden vermischt und ergeben die Durchschnittsfarbe grau (#808080). Verändert man die Fenstergröße dahingehend, dass das Bild auf ganzen Pixeln zentriert wird, wird das Bild wieder korrekt angezeigt.
Ein ähnliches Problem bekommt man, wenn man ein Grid in drei *-Spalten aufteilt, der horizontale Platz also gleichermaßen unter allen drei Spalten aufgeteilt wird, und das Fenster eine Breite von 100 Einheiten hat. Jede Spalte ist dann 33,33… Einheiten breit, bei 96 dpi also auch 33,33… Pixel.
WPF bietet dafür eine einfache, effektive Lösung: Die Eigenschaft UseLayoutRounding. Setzten Sie diese auf True, werden die Werte bereits im Layoutprozess auf Ganzzahlen gerundet, sodass diese Probleme nicht auftreten können. Dabei ist dieses Layout-Rounding so intelligent, nicht einfach mathematisch zu Runden, sondern ‒ am Beispiel des Grids ‒ zusätzlich aufzupassen, dass dadurch keine Pixel “verloren gehen”. Rundete man alle 3 Spalten auf 33 Pixel herunter, fehlte am rechten Rand ein Pixel. Das Layout-Rounding rundet eine Spalte auf 34 Pixel auf.
Übrigens: Vektorelemente wie Paths mit Unterpunkten werden nicht gerundet, um eine einfandfreie Darstellung ohne Verzerrungen zu gewährleisten.
Das Beispiel oben sieht mit eingeschaltetem Layout-Rounding so aus:
Perfekt! Um dies zu erreichen, setzen Sie die Eigenschaft UseLayoutRounding auf True, alle Unterelemente erben diese Einstellung. Brauchen Sie das Layout-Rounding überall, bis auf wenige Ausnahmen, setzen Sie die Eigenschaft am Stammelement auf True und überschreiben Sie diesen Wert bei den Elementen, für die das Layout-Rounding ausgeschaltet sein soll, mit False.
Brauchen Sie das Layout-Rounding nur an wenigen Stellen, ist es wichtig zu wissen, dass die Elementkoordinaten immer relativ zu den Koordinaten des übergeordneten Elements angegeben werden. Liegt das übergeordnete Element also zwischen zwei Pixeln, wird auch das gerundete Element dazwischen liegen! Um dessen vorzubeugen, setzen Sie zusätzlich die Eigenschaft SnapsToDevicePixels bei dem Element mit Layout-Rounding auf True.
Weiterführende Informationen zum Layout-Rounding von WPF finden Sie hier:
Wichtig: Der Inhalt dieses Artikels funktioniert nur ab .NET 4 aufwärts.
Wie Sie sicher wissen, ist WPF vektorbasiert und auflösungsunabhängig. Dies wird dadurch erreicht, dass die Größeneinheit in WPF nicht aus “normalen” Gerätepixeln besteht, sondern aus einer logischen Einheit, die 1/96 Zoll groß ist. Das bedeutet, bei der in Windows standardmäßig eingestellten Auflösung von 96 dpi (dots per inch) eine logische Einheit genau ein Pixel groß ist, bei höheren Auflösungen dementsprechend mehr, bei niedrigeren weniger. Die Werte müssen daher keine ganzen Zahlen sein.
Testbild für das Layout-Rounding
Liegt nun ein Wert in der logischen Einheit umgerechnet zwischen zwei Gerätepixeln, müssen diese per Anti-Aliasing verrechnet werden, was in Fällen, in welchen es auf pixelgenaue Positionierung ankommt, zu mehr oder minder starker Unschärfe und Farbveränderungen kommt. Als Beispiel habe ich ein Bild mit pixeldünnen schwarzen und weißen Linien genommen. Liegt dieses auch nur minimal neben einem Gerätepixel, verwaschen die Linien ins Graue. Wenn dies in WPF geschieht ‒ in diesem Fall genau zwischen zwei Pixeln, also 0,5 zu viel oder zu wenig ‒ sieht es aus wie auf dem Bild unten.
Von einem Muster, geschweige denn von Linien kann man hier nicht mehr sprechen. Schwarz und weiß werden vermischt und ergeben die Durchschnittsfarbe grau (#808080). Verändert man die Fenstergröße dahingehend, dass das Bild auf ganzen Pixeln zentriert wird, wird das Bild wieder korrekt angezeigt.
Ein ähnliches Problem bekommt man, wenn man ein Grid in drei *-Spalten aufteilt, der horizontale Platz also gleichermaßen unter allen drei Spalten aufgeteilt wird, und das Fenster eine Breite von 100 Einheiten hat. Jede Spalte ist dann 33,33… Einheiten breit, bei 96 dpi also auch 33,33… Pixel.
WPF bietet dafür eine einfache, effektive Lösung: Die Eigenschaft UseLayoutRounding. Setzten Sie diese auf True, werden die Werte bereits im Layoutprozess auf Ganzzahlen gerundet, sodass diese Probleme nicht auftreten können. Dabei ist dieses Layout-Rounding so intelligent, nicht einfach mathematisch zu Runden, sondern ‒ am Beispiel des Grids ‒ zusätzlich aufzupassen, dass dadurch keine Pixel “verloren gehen”. Rundete man alle 3 Spalten auf 33 Pixel herunter, fehlte am rechten Rand ein Pixel. Das Layout-Rounding rundet eine Spalte auf 34 Pixel auf.
Übrigens: Vektorelemente wie Paths mit Unterpunkten werden nicht gerundet, um eine einfandfreie Darstellung ohne Verzerrungen zu gewährleisten.
Das Beispiel oben sieht mit eingeschaltetem Layout-Rounding so aus:
Perfekt! Um dies zu erreichen, setzen Sie die Eigenschaft UseLayoutRounding auf True, alle Unterelemente erben diese Einstellung. Brauchen Sie das Layout-Rounding überall, bis auf wenige Ausnahmen, setzen Sie die Eigenschaft am Stammelement auf True und überschreiben Sie diesen Wert bei den Elementen, für die das Layout-Rounding ausgeschaltet sein soll, mit False.
Brauchen Sie das Layout-Rounding nur an wenigen Stellen, ist es wichtig zu wissen, dass die Elementkoordinaten immer relativ zu den Koordinaten des übergeordneten Elements angegeben werden. Liegt das übergeordnete Element also zwischen zwei Pixeln, wird auch das gerundete Element dazwischen liegen! Um dessen vorzubeugen, setzen Sie zusätzlich die Eigenschaft SnapsToDevicePixels bei dem Element mit Layout-Rounding auf True.
Weiterführende Informationen zum Layout-Rounding von WPF finden Sie hier: