Question

Ok, So I have an Image object that I want to rotate around its upper left corner. I setup a RotateTransform of 'X' degrees. My Image has the margin set to (400,400,0,0). This centers it on the canvas.

The problem is that when I apply different angles of rotation, the image shifts over to the right and down. Looking at posts, I now understand WHY this is happening. The rotate transform figures out a bounding rectangle so if you rotate the image, it will have a larger bounding rectangle. The Margin is applied to that bounding rectangle which results in the actual image shifting downward and to the right as the angle changes.

What I want is for the upper left corner of the image to always be at precisely (400,400) on my canvas and the image to rotate around that point.

I have done some calculations and can get it to behave by shifting the left and top margin, but can only get the calulations right in quadrant 1 and 4 (0-90 degrees) and (270-360 degrees) using the following equations:

For 0-90 degrees: (just have to alter the TOP part of the margin. Desired margin is (400,400,0,0) Margin=(400-sin(angle)*Image.Height, 400, 0, 0);

For 270-360 degrees: (just have to alter the TOP part of the margin. Desired margin is (400,400,0,0) Margin=(400, 400 + sin(angle)*Image.Width, 0, 0);

I have experimented using RotateTransform.Transform(point) and using Image.TranslatPoint(point) but cannot seem to get those to work.

Does anybody have any suggestions for me?

I guess if I could figure out what the binding rectangle of the image was at any given time, I could figure out the difference between it's width/height and my image width/height and use that to shift the margin appropriately.

I have put together a small app that will show my problem. When running, the app shows a red dot where I want the upper left of the rectangle to be. I simplified it to use a grid instead of an image. At the upper left of the screen, there is a textbox where you can specify the angle of rotation. To the right of the text box are two RepeatButtons that will increment and decrement the angle if you just click and hold them down. Notice how when you change the angle, the upper left corner of the grid shifts away from the red dot. I want it to act like you put a pin in the upper left corner, and rotated it about the pin with no shift from the dot that the corner starts on.

Thanks for your help.

Here's the code for the sample app:

xaml:

<Window x:Class="RenderTransformTester.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="900" Width="900">
    <Canvas x:Name="_canvas">
        <Grid x:Name="_gridImage"  Width="400" Height="200" Canvas.Left="400" Canvas.Top="400" Background="Navy">
            <Grid.LayoutTransform>
                <RotateTransform Angle="45" CenterX="0" CenterY="0"/>
            </Grid.LayoutTransform>
        </Grid>
        <Ellipse Fill="Red" Width="20" Height="20" Margin="390,390,0,0"/>
        <TextBox x:Name="_textBoxAngle" Width="70" Height="30" TextChanged="_textBoxAngle_TextChanged" Text="0"/>
        <RepeatButton x:Name="_buttonUp" Width="30" Height="15" Margin="70,0,0,0" Click="_buttonUp_Click"/>
        <RepeatButton x:Name="_buttonDown" Width="30" Height="15" Margin="70,15,0,0" Click="_buttonDown_Click"/>
    </Canvas>
</Window>

CodeBehind:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace RenderTransformTester
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow
    {
        static double _left = -1;
        static double _top = -1;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void _textBoxAngle_TextChanged(object sender, TextChangedEventArgs e)
        {
            int angle;
            int.TryParse(_textBoxAngle.Text, out angle);
            angle += 720;
            angle %= 360;

            _gridImage.LayoutTransform = new RotateTransform(angle, 0, 0);
        }

        private void _buttonUp_Click(object sender, RoutedEventArgs e)
        {
            int angle;
            int.TryParse(_textBoxAngle.Text, out angle);
            angle++;
            angle = angle % 360;
            _textBoxAngle.Text = angle.ToString();
        }

        private void _buttonDown_Click(object sender, RoutedEventArgs e)
        {
            int angle;
            int.TryParse(_textBoxAngle.Text, out angle);
            angle--;
            angle = angle % 360;
            _textBoxAngle.Text = angle.ToString();
        }

        private static double RadiansToDegrees(double radians)
        {
            return radians * 180 / Math.PI;
        }

        private static double DegreesToRadians(double degrees)
        {
            return degrees * Math.PI / 180;
        }
    }
}
Was it helpful?

Solution

You want a TranslateTransform within a TransformGroup

<Grid>
    <Grid.RenderTransform>
        <TransformGroup>
            <RotateTransform Angle="45" CenterX="0" CenterY="0" />
            <TranslateTransform X="400" Y="400" />
        </TransformGroup>
    </Grid.RenderTransform>
</Grid>

OTHER TIPS

  1. Don't use Margin for this kind of positioning, use the attached Canvas properties, i.e. Canvas.Left, etc.
  2. You could also use a TransformGroup and apply a TranslateTransform and a RotateTransform, presumably the rotation should be done before the translation.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top