Invariant stroke thickness of Path regardless of the scale Invariant stroke thickness of Path regardless of the scale wpf wpf

Invariant stroke thickness of Path regardless of the scale


A better solution would be to use one or more System.Windows.Media.Geometry object to store your paths, points etc.

This geometry can be drawn with a Pen, so you could indeed change the stroke thickness when you zoom, but more flexible is to use the Transform property.

Using the transform, you can "zoom" the actual coordinates of the geometric representation and not the visualization - so when you draw it, you don't need to fiddle with render transforms.

To compute the transform, I use the following code like:

public static Matrix TransformShape(Rect fromPosition, Rect toPosition, bool flipVertical) {    Matrix translateThenScale = Matrix.Identity;    //we first translate to origin since that's just easier    translateThenScale.Translate(-fromPosition.X, -fromPosition.Y);    //now we scale the graph to the appropriate dimensions    translateThenScale.Scale(toPosition.Width / fromPosition.Width, toPosition.Height / fromPosition.Height);    //then we flip the graph vertically around the viewport middle since in our graph positive is up, not down.    if (flipVertical)        translateThenScale.ScaleAt(1.0, -1.0, 0.0, toPosition.Height / 2.0);    //now we push the graph to the right spot, which will usually simply be 0,0.    translateThenScale.Translate(toPosition.X, toPosition.Y);    return translateThenScale;}

where the fromPosition Rect should contain the untransformed bounds, and the toPosition Rect should contain the transformed bounds. This also trivially allows for scaling X and Y separately, which is often necessary for plotting.

It's easy to compute the bounds of your geometry:

Geometry graphGeom;//[...]   //the bounds are modified by the transform, so we want no transform!    graphGeom.Transform = Transform.Identity; Rect graphBounds = graphGeom.Bounds;//then set the transform again//or, if the transform is axis-aligned, the following _should_ work:Rect graphBoundsAlt = graphGeom.Transform.Inverse.TransformBounds(graphGeom.Bounds);

And of course WPF can tell you which bounds you need to render into, should that be necessary. Putting it together, you could do something like

public void RecomputeTransform(Rect targetRect, bool flipVertical) {    graphGeom.Transform = Transform.Identity;     Rect graphBounds = graphGeom.Bounds;    Matrix transMat = TransformShape(graphBounds,targetRect,flipVertical);    graphGeom.Transform = new MatrixTransform(transMat);}

The advantage of using this solution is that you don't need to mess with RenderTransforms, you're free to use transforms that shear and/or scale X and Y independently without getting weird distortions in your lines, and you can treat the Pen as an opaque object (i.e. easier to customize from the UI - if you select Pen width or whatnot, no further correction is necessary).


Finally I got the workaround for the above solution. I did it by setting the StrokeThickness of the Path relative to the scaling of the Canvas.

Like this:

// scale = scaling factor applied to the canvaspath.StrokeThickness = 1.0 / scale;

This only works if ScaleX and ScaleY are uniform.


Had the same problem, and the solution is really simple.

  1. Use a Geometry to define figure you wanto create
  2. Let a Path Shape render your geometry
  3. Apply Transforms to your Geometry, and the Path shape will update automatically

For example:

// Create your geometryvar rectGeometry = new RectangleGeometry(new Rect(0, 0, 200, 200));// Let the Path Shape paint your geometryvar path = new Path();path.Data = rectGeometry;path.StrokeThickness = 2;// Modify your geometry, NOT the Path shapevar scaleTransform = new ScaleTransform();rectGeometry.Transform = scaleTransform;scaleTransform.ScaleX = 2;scaleTransform.ScaleY = 1.5;