On some monitors, it seems that thin WPF lines are blurred across two pixels instead of one. Ick. How do I get sharply rendered lines?


Rectangle

Without Pixel snapping

With Pixel Snapping

An anti-aliased rectangle zoomed in on Magnifier:

WPF offers a way to get sharp lines and keep anti-aliasing, by auto-magically aligning the horizontal and vertical edges of a UIElement to land on the pixel grid when you set UIElement.SnapsToDevicePixels=true. The property inherits: set SnapsToDevicePixels on your topmost UIElement, and on the first child of every VisualBrush that you want sharp lines on.

 

When SnapsToDevicePixels=true, the layout system produces 2 horizontal guidelines and 2 vertical guidelines that coincide with the bounding box of any UIElement. These 'guidelines' are produced during the Measure/Arrange passes, and hint to MIL to place that boundary lines on the pixel edge.

 

SnapsToDevicePixels only affects horizontal and vertical lines. Setting this property will also help situations where you want abutting edges. Here are some pictures of the old ListBox style with and without SnapsToDevicePixels set:

 

Without Pixel Snapping

With Pixel Snapping

96 dpi Abutting blue rectangles without Pixel Snapping

Abutting Blue rectangles with pixel snapping

 

Why should I care about the pixel grid? WPF graphics are anti-aliased and device-independent, which is a great story for high-resolution machines. However, if you have a black one-pixel line that lands in between two pixels on a low-resolution machine, we will color the two pixels 50% black and 50% white (also known as Grey).

 

Shall I just adjust my line offsets manually? Should I round my doubles to ints? No, this will make your application device-dependent. On a machine with 120-dpi, an app optimized for 96 dpi will have blurry lines.

 

How does it work? The WPF graphics layer, MIL, takes any specified guideline and ensures that that coordinate lands on a whole pixel. To do this, MIL might shift your lines up to .5 pixel in either direction. Any line affected by the horizontal guideline will be adjusted the same distance as the guideline. Thus, if you have a horizontal line with the same y-coordinate as the horizontal guideline, that line will be placed on the pixel grid. If you have a vertical line with the same x-coordinate as the vertical guideline, that line will be placed on the pixel grid.

 

Pixel snapping only affects horizontal and vertical lines that have a specified GuidelineSet. SnapsToDevicePixels creates guidelines that coincide with the bounding box of your element -- for a Rectangle, having SnapsToDevicePixels generate those guidelines will ensure that the lines of the Rectangle land on the pixel grid.

 

A few things to remember:

This is a hinting mechanism, not sure-fire.

We still respect the anti-aliasing rules: if you specify a line that is thicker than a pixel, we will still spread that line across 2 pixels.