Reputation: 3
I have a problem with object transformations via Manipulation events, more precisely matrix.
Problem: After a first successfull rotation/scale/translation of my object(btw: a Rectangle), the second manipulation (a translation in this case) will move the object in the direction of its new angle. For example, if after a first rotation (new angle of 45°) I later touch the screen from right to left, the object will follow a 45° diagonal path instead of the path that I draw.
Expectation: I want my object to follow exactly the path I make on the screen regardless of its rotation.
Situation: I drop a Rectangle in a Canvas by code, this rectangle has "IsManipulationEnabled = true" and a "RenderTransformOrigin = new Point(.5, .5)" and "mySuperRectangle.ManipulationDelta += Container_ManipulationDelta;"
This is the code I use:
private void Container_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
try
{
if (e.OriginalSource != null)
{
Rectangle image = e.OriginalSource as Rectangle;
Point center = new Point(image.ActualWidth / 2.0, image.ActualHeight / 2.0);
Matrix imageMatrix = ((MatrixTransform)image.RenderTransform).Matrix;
center = imageMatrix.Transform(center);
// Move the Rectangle.
imageMatrix.Translate(e.DeltaManipulation.Translation.X,
e.DeltaManipulation.Translation.Y);
// Rotate the Rectangle.
imageMatrix.RotateAt(e.DeltaManipulation.Rotation,
center.X,
center.Y);
// Resize the Rectangle. Keep it square
// so use only the X value of Scale.
imageMatrix.ScaleAt(e.DeltaManipulation.Scale.X,
e.DeltaManipulation.Scale.X,
center.X,
center.Y);
// Apply changes
image.RenderTransform = new MatrixTransform(imageMatrix);
Rect containingRect =
new Rect(((FrameworkElement)e.ManipulationContainer).RenderSize);
Rect shapeBounds =
image.RenderTransform.TransformBounds(
new Rect(image.RenderSize));
// Check if the rectangle is completely in the window.
// If it is not and intertia is occuring, stop the manipulation.
if (e.IsInertial && !containingRect.Contains(shapeBounds))
{
e.Complete();
}
e.Handled = true;
}
}
catch (Exception)
{
throw;
}
}
Any ideas ?
Thanx a lot.
Upvotes: 0
Views: 716
Reputation: 3
Final code for me:
private void CanvasManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
var transform = rectangle.RenderTransform as MatrixTransform;
var matrix = transform.Matrix;
Point center = new Point(rectangle.ActualWidth / 2.0, rectangle.ActualHeight / 2.0);
center = matrix.Transform(center);
matrix.ScaleAt(e.DeltaManipulation.Scale.X , e.DeltaManipulation.Scale.X, center.X, center.Y);
matrix.RotateAt(e.DeltaManipulation.Rotation, center.X, center.Y);
matrix.Translate(e.DeltaManipulation.Translation.X, e.DeltaManipulation.Translation.Y);
rectangle.RenderTransform = new MatrixTransform(matrix);
}
Edit: Don't use the as
operator without checking the result for null
. In this case, if transform
is null, create a new MatrixTransform and assign it to the RenderTransform
property. Afterwards, reuse the transform and just update its Matrix
property.
Improved code:
private void CanvasManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
var transform = rectangle.RenderTransform as MatrixTransform;
if (transform == null) // here
{
transform = new MatrixTransform();
rectangle.RenderTransform = transform;
}
var matrix = transform.Matrix;
var center = new Point(rectangle.ActualWidth / 2.0, rectangle.ActualHeight / 2.0);
center = matrix.Transform(center);
matrix.ScaleAt(e.DeltaManipulation.Scale.X , e.DeltaManipulation.Scale.X, center.X, center.Y);
matrix.RotateAt(e.DeltaManipulation.Rotation, center.X, center.Y);
matrix.Translate(e.DeltaManipulation.Translation.X, e.DeltaManipulation.Translation.Y);
transform.Matrix = matrix; // here
}
Upvotes: 0
Reputation: 128180
You should handle the manipulation event on the Canvas, because the origin of the manipulation should be relative to the fixed Canvas, instead of the moving Rectangle.
<Canvas IsManipulationEnabled="True"
ManipulationDelta="CanvasManipulationDelta">
<Rectangle x:Name="rectangle" Width="200" Height="200" Fill="Red">
<Rectangle.RenderTransform>
<MatrixTransform/>
</Rectangle.RenderTransform>
</Rectangle>
</Canvas>
The event handler would work like this:
private void CanvasManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
var transform = (MatrixTransform)rectangle.RenderTransform;
var matrix = transform.Matrix;
var center = e.ManipulationOrigin;
var scale = (e.DeltaManipulation.Scale.X + e.DeltaManipulation.Scale.Y) / 2;
matrix.ScaleAt(scale, scale, center.X, center.Y);
matrix.RotateAt(e.DeltaManipulation.Rotation, center.X, center.Y);
matrix.Translate(e.DeltaManipulation.Translation.X, e.DeltaManipulation.Translation.Y);
transform.Matrix = matrix;
}
Upvotes: 1