Hi All,
I wrote utility that has border and rectangle in it.
I can drag the rectangle out of the border, and i can do TranslateTransform and RotateTransform on the rectangle.
The rectangle has RenderTransformOrigin set to : 0.5,0.5.

When i drag the rectangle and do rotate and then drag again, all is working just fine, the rectangle rotate around his center Point (RenderTransformOrigin=0.5,0.5).

Now i want that the rotation center will be from the center of the border, even if i dragged the rectangle out of the border.
When i change the CenterX and CenterY properties of the RotateTransform, to be fit with the offset of the rectangle position (So that the rotation center will be the center of the border), and then try to drag again, the rectangle isn't drag to the right position.
what is wrong ?

The Xaml File:

<Window x:Class="TransformationTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:TransformationTest"
Title="TransformationTest" Height="700" Width="1000"
x:Name="MyWindow" >

<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>

<Border BorderBrush="Black" BorderThickness="2" Grid.Column="0"
KeyboardNavigation.DirectionalNavigation="None"
KeyboardNavigation.ControlTabNavigation="None"
KeyboardNavigation.IsTabStop="False"
KeyboardNavigation.TabNavigation="None">
<StackPanel>
<Border BorderBrush="LightBlue" BorderThickness="1" Margin="3">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Rotation: " Margin="3"/>
<TextBox Width="50" Margin="3" Text="{Binding ElementName=MyWindow, Path=Angle}"/>
</StackPanel>
</Border>

<Border BorderBrush="LightBlue" BorderThickness="1" Margin="3">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Flip X: " Margin="3"/>
<TextBox Text="{Binding ElementName=MyWindow, Path=FlipX}" Margin="3" Width="25"/>
<Button Content="Flip X" Margin="3" Click="FlipXClick" />
</StackPanel>
</Border>

<Border BorderBrush="LightBlue" BorderThickness="1" Margin="3">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Flip Y: " Margin="3"/>
<TextBox Text="{Binding ElementName=MyWindow, Path=FlipY}" Margin="3" Width="25"/>
<Button Content="Flip Y" Margin="3" Click="FlipYClick"/>
</StackPanel>
</Border>

<Border BorderBrush="LightBlue" BorderThickness="1" Margin="3">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Translate X: " Margin="3"/>
<TextBox x:Name="TranslateX" Width="50" Margin="3" Text="{Binding ElementName=MyWindow, Path=OffsetX}"/>
<TextBlock Text="Translate Y: " Margin="3"/>
<TextBox x:Name="TranslateY" Width="50" Margin="3" Text="{Binding ElementName=MyWindow, Path=OffsetY}"/>
</StackPanel>
</Border>

<Border BorderBrush="LightBlue" BorderThickness="1" Margin="3">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Scale X: " Margin="3"/>
<TextBox x:Name="ScaleX" Width="50" Margin="3" Text="1"/>
<TextBlock Text="Scale Y: " Margin="3"/>
<TextBox x:Name="ScaleY" Width="50" Margin="3" Text="1"/>
</StackPanel>
</Border>

<Border BorderBrush="LightBlue" BorderThickness="1" Margin="3">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Transform Origin: " Margin="3"/>
<RadioButton Name="rb1" Checked="RenderTransfornOrigin" Content="0, 0" Margin="3"/>
<RadioButton Name="rb2" Checked="RenderTransfornOrigin" Content="0.5, 0.5" Margin="3" IsChecked="True"/>
</StackPanel>
</Border>

<Button Content="Clear Values" Click="ClearValues_Click" Margin="5" Height="35" HorizontalAlignment="Left"/>

</StackPanel>
</Border>

<Border x:Name="Sheet" BorderBrush="Black" BorderThickness="7" Grid.Column="1" ClipToBounds="True" Background="White">
<Border BorderThickness="1" BorderBrush="Black" Height="100" Width="100" >

<Grid x:Name="MyGrid" RenderTransformOrigin="0.5,0.5">
<Grid.RenderTransform>
<TransformGroup>

<RotateTransform Angle="{Binding ElementName=MyWindow, Path=Angle}" x:Name="rotateTransform"/>

<ScaleTransform ScaleX="{Binding ElementName=MyWindow, Path=FlipX}" ScaleY="{Binding ElementName=MyWindow, Path=FlipY}"/>

<TranslateTransform x:Name="translateTransform"
X="{Binding ElementName=MyWindow, Path=OffsetX}"
Y="{Binding ElementName=MyWindow, Path=OffsetY}"/>

<ScaleTransform x:Name="scaleTransform" ScaleX="{Binding ElementName=ScaleX, Path=Text}" ScaleY="{Binding ElementName=ScaleY, Path=Text}"/>


</TransformGroup>


</Grid.RenderTransform>

