I'm trying to move a rectangle drawn in Canvas by mouse (drag and drop).
When I click on the Rectangle and move the mouse (keep clicking) to a new position it's OK:
But when I click on it again and move the mouse just a bit the Rectangle move to old position:
I don't know why it is. Can anybody give me some explaination?
Here is my code (all parameters have been assigned and initialized). Thanks in advance!
private void MyCanvas_MouseMove_1(object sender, MouseEventArgs e) { if (e.RightButton == MouseButtonState.Pressed && e.OriginalSource is Shape) { p2 = e.GetPosition(MyCanvas); TranslateTransform tf = new TranslateTransform(p2.X - p1.X, p2.Y - p1.Y); _MyTestRect.RenderTransform = tf; } } private void MyCanvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e) { if (e.OriginalSource is Shape) { p1 = e.GetPosition(MyCanvas); } } 13 Answers
Mvvm Behavior Approach add the refference to: System.Windows.Interactivity.
In XAML:
<Canvas x:Name="MyCanvas" Background="#00FFFFFF"> <i:Interaction.Behaviors> <!--soSandBox is the reffrence to the behavior--> <soSandBox:MoveElementsInCanvasBehavior/> </i:Interaction.Behaviors> <Rectangle x:Name="_MyTestRect1" Fill="Tomato" Width="50" Height="50"/> <Rectangle x:Name="_MyTestRect2" Fill="Tomato" Width="50" Height="50"/> <Rectangle x:Name="_MyTestRect3" Fill="Tomato" Width="50" Height="50"/></Canvas>Behavior code:
protected override void OnAttached() { base.OnAttached(); AssociatedObject.MouseLeftButtonDown += AssociatedObjectOnMouseLeftButtonDown; AssociatedObject.MouseLeftButtonUp += AssociatedObjectOnMouseLeftButtonUp; AssociatedObject.MouseMove += AssociatedObjectOnMouseMove; } private void AssociatedObjectOnMouseMove(object sender, MouseEventArgs e) { if (_shape != null && _shape.IsMouseCaptured) { _p2 = e.GetPosition(AssociatedObject); _shape.SetValue(Canvas.TopProperty, _p2.Y - _originalOffsetFromTop); _shape.SetValue(Canvas.LeftProperty, _p2.X - _originalOffsetFromLeft); } } private void AssociatedObjectOnMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { if (_shape == null) return; _shape.ReleaseMouseCapture(); _shape = null; } private double GetDist(double originalValue, double newValue) { if (double.IsNaN(originalValue) || double.IsInfinity(originalValue)) return Math.Abs(newValue - 0); return Math.Abs(newValue - originalValue); } private void AssociatedObjectOnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { _shape = e.OriginalSource as Shape; if (_shape != null) { _p1 = e.GetPosition(AssociatedObject); _shape.CaptureMouse(); _originalOffsetFromTop = GetDist((double)_shape.GetValue(Canvas.TopProperty), _p1.Y); _originalOffsetFromLeft = GetDist((double)_shape.GetValue(Canvas.LeftProperty), _p1.X); } } protected override void OnDetaching() { base.OnDetaching(); AssociatedObject.MouseLeftButtonDown -= AssociatedObjectOnMouseLeftButtonDown; AssociatedObject.MouseLeftButtonUp -= AssociatedObjectOnMouseLeftButtonUp; AssociatedObject.MouseMove -= AssociatedObjectOnMouseMove; }- Summary: This way you can put inside the canvas as much elements as you wish, and when you click the element (and when this element is a shape) you will be able to move it.
Translate Transform XAML:
<Canvas x:Name="MyCanvas" Background="#00FFFFFF"> <i:Interaction.Behaviors> <!--convert event to command mechanism--> <soSandBox:CanvasMouseEventObservingBehavior OnMouseMoveAction="{Binding OnMouseMoveAction, UpdateSourceTrigger=PropertyChanged}" OnMouseRightButtonDownAction="{Binding OnMouseRightButtonDownAction, UpdateSourceTrigger=PropertyChanged}" OnMouseRightButtonUpAction="{Binding OnMouseRightButtonUpAction, UpdateSourceTrigger=PropertyChanged}"/> </i:Interaction.Behaviors> <Rectangle x:Name="_MyTestRect1" Fill="Tomato" Width="50" Height="50"> <Rectangle.RenderTransform> <TransformGroup> <TranslateTransform X="{Binding TranslateTransformX, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Y="{Binding TranslateTransformY, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"></TranslateTransform> </TransformGroup> </Rectangle.RenderTransform> </Rectangle></Canvas>Translate Transform view model (translation related code):
private void RightMouseUp(Point obj) { _originalPoint = obj; _currentOffsetByX = GetDist(_currentOffsetByX, obj.X); _currentOffsetByY = GetDist(_currentOffsetByY, obj.Y); } private void RightMouseDown(Point obj) { _originalPoint = obj; _currentOffsetByX = GetDist(_currentOffsetByX, obj.X); _currentOffsetByY = GetDist(_currentOffsetByY, obj.Y); } private void MouseMoved(Point obj) { TranslateTransformX = obj.X - _currentOffsetByX; TranslateTransformY = obj.Y - _currentOffsetByY; } private double GetDist(double originalValue, double newValue) { if (double.IsNaN(originalValue) || double.IsInfinity(originalValue)) return Math.Abs(newValue - 0); return Math.Abs(newValue - originalValue); } public double TranslateTransformY { get { return _translateTransformY; } set { _translateTransformY = value; OnPropertyChanged(); } } public double TranslateTransformX { get { return _translateTransformX; } set { _translateTransformX = value; OnPropertyChanged(); } }
Regards
0You should not create the transform every time otherwise you reset the translation.
Create a TranslateTransform if none exist.
Then cumulate the translations inside.
Regards
1Try just using the Canvas.SetTop and Canvas.SetLeft.
Rectangle myRectangle = new Rectangle(); myRectangle += myRectangle_MouseLeftButtonDown; void myRectangle_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { myRectangle.MouseMove += origin_MouseMove; } void myRectangle_MouseMove(object sender, System.Windows.Input.MouseEventArgs e) { Point p = new Point() { // Just round the coordinates X = Math.Round(e.GetPosition(canvas).X, 0, MidpointRounding.AwayFromZero), Y = Math.Round(e.GetPosition(canvas).Y, 0, MidpointRounding.AwayFromZero), }; Canvas.SetTop(myRectangle, p.Y); Canvas.SetLeft(myRectangle, p.X); myRectangle.MouseLeftButtonUp += myRectangle_MouseLeftButtonUp; } void myRectangle_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e) { myRectangle.MouseMove -= origin_MouseMove; myRectangle.MouseLeftButtonUp -= origin_MouseLeftButtonUp; } 0 