Skip to main content

C# WPF Snippet - Single Pixel Lines [Beginner]


WPF is great in so many ways - but it can be terribly frustrating in others. One of its greatest assets - the fact that as much as possible all drawing is vector based and done in device independent units - can be a huge pain when the time comes that you actually want to draw on a per pixel basis. This is because by default WPF anti-aliases everything, and it anti-aliases with a vengeance. What we are going to take a look at today is how to turn that anti-aliasing off in certain cases.

Most likely you have already heard of the boolean property on UIElement called SnapsToDevicePixels. This in many cases does exactly what we want - it forces WPF to snap drawing to the nearest pixel - i.e., if you tell something to be at position 10.7, it will snap to pixel 11 (on a 96 dpi display) when drawing, instead of sort of fading across pixels 10 and 11. But this does not turn off anti-aliasing, and so it is still not possible to draw single pixel width lines. Take a look at the image below to see an example of what I'm talking about:

Rectangle Comparison

The picture above is a screenshot of a small sample app blown up to 400%. Both rectangles are drawn using the exact same command, and as you can see, the one on the left is anti-aliased and the one on the right is not. The only difference is a special rendering flag called EdgeMode.Aliased. Let's take a look at the code behind this example:
public class SillyUserControl : FrameworkElement
{
  protected override void OnRender(DrawingContext dc)
  {
    dc.DrawRectangle(null, new Pen(Brushes.Black, 1), 
        new Rect(5, 5, 10, 10));
  }
}
 
This is the code for the control that is generating those rectangles. Yes, I know that a much better way to make a rectangle is with the Rectangle visual, but I wanted to do my own drawing for the example. So all we have here is a single call to DrawRectangle in the OnRender method, which should draw a 1 DIU thick black border. Since this is happening on a 96dpi display, that should translate to a 1 pixel thick border, but as you can see in the above screenshot, that is not always the case.

Here is the rather simple xaml for the window these SillyUserControls are sitting in:
<Window x:Class="RenderingExample.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:me="clr-namespace:RenderingExample"
    Title="Window1" Height="50" Width="40">
  <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
    <me:SillyUserControl x:Name="Fuzzy" Height="20" Width="20"/>
    <me:SillyUserControl x:Name="Crisp" Height="20" Width="20"/>
  </StackPanel>
</Window>
 
Again, no difference except for the name. So on to that one very special call:
public partial class Window1 : Window
{
  public Window1()
  {
    InitializeComponent();
    Crisp.SetValue(RenderOptions.EdgeModeProperty, EdgeMode.Aliased);
  }
}
 
On the rectangle named Crisp, we set the attached property EdgeMode to Aliased. By default, this is always set to Unspecified, which essentially means that the element will be anti-aliased. By setting it to Aliased, we are essentially turning off anti-aliasing for that element. And that is all you have to do!

There are a few caveats for this property - you can't use it to turn off anti-aliasing for an entire WPF application. This is because only non-text drawing primitives listen to this flag - there is no way to turn off anti-aliasing for text.

Well, I hope this answers some questions about WPF and anti-aliasing - I know it took me a while (and a good amount a frustration) to find this flag when I first needed it.

Comments

Popular posts from this blog

C# Snippet - Shuffling a Dictionary [Beginner]

Randomizing something can be a daunting task, especially with all the algorithms out there. However, sometimes you just need to shuffle things up, in a simple, yet effective manner. Today we are going to take a quick look at an easy and simple way to randomize a dictionary, which is most likely something that you may be using in a complex application. The tricky thing about ordering dictionaries is that...well they are not ordered to begin with. Typically they are a chaotic collection of key/value pairs. There is no first element or last element, just elements. This is why it is a little tricky to randomize them. Before we get started, we need to build a quick dictionary. For this tutorial, we will be doing an extremely simple string/int dictionary, but rest assured the steps we take can be used for any kind of dictionary you can come up with, no matter what object types you use. Dictionary < String , int > origin = new Dictionary < string , int >(); ...

C# WPF Printing Part 2 - Pagination [Intermediate]

About two weeks ago, we had a tutorial here at SOTC on the basics of printing in WPF . It covered the standard stuff, like popping the print dialog, and what you needed to do to print visuals (both created in XAML and on the fly). But really, that's barely scratching the surface - any decent printing system in pretty much any application needs to be able to do a lot more than that. So today, we are going to take one more baby step forward into the world of printing - we are going to take a look at pagination. The main class that we will need to do pagination is the DocumentPaginator . I mentioned this class very briefly in the previous tutorial, but only in the context of the printing methods on PrintDialog , PrintVisual (which we focused on last time) and PrintDocument (which we will be focusing on today). This PrintDocument function takes a DocumentPaginator to print - and this is why we need to create one. Unfortunately, making a DocumentPaginator is not as easy as...

C# WPF Tutorial - Implementing IScrollInfo [Advanced]

The ScrollViewer in WPF is pretty handy (and quite flexible) - especially when compared to what you had to work with in WinForms ( ScrollableControl ). 98% of the time, I can make the ScrollViewer do what I need it to for the given situation. Those other 2 percent, though, can get kind of hairy. Fortunately, WPF provides the IScrollInfo interface - which is what we will be talking about today. So what is IScrollInfo ? Well, it is a way to take over the logic behind scrolling, while still maintaining the look and feel of the standard ScrollViewer . Now, first off, why in the world would we want to do that? To answer that question, I'm going to take a an example from a tutorial that is over a year old now - Creating a Custom Panel Control . In that tutorial, we created our own custom WPF panel (that animated!). One of the issues with that panel though (and the WPF WrapPanel in general) is that you have to disable the horizontal scrollbar if you put the panel in a ScrollV...