As you are probably aware resolution and device independence is one of the fundamental design goals of WPF. In order to achieve the desired effect the framework does not work directly with physical (device-dependent) pixels but abstracts this notion into device-independent measuring units like inches.

There are two main benefits that you get from this:
  • Automatic DPI-aware scaling
  • Sub-pixel positioning -- WPF coordinates use floating point numbers that theoretically give you the ability to draw with higher precision than the one provided by the ordinary pixel grid.
However, there is one nasty side-effect related to the fact that WPF graphics are anti-aliased by default – sub-pixel positioning sounds great but in the end you are still stuck with the physical pixel grid and its limitations. These artifacts are most notably seen when rendering horizontal / vertical lines as blurry edges and occur when the location of a line edge falls in the middle of a device pixel rather than between device pixels.

What can you do about this?
  • Enable the UIElement.SnapsToDevicePixels property -- the WPF designers tried to provide a way to eliminate the need for anti-aliasing in this scenario by instructing object edges in a visual tree to align to device pixels. Unfortunately, with WPF v1 they did only partially succeed with this task as there are quite a few scenarios that are currently not handled as expected. Moreover, this mechanism only affects elements that run through the layout pass.
  • Apply a guideline set to your drawing – actually guideline sets are used by the SnapsToDevicePixels mechanism as well. For SnapsToDevicePixels the layout system produces two horizontal and two vertical guidelines that coincide with the bounding rectangle of any UIElement and instruct the graphic subsystem to place it on the pixel edge.
  • Size the objects to physical pixel sizes and position them on the pixel grid by either manually participating in the measure / arrange stage or through using some home-grown / 3rd party pixel snapper control.
  • There is one additional option that gets fairly less attention but could also help you in this situation. The blurry effect is a result of the combination of the sub-pixel positioning and the anti-aliasing applied by default on horizontal / vertical lines. All previous techniques try to tamper with the actual positioning but you can also conditionally disable the anti-aliasing as well. Generally pixel snapping is effective only for the horizontal and vertical lines but not to the diagonal lines. Anti-aliasing on the other hand is most beneficial to these same diagonal lines – horizontal / vertical lines do not have jagged edges to worry about in the first place so you could easily get away with disabling the anti-aliasing for them. You can control this via the RenderOptions.EdgeMode attached property (default value is “Unspecified”, turn off anti-aliasing by setting it to “Aliased”).

All approaches, however, suffer from the same general flaw – tweaking your layout to look good for a specific DPI setting does not guarantee you at all that it will work as smoothly on another. Unfortunately, there is no magic wand that solves this problem – hopefully with new updates of the WPF framework the UIElement.SnapsToDevicePixels automatic behavior will improve but for the time being these are the tools that you can use to make the best of a bad situation.

Related Posts


Comments are disabled in preview mode.