WPF.
(I applogize upfront for the seemingly wall of code, but the problem seems complex to me and I wanted to be as complete as necessary).
I have an Image that is dropped onto an InkCanvas:
Resising of path outline durring DragDelts shows:
After DragCompleted. Note: I WANT THE TOP LEFT CORNER TO BE AT THE POSITION OF THE "X" WHERE THE CURSOR IS AT TIME OF DRAGCOMPLETE:
Here is the code that I'm using to achieve the above images. Essentially, the problem comes down to calculating the correct translation transform after the image has been resized. I have tried all that I can think of without success. What is truly perplexing is that the offset from the cursor seems in someway dependent on the position the Image is dropped onto the InkCanvas. That is, if the image is dropped closer to the left border, the offset is more pronounced then if it were dropped in the center. Very confusing.
private void IC_Drop(object sender, DragEventArgs e)
{
InkCanvas ic = sender as InkCanvas;
ic.EditingMode = InkCanvasEditingMode.None;
ImageInfo image_Info = e.Data.GetData(typeof(ImageInfo)) as ImageInfo;
if (image_Info != null)
{
Image image = new Image();
image.Width = image_Info.Width * 4;
image.Stretch = Stretch.Fill;
image.Source = new BitmapImage(image_Info.Uri);
Point position = e.GetPosition(ic);
TranslateTransform mov = new TranslateTransform(position.X, position.Y);
image.RenderTransform = mov;
ic.Children.Add(image);
ImageResizing imgResize = ImageResizing.Create(image);
}
}
ImageResizing.Create() now adds the rendertransforms and thumb adorners to the image for resizing, movement, and rotation:
private ImageResizing(Image image)
{
if (image == null)
throw new ArgumentNullException("image");
_image = image;
ScaleTransform scale = new ScaleTransform();
RotateTransform rot = new RotateTransform();
TranslateTransform mov = new TranslateTransform();
TransformGroup tg = new TransformGroup();
tg.Children.Add(image.RenderTransform);
tg.Children.Add(scale);
tg.Children.Add(rot);
tg.Children.Add(mov);
image.RenderTransform = tg;
image.RenderTransformOrigin = new Point(0.5,0.5);
// Create the adorner.
_adorner = new MyImageAdorner(image);
// Get the Adorner Layer and add the Adorner.
InstallAdorner();
}
The TopLeft Thumb adorner is defined with:
Thumb topLeft;
Path outline;
public MyImageAdorner(UIElement adornedElement)
: base(adornedElement)
{
visualChildren = new VisualCollection(this);
// Initialize the Resizing (i.e., corner) thumbs with specialized cursors.
BuildAdornerCorner(ref topLeft, Cursors.SizeNWSE);
topLeft.DragDelta += new DragDeltaEventHandler(TopLeft_DragDelta);
topLeft.DragCompleted += TopLeft_DragCompleted;
}
// Helper method to instantiate the corner Thumbs, set the Cursor property,
// set some appearance properties, and add the elements to the visual tree.
void BuildAdornerCorner(ref Thumb cornerThumb, Cursor customizedCursor)
{
if (cornerThumb != null) return;
cornerThumb = new Thumb();
// Set some arbitrary visual characteristics.
cornerThumb.Cursor = customizedCursor;
cornerThumb.Height = cornerThumb.Width = 15;
cornerThumb.Opacity = 0.40;
cornerThumb.Background = new SolidColorBrush(Colors.MediumBlue);
visualChildren.Add(cornerThumb);
}
I am using the DragDelta to position the outline path element to the final desired size like so:
// Top Left Corner is being dragged. Anchor is Bottom Right.
void TopLeft_DragDelta(object sender, DragDeltaEventArgs e)
{
ScaleTransform sT = new ScaleTransform(1 - e.HorizontalChange / outline.ActualWidth, 1 - e.VerticalChange / outline.ActualHeight,
outline.ActualWidth, outline.ActualHeight);
outline.RenderTransform = sT;
}
Note that I am intending the TopLeft corner to resize the image from the bottom right corner. Hence all points of the image are to move away from the bottom right corner as the TopLeft corner is dragged.
Finally, I am using the DragCompleted event to actually perform the new rendering on the Image:
private void TopLeft_DragCompleted(object sender, DragCompletedEventArgs e)
{
// Get new scaling from the Outline.
ScaleTransform sT = outline.RenderTransform as ScaleTransform;
double anchorX = sT.CenterX;
double anchorY = sT.CenterY;
// Get the previous scaling
TransformGroup gT = AdornedElement.RenderTransform as TransformGroup;
ScaleTransform sT0 = gT.Children[1] as ScaleTransform;
double oldscaleX = sT0.ScaleX;
double oldscaleY = sT0.ScaleY;
double oldCenterX = anchorX - (outline.ActualWidth ) * oldscaleX;
double oldCenterY = anchorY - (outline.ActualHeight) * oldscaleY;
// Get the previous translation
TranslateTransform tT = gT.Children[3] as TranslateTransform;
sT0.CenterX = anchorX;
sT0.CenterY = anchorY;
sT0.ScaleX *= sT.ScaleX;
sT0.ScaleY *= sT.ScaleY;
double newCenterX = anchorX -(outline.ActualWidth) * sT0.ScaleX;
double newCenterY = anchorY -(outline.ActualHeight) * sT0.ScaleY;
tT.X += newCenterX - oldCenterX;
tT.Y += newCenterY - oldCenterY;
// Put transforms back.
gT.Children[1] = sT0;
gT.Children[3] = tT;
AdornedElement.RenderTransform = gT;
outline.RenderTransform = Transform.Identity;
}
And for completeness, the adorner is positioned as:
protected override Size ArrangeOverride(Size finalSize)
{
double eW = AdornedElement.RenderSize.Width;
double eH = AdornedElement.RenderSize.Height;
FrameworkElement ele = AdornedElement as FrameworkElement;
EnforceSize(ele);
eH = ele.Height;
eW = ele.Width;
Size aS = new Size(eW, eH);
// calculate the center of the image.
var center = new Point(eW / 2, eH / 2);
topLeft.Arrange(new Rect(new Point(-center.X, -center.Y), aS));
topRight.Arrange(new Rect(new Point(center.X, -center.Y), aS));
bottomLeft.Arrange(new Rect(new Point(-center.X, center.Y), aS));
bottomRight.Arrange(new Rect(new Point(center.X, center.Y), aS));
// The RotateHandle is placed slightly above the Top of the Image center.
// The MoveHandle is placed at the center of the Image.
rotateHandle.Arrange(new Rect(new Point(0, -(center.Y + HANDLEMARGIN)), aS));
moveHandle.Arrange(new Rect(new Point(0, 0), aS));
// Place a blue outline arround the image.
outline.Data = new RectangleGeometry(new Rect(aS));
outline.Arrange(new Rect(aS));
// Return the final size.
return aS;
}
I would surely appreciate any help or suggestions. What am I doing wrong?
TIA
Note: This question is answered Scaling causing Skewing