<Rectangle x:Name="rectangle" Fill="LightBlue" Opacity="0.8"/>
<TextBlock Text="(0,0)" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<TextBlock Text="(100,100)" VerticalAlignment="Bottom" HorizontalAlignment="Right"/>
</Grid>

</Border>
</Border>

</Grid>
</Window>





The behind code:

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Controls.Primitives;
using System.Diagnostics;
using System.ComponentModel;


namespace TransformationTest
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>

public partial class Window1 : System.Windows.Window , INotifyPropertyChanged
{
private int step = 5;
private double offsetX = 0;
private double offsetY = 0;
private int angle = 0;
private double flipX = 1.0;
private double flipY = 1.0;
private bool isDragging = false;
private Point startPoint = new Point();

#region Properties
public double FlipY
{
get { return flipY; }
set { flipY = value; OnPropertyChanged(this, "FlipY"); }
}
public double FlipX
{
get { return flipX; }
set { flipX = value; OnPropertyChanged(this, "FlipX"); }
}
public int Angle
{
get { return angle; }
set
{
angle = value;
if (Math.Abs(angle) == 360)
angle = 0;

OnPropertyChanged(this, "Angle");
}
}
public double OffsetX
{
get { return offsetX; }
set { offsetX = value; OnPropertyChanged(this, "OffsetX"); }
}
public double OffsetY
{
get { return offsetY; }
set { offsetY = value; OnPropertyChanged(this, "OffsetY"); }
}

#endregion

public Window1()
{
InitializeComponent();
this.KeyDown += new KeyEventHandler(Window1_KeyDown);
MyGrid.RenderTransform.Changed += new EventHandler(RenderTransform_Changed);
}

void RenderTransform_Changed(object sender, EventArgs e)
{
Debug.WriteLine(string.Format("Debug - Matrix {0}", MyGrid.RenderTransform.Value));
}


private void FlipXClick(object sender, RoutedEventArgs e)
{
FlipX *= -1;
}

private void FlipYClick(object sender, RoutedEventArgs e)
{
FlipY *= -1;
}

void Window1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Right)
{
OffsetX += step;
}
else if (e.Key == Key.Left)
{
OffsetX -= step;
}
else if (e.Key == Key.Up)
{
OffsetY -= step;
}
else if (e.Key == Key.Down)
{
OffsetY += step;
}
else if (e.Key == Key.R && Keyboard.Modifiers == ModifierKeys.Control)
{
Angle += -90;
}


}

private void ClearValues_Click(object sender, RoutedEventArgs args)
{
Angle = 0;
FlipX = FlipY = 1;
OffsetX = OffsetY = 0;
ScaleX.Text = "1";
ScaleY.Text = "1";
isDragging = false;
rb2.IsChecked = true;
}

void RenderTransfornOrigin(object sender, RoutedEventArgs e)
{
if (MyGrid == null)
return;

RadioButton rb = sender as RadioButton;
if (rb.Name == rb1.Name)
{
MyGrid.RenderTransformOrigin = new Point();
}
else if (rb.Name == rb2.Name)
{
MyGrid.RenderTransformOrigin = new Point(0.5, 0.5);
}
}

protected override void OnMouseDoubleClick(MouseButtonEventArgs e)
{
if ((e.OriginalSource as Border) == Sheet || (e.OriginalSource as Rectangle) == rectangle)
{
if (Mouse.RightButton == MouseButtonState.Pressed)
{
Angle -= 90;
}
else
{
double scale = Math.Max(1, Math.Min(4.5, Convert.ToDouble(ScaleX.Text) + 0.5));
if (scale > 4)
scale = 1;

ScaleX.Text = scale.ToString();
ScaleY.Text = scale.ToString();
}
}
}


protected override void OnMouseDown(MouseButtonEventArgs e)
{
isDragging = true;
Cursor = Cursors.Hand;
startPoint = Mouse.GetPosition(Sheet);
base.OnMouseDown(e);
}

protected override void OnMouseMove(MouseEventArgs e)
{
if (isDragging && e.LeftButton == MouseButtonState.Pressed)
{
Point endPoint = Mouse.GetPosition(Sheet);

OffsetX += endPoint.X - startPoint.X;
OffsetY += endPoint.Y - startPoint.Y;

startPoint = endPoint;

}

base.OnMouseMove(e);
}

protected override void OnMouseUp(MouseButtonEventArgs e)
{
Cursor = Cursors.Arrow;
isDragging = false;
base.OnMouseUp(e);
}

#region INotifyPropertyChanged Members

public event PropertyChangedEventHandler PropertyChanged;

private void OnPropertyChanged(object sender, string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(sender, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}



Just change the radio button, change from "Center Object" to "Center Border" and see the the "Rotation Center X" and the "Rotation Center Y" change.

Thanks,
Eitan Gabay.