I am trying to draw dotted lines using the DrawLine
method. However, it adds a slight offset to the ends which makes the lines ugly. How can prevent adding offset to the ends?
protected override void OnPaint (PaintEventArgs e) {
Graphics g = e.Graphics;
g.DrawLine (Dot, new Point (10, 50), new Point (70, 50));
g.DrawLine (Dot, new Point (70, 50), new Point (70, 100));
}
Pen Dot = new Pen (Brushes.Black, 2) { DashStyle = DashStyle.Dot };
Output
Intended Result
The goal is simple and plausible:
And the rules of the game are also rather simple:
PenAlignment.Center
.Unfortunately the consequences of the combination are rather complicated, so bear with me...
Let's first look at the 1st rule; let's ignore other
DashStyles
and stay withDashStyle.Dot
. By default each dot and each gap havePen.Width
pixels for both sides. This leads right into the first issues:Pen.Width
we are in trouble.n
dots andn-1
gaps.There is more but let's next look into the 2nd rule; to illustrate it I drew this 10x upscaled image:
This is the code that created the colored parts:
Look closely to see how the lines are drawn!
(Aside: You may also want to watch the difference of
DrawRectangle
andFillRectangle
!)The problem is that they just won't fit together at the (outer) edges.
So what can we do? I don't think a
DashOffset
can help. But there is another option to tweak aPen
: We can set itsDashPattern
to use custom values.The values we need are two
floats
, containing the scaling for the dots and the gaps. By default both values are1f
. I decided to keep the dots square and modify only the gaps. Here is a function which solves the issue byHere is the line drawing function; it takes the
Graphics
object, aPen
, the two endPoints
and abyte
that tells us if the line is meant to stand alone or will connect to other lines, as in our example cases.To make a good connection, that will work well with semi-tranparent brushes we need the ability to skip a dot a at the beginning or end or even both, e.g. when we want to insert a diaogonal line as in my test below.
The skip values are
0
to skip none,1 or 2
to skip the 1st or last dot and3
to skip both. Of course you may want to use anenumeration
instead.After some obvious preparations I move the points out a bit and then calculate the number of dots and of gaps for vertical or horizontal lines. Then I calculate the modified the gap scale.
Here is the result, scaled up 4x, of drawing four lines to form a rectangle with varying pen widths going from
1/3 - 10/3
:This is the testbed I used; note the use of semi-transparent black to illustrate how the corners are drawn correctly, i.e. non-overlapping:
I really wish one could avoid the connection problems by simply using
DrawLines
, but this didn't work and after figuring out this solution I'm not really amazed it didn't..The coordinates you provide are the point to the top-left positions of the resulting line. So when you are drawing a line that is two pixels wide, you should calculate your begin- and endpoints to include the width of that line.
In this case, you would want to offset the vertical line slightly more to the left and top (by
borderwith
to be precise).So, by adding (or subtracting) an offset equal to the width of the line, the result is this: