Search code examples
c#wpfxamltransformscale

WPF Xaml : Scaling parent panel while maintaining child image size


I'm trying to implement a specification for how children of a canvas behave when the canvas has a scale transform applied to it. Specifically for images, I need to support cases where an image maintains it's width and height when it's parent is scaled. As I investigate both Render and Layout scale transforms, it appears that a child is not automatically aware of transforms applied to ancestors and therefor doesn't obey it's stretch rule.

Here is an example in xaml:

<Canvas Background="#99000000">
    <Canvas Background="white" Canvas.Left="100"  Canvas.Top="100" Width="400" Height="300">
        <UserControl x:Name="_panelGreen" RenderTransformOrigin=".5 .5">
            <Canvas Background="#FF9ACD32" Width="100" Height="100">
                <TextBlock Text="Green Box"></TextBlock>
            </Canvas>
        </UserControl>
        <UserControl x:Name="_panelRed" RenderTransformOrigin=".5 .5">
            <UserControl.RenderTransform>
                <TransformGroup>
                    <ScaleTransform ScaleX="{Binding Path=Value, ElementName=xScale}" ScaleY="{Binding Path=Value, ElementName=yScale}" />
                    <RotateTransform Angle="{Binding Path=Value, ElementName=Rotate}"/>
                    <TranslateTransform X="{Binding Path=Value, ElementName=xTranslate}" Y="{Binding Path=Value, ElementName=yTranslate}"/>
                </TransformGroup>
            </UserControl.RenderTransform>
            <Canvas Background="red" Width="100" Height="100">
                <Image Source="Error-50.png" Stretch="None" Width="50" Height="50"></Image>
                <TextBlock Text="Red Box"></TextBlock>
            </Canvas>
        </UserControl>

    </Canvas>
    <StackPanel Canvas.Left="800">
        <Label Content="Rotate"></Label>
        <Slider Name="Rotate" Margin="10,10,10,0" Minimum="0" Maximum="359" Width="100" TickFrequency="1" IsSnapToTickEnabled="True"/>
        <Label Content="X Position"></Label>
        <Slider Name="xTranslate" Margin="10,10,10,0" Minimum="0" Maximum="500" Width="100" TickFrequency="1" IsSnapToTickEnabled="True"/>
        <Label Content="Y Position"></Label>
        <Slider Name="yTranslate" Margin="10,10,10,0" Minimum="0" Maximum="500" Width="100" TickFrequency="1" IsSnapToTickEnabled="True"/>
        <Label Content="X Scale"></Label>
        <Slider Name="xScale" Margin="10,10,10,0" Minimum="1" Maximum="2" Width="100" TickFrequency=".1" IsSnapToTickEnabled="True"/>
        <Label Content="Y Scale"></Label>
        <Slider Name="yScale" Margin="10,10,10,0" Minimum="1" Maximum="2" Width="100" TickFrequency=".1" IsSnapToTickEnabled="True"/>
    </StackPanel>
</Canvas>

Regardless of whether the transform is render or layout, the image scales when the parent scales.

I wondering if someone has a suggestion for an approach to keeping the image 50x50 during the parent scale. Should I try applying an inverse scale to the image by subscribing to the parent scale's changed event?

The actual rules that govern this system can be more complex than this because I have to make this work with an n-depth tree. I'm trying here to start with the most basic example.


Solution

  • Place two Canvases inside a Grid, placing them overlapped. Place scaleable children into one canvas, and non-scaleable ones into another. Do transform on just the scaleable canvas.