wpfanimationgraphicsgeometrydrawingvisual

Overlay geometry objects in WPF such that each has the same dimensions but different transformations


I have been stuck with this issue for a week now and have tried multiple approaches.

I have a drawing of a balance scale as shown below. I have split up this image into two such that the base + vertical shaft represent one image while the horizontal bar represents another. I then extract path geometries from both and try to draw them from within a single custom shape or as a visual layer drawing.

As you can imagine, the base+vertical shaft need to stay static while the horizontal bar needs to rotate on its center point up to a certain degree of freedom. The WPF is simply:

<Canvas>
    <!-- Custom Shape or Visual Layer Drawing here. -->
</Canvas>

Problem: Since I want both geometries to have the same left, top, right, bottom values, I use either a single custom Shape or a single DrawingVisual, try to draw both geometries which works fine. However, when I apply a transform to either one of them, the transform gets applied to both making the base+vertical shaft move as well.

Question: Please note that I am not trying to find a fix to the code I already have since I have tried to ask the question before many times in vain e.g. here. Could someone please explain how to achieve an overlay like this from scratch?

Balance Scale


Solution

  • Maybe your approach with a custom shape and drawing visuals is too complicated. Try the following pure XAML solution (of course with simplified geometries) to get an idea how this could be done with just a Path and a few geometries.

    <Canvas>
        <Path Canvas.Left="200" Canvas.Top="100" Fill="Black">
            <Path.Data>
                <GeometryGroup FillRule="Nonzero">
                    <PathGeometry Figures="M0,0 L50,200 -50,200 Z"/>
                    <GeometryGroup>
                        <GeometryGroup.Transform>
                            <RotateTransform
                                Angle="{Binding Value,ElementName=angleSlider}"/>
                        </GeometryGroup.Transform>
                        <PathGeometry Figures="M-100,-5 L100,-5 100,5 -100,5 Z"/>
                        <PathGeometry Figures="M-100,0 L-75,100 L-125,100 Z">
                            <PathGeometry.Transform>
                                <TransformGroup>
                                    <RotateTransform CenterX="-100"
                                        Angle="{Binding Value,ElementName=angleSlider}"/>
                                    <ScaleTransform ScaleX="-1" CenterX="-100"/>
                                </TransformGroup>
                            </PathGeometry.Transform>
                        </PathGeometry>
                        <PathGeometry Figures="M100,0 L75,100 L125,100 Z">
                            <PathGeometry.Transform>
                                <TransformGroup>
                                    <RotateTransform CenterX="100"
                                        Angle="{Binding Value,ElementName=angleSlider}"/>
                                    <ScaleTransform ScaleX="-1" CenterX="100"/>
                                </TransformGroup>
                            </PathGeometry.Transform>
                        </PathGeometry>
                    </GeometryGroup>
                </GeometryGroup>
            </Path.Data>
        </Path>        
    </Canvas>
    ...
    <Slider x:Name="angleSlider" Minimum="-45" Maximum="45"/>
    

    Edit: in order to make this scalable you may set the Canvas Width and Height to fixed values (those given by the contained geometries) and put the whole thing in a Viewbox:

    <Viewbox>
        <Canvas Width="250" Height="205">
            ...
        </Canvas>
    </Viewbox>