WPF shape geometry scaling without affecting stroke
我想知道是否有人在将缩放变换应用于形状时设法覆盖了 WPF 形状渲染的默认行为。默认行为会转换整个形状绘图,包括笔划,但我只想缩放几何图形。困难在于我的形状位于视觉层次结构中,渲染变换应用于不同的级别(有点像 2D 场景图,但 WPF 视觉树),我无法更改(!)。我在不同的地方读到,可以创建一个自定义形状来取消渲染变换的变换并将其放在几何体上。目前我有类似的东西:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | public sealed class MyPath : Shape { // This class has a Data property of type Geometry just like the normal Path class protected override Geometry DefiningGeometry { get { Geometry data = Data; if (data == null) { data = Geometry.Empty; } return data; } } protected override void OnRender(DrawingContext drawingContext) { Transform tr = RenderedGeometry.Transform; Geometry geomToDraw = RenderedGeometry.Clone(); geomToDraw.Transform = new MatrixTransform(tr.Value * tr.Value); Matrix trInv = tr.Value; trInv.Invert(); drawingContext.PushTransform(new MatrixTransform(trInv)); drawingContext.DrawGeometry(Brushes.Transparent, new Pen() { Brush = Brushes.Black, Thickness = 1 }, geomToDraw); } } |
很明显,我对此很陌生,上面的代码可能完全搞砸了。我试图将矩阵转移到几何而不改变最终得到的几何变换,因此 tr.Value*tr.Value 和 trInv。但它不像我想要的那样工作。我知道这种转移变换技术在理论上是有效的,因为我用恒定变换进行了尝试(测试设置 Geometry.Transform 以用 4 缩放 x 并推动变换以用 0.25 缩放 x 工作正常,但生成的形状绘图似乎并不适用拉伸=填充,我依赖)。所以渲染转换一定有一些我缺少的东西。
不工作的测试场景是这样的:
- 我在 xaml 中应用了 scaleX=4 和 scaleY=1 的渲染比例变换。
- 内置的 Path 类缩放整个绘图,使 x 方向的笔划比 y 方向的笔划宽 4 倍。
-
我希望 MyPath 仅缩放几何图形,而不是笔划。 <- 这不起作用!
- 发生的情况是:几何图形正确缩放,笔划在 x 方向缩放 4,在 y 方向缩放略小于 4。怎么了?我有一种感觉,我不应该只使用 RenderedGeometry.Transform 而是应该使用什么来代替?我需要在形状上合并渲染变换和拉伸=填充。我的渲染变换层次结构可能包含缩放、旋转和平移的混合,因此解决方案必须足够通用以处理任何变换,而不仅仅是轴对齐缩放。
注意:我知道在 OnRender 中创建几何体很糟糕,但我想在花时间清理之前让它工作。
顺便说一句,我看过这篇文章:
Path的笔画粗细与比例无关
如前所述,问题是我必须考虑渲染转换,但我不确定如何调整该解决方案以与它们一起使用。
如果我正确理解了这个问题,您想取消渲染转换对笔而不是几何图形的影响。
这可以通过获取控件相对于要取消转换的项目的转换来实现,并使用它的逆来取消对笔的影响。 (例如,如果您有层次结构 P1/P2/P3/UrShape,并且 P1、P2、P3 都对它们进行了变换,并且您希望它们都不会影响您的笔,则需要获得 P1 相对的变换到 UrShape)。然后您可以将变换重新应用到您的形状。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | var brush = new SolidColorBrush(Colors.Red); var pen = new Pen(brush, 5); //Instead of the direct parent you could walk up the visual tree to the root from where you want to cancel the transform var rootTransform = (MatrixTransform)this.TransformToAncestor((Visual)this.Parent); var inverserRootTransform = (MatrixTransform)rootTransform.Inverse; //We cancel out the transformation from the parent drawingContext.PushTransform(inverserRootTransform); var renderGeometry = this.Geometry.Clone(); // We apply the parent transform to the shape only, and group it with the original transform that was present on the shape // we do this to honor any transformation that was set on the shape. renderGeometry.Transform = new TransformGroup() { Children = { rootTransform, this.Geometry.Transform } }; //We draw the shape, the pen size will have the correct size since we canceled out the transform from the parent // but the shape now has the transformation directly on it. drawingContext.DrawGeometry(brush, pen, renderGeometry); drawingContext.Pop(); |