Bad
Here's an example of a GraphicsPath
that tries to draw an arrow head beyond the end of the line and causes a System.NotImplementedException
.
GraphicsPath capPath = new GraphicsPath();
capPath.AddLine(0, 8, -5, 0);
capPath.AddLine(-5, 0, 5, 0);
arrowPen.CustomEndCap = new CustomLineCap(capPath, null); // System.NotImplementedException
This fails because the path must intercept the negative Y-axis. But the path above only passes through the origin and doesn't actually hit the negative Y-axis.
The remarks in the docs are crucially important:
The fillPath
and strokePath
parameters cannot be used at the same time. One parameter must be passed a null value. If neither parameter is passed a null value, fillPath
will be ignored. If strokePath
is null
, fillPath
should intercept the negative y-axis.
This is one of those cases where the wording is poor. The docs say "should intercept" but I think it's the case that it "must intercept" or else you're going to get a System.NotImplementedException
. This intercept issue applies only to a fillPath
, and not a strokePath
. (The documentation could probably use some pictures too.)
Good
Here's an example of a GraphicsPath
that draws an arrow head at the end of the line and works correctly. And it's likely the kind of arrow head that most people want to draw anyway.

GraphicsPath capPath = new GraphicsPath();
capPath.AddLine(0, 0, -5, -8);
capPath.AddLine(-5, -8, 5, -8);
arrowPen.CustomEndCap = new CustomLineCap(capPath, null); // OK
Fixing Your Example
A fix is to just subtract the triangleHeight
from y. This places the tip of the arrow at 0,0 (which are the coordinates of the end of the line) which is likely what you intended anyway, and puts the base of the triangle at -triangleHeight
.
float radius = 5.0f;
float triangleSide = 3.0f * radius / (float)Math.Sqrt(3.0f);
float triangleHeight = 3.0f * radius / 2.0f;
GraphicsPath capPath = new GraphicsPath();
capPath.AddLines(new PointF[] {
new PointF(-triangleSide / 2.0f, -triangleHeight),
new PointF(triangleSide / 2.0f, -triangleHeight),
new PointF(0, 0) }
);
arrowPen.CustomEndCap = new CustomLineCap(capPath, null);
The exact fix for you (in Visual Basic):
Dim points() As PointF = New PointF() { _
New PointF(-triangleSide / 2, -triangleHeight), _
New PointF(triangleSide / 2, -triangleHeight), _
New PointF(0, 0) }
path.AddLines(points)