Input hit testing yields incorrect results on a Path element with large scaling factors in its RenderTransform
property.
The following XAML defines a Path with a filled circle and a Hand
cursor.
<Canvas Background="LightGray">
<Path StrokeThickness="0" Fill="Blue" Cursor="Hand">
<Path.Data>
<EllipseGeometry RadiusX=".5" RadiusY=".5" Center="1,1"/>
</Path.Data>
<Path.RenderTransform>
<ScaleTransform ScaleX="150" ScaleY="150"/>
</Path.RenderTransform>
</Path>
</Canvas>
As can be seen in the image below, the Hand
cursor appears although its position is way outside the shape.
With a larger Path and smaller scaling factors the Problem disappears and the Cursor behaves as expected.
<Canvas Background="LightGray">
<Path StrokeThickness="0" Fill="Blue" Cursor="Hand">
<Path.Data>
<EllipseGeometry RadiusX="50" RadiusY="50" Center="100,100"/>
</Path.Data>
<Path.RenderTransform>
<ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
</Path.RenderTransform>
</Path>
</Canvas>
Performing an explicit hit test like this
private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var canvas = (UIElement)sender;
var hitElement = canvas.InputHitTest(e.GetPosition(canvas));
Trace.TraceInformation("hitElement = {0}", hitElement);
}
in a mouse event handler on the Canvas gives the same incorrect results. A mouse click clearly outside the scaled Path still returns the Path as hit element.
It's also worth to note that the problem does not appear in Silverlight.
Now the question is: what is the cause of this behaviour and how can it be avoided? Note that I can not simply change the original sizes of my Path elements, so an answer like "don't use large scale factors" won't be helpful.
My current workaround is not to transform the Path by a RenderTransform, but transform the Data instead (by applying the transform to the Geometry.Transform
property). But as there may be complex Fills (e.g. with an ImageBrush) I have to transform the fill brushes too (which involves not only setting their transform, but also their viewport).
Moreover, the actual transform is not only scaling, but a MatrixTransform that also rotates and translates.
It may also be worth to note that the problem also emerges with other geometries and additional transforms. For example, a transformed Path with a RectangleGeometry shows a similarly incorrect behaviour.
Incorrect with large scale factors:
<Canvas Background="LightGray">
<Path StrokeThickness="0" Fill="Blue" Cursor="Hand">
<Path.Data>
<RectangleGeometry Rect=".5,.5,1,1"/>
</Path.Data>
<Path.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="150" ScaleY="150"/>
<RotateTransform Angle="45" CenterX="150" CenterY="150"/>
<TranslateTransform X="100"/>
</TransformGroup>
</Path.RenderTransform>
</Path>
</Canvas>
Correct with small scale factors:
<Canvas Background="LightGray">
<Path StrokeThickness="0" Fill="Blue" Cursor="Hand">
<Path.Data>
<RectangleGeometry Rect="50,50,100,100"/>
</Path.Data>
<Path.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1.5" ScaleY="1.5"/>
<RotateTransform Angle="45" CenterX="150" CenterY="150"/>
<TranslateTransform X="100"/>
</TransformGroup>
</Path.RenderTransform>
</Path>
</Canvas>
More of an extended comment than an answer:
That seems like strange behaviour, I had a play with some Paths
, and tried using Geometry.GetWidenedPathGeometry
to apply a slightly different scaling effect on the data itself, but didn't get very far.
The root cause of the problem seems to be the way the hit detection tolerance is chosen in WPF, there are two answers to a similar questions on MSDN by Brendan Clark, it seems like something that has never been fixed.
Essentially the hit testing tolerance used seems to be an absolute value derived from the base size of the geometry itself, rather than the rendered/transformed size. So whilst that's fine for big shapes that you're making smaller, or indeed small shapes that you're keeping small, it will start become quite inaccurate when small shapes are scaled up (as you've found).
I.e. A small hit testing tolerance relative to the size of a small shape is fine, but when the shape and tolerance both get scaled up, that starts to look horrible.
The proposed solution in one thread was to scale the shapes up to the maxiumum size you need, and scale them down when you want them smaller (which isn't a solution for you). Ew.
It looks like you might be stuck with some transforming. I'll try and see if I can come up with anything better.
Links I was looking at:
http://social.msdn.microsoft.com/Forums/vstudio/en-US/8708e340-f734-4cf4-b91d-28b49fee2b72/hittest-is-buggy-not-accurate-for-transformed-scaled-etc-visuals?forum=wpf
http://social.msdn.microsoft.com/Forums/vstudio/en-US/b307676b-d8b2-4af0-9f6f-1e150eed97ba/hittesting-with-a-scaled-path-doesnt-work?forum=wpf
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With