.net – Anti-aliasing artifacts in WPF


I have a bizarre rendering issue when I'm trying to use anti-aliased graphics in WPF.

Here's a simplified version.

If I use the following XAML

<Window x:Class="RenderingBug.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Width="300" Height="300">
    <Grid Name="myGrid" Background="AntiqueWhite" Width="250" Height="250">
        <ScrollViewer Name="myScrollViewer" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
            <Canvas Height="500" Width="500" Name="myCanvas" />

And the following cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace RenderingBug
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
        public Window1()

            PathFigureCollection pfc = new PathFigureCollection();
            PathFigure pf = new PathFigure();
            pf.StartPoint = new Point(100, 20);
            LineSegment ls = new LineSegment();
            ls.Point = new Point(20, 100);
            PathSegmentCollection psc = new PathSegmentCollection();
            pf.Segments = psc;
            PathGeometry pg = new PathGeometry(pfc);

            RectangleGeometry clippingRectangle = new RectangleGeometry(new Rect(0, 0, 80, 80));

            Path p1 = new Path();
            p1.ClipToBounds = true;
            p1.Clip = clippingRectangle;
            p1.StrokeDashCap = PenLineCap.Square;
            p1.Stroke = Brushes.Black;
            p1.StrokeThickness = 30;
            p1.Data = pg;

            Path p2 = new Path();
            p2.ClipToBounds = true;
            p2.Clip = clippingRectangle;
            p2.StrokeDashCap = PenLineCap.Square;
            p2.Stroke = Brushes.White;
            p2.StrokeThickness = 10;
            p2.Data = pg;

I get a strange rendering issue with the anti-aliasing where the cropping rectangle edge is (running the program, it will be fairly obvious, but it's a hazy gray line where the cropping rectangle is truncating the paths.)

I've tried various techniques like aligning the controls to specific pixels, and setting SnapsToDevicePixels on the various controls in the hopes that this would solve this issue (remove the extra hazy gray band), but nothing seems to help.

Any ideas?

Best Solution

Turns out that this is because although the window is, of course, aligned on a pixel boundry, the grid within the window may not be. Fixing this isn't overly difficult, but it can take a while to figure out what to do.

There is a nice feature called SnapsToDevicePixels that should get everything aligned correctly. And, sadly, for whatever reason, it doesn't seem to work at all (this seems to be an understood bug). So what to do?

First, the grid must be aligned at a pixel boundry (ie not centered, or something like that since if the window has an odd number of pixels in either the horizontal or vertical direction then the grid, and hence the grid contents, will be misaligned.)

But then, there are other issues to deal with...as soon as you start to scroll the scroll bars, the artifact reappears! This is because the scrollbars do not necessarily scroll the content an integer number of pixels. To deal with that, I capture some events in the ScrollViewer to set the scrolling location to integer values.

private void workingAreaScrollViewer_SizeChanged(object sender, SizeChangedEventArgs e)
    double w = e.NewSize.Width;
    double h = e.NewSize.Height;
    workingAreaScrollViewer.Width = Math.Round(w);
    workingAreaScrollViewer.Height = Math.Round(h);

private void Window_KeyDown(object sender, KeyEventArgs e)
    if (e.Key == Key.A)
    if (e.Key == Key.Z && p2.Parent != workingAreaCanvas)

Do that, and all seems to be well.

(As a side note, for people that are having Image problems inside ScrollViews...if you're running into the same issue, this should fix that as well, so long as the Image isn't scaled, rotated, etc...so long as you're just trying to align the image to a pixel boundry, this should do the job.)