wpf apply transforms when drawing drag adorner

999 views Asked by At

In our drag and drop implementation, we like the adorner to apply the adorned element's transform value.

The transform may apply to UIElements in Xaml like this:

<DockPanel.LayoutTransform>
    <TransformGroup>
        <ScaleTransform ScaleX="0.75" ScaleY="0.75"/>
        <RotateTransform Angle="10" />
    </TransformGroup>
</DockPanel.LayoutTransform>
    <TextBlock>
        <TextBlock.LayoutTransform>
...

This is how transforms are retrieved by summarizing the applied transforms of ancestor elements:

public static Transform GetAncestorTransforms(this DependencyObject descendant)
    {
        TransformGroup transformGroup = new TransformGroup();
        DependencyObject dObj = descendant;
        do
        {
            Visual visual = dObj as Visual;
            if (visual != null)
            {
                // determine the current transform by matrix determinants
                Transform t = VisualTreeHelper.GetTransform(visual);
                if (t != null)
                {
                    transformGroup.Children.Add(t);
                }
            }
            dObj = VisualTreeHelper.GetParent(dObj);
        }
        while (dObj != null);

        return transformGroup;
    }

The Adorner draws a round rectangle around the UIElement

protected override void OnRender(DrawingContext drawingContext)
    {
        var transform = (AdornedItem as DependencyObject).GetAncestorTransforms();
        if (transform != null)
        {
            drawingContext.PushTransform(transform);
        }
        Rect rect = new Rect(AdornedItem.TranslatePoint(new Point(0, 0), AdornedElement), AdornedItem.RenderSize);
        drawingContext.DrawRoundedRectangle(null, new Pen(Foreground, 2), rect, 2, 2);
        if (transform != null)
        {
            drawingContext.Pop();
        }
    }

The code draws the adorner in the correct size (e.g. if it's a scale transform) or it rotates the adorner into the correct angle (if there's a skew/ rotatetransform) but anyway the rectangle is never around the adorned element. it's somewhere beside it. This looks like an offset problem ?

(Please note that the problem already occurs when theres just a single transform in the wohole visual tree. When there are more transforms, I'm aware that I might swap the order of the summarized transforms in GetAncestorTransforms().)

1

There are 1 answers

0
deafjeff On

Found solution: The above code works, except one thing: AdornedItem.TranslatePoint() calculates the starting point for the surrounding rectangle by (already) transformed UIElements. To not apply the transform twice, remove it from the point.

Point pos = AdornedItem.TranslatePoint(new Point(0, 0), AdornedElement);
if (transform != null)
{
    pos = transform.Inverse.Transform(pos);
}
Rect rect = new Rect(pos, AdornedItem.RenderSize);