Wpf: How to translateTransform a Rectangle?

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);
            }

        }
1

3 Answers

Mvvm Behavior Approach add the refference to: System.Windows.Interactivity.

  1. 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>
    
  2. 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;
    }
    
    1. 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.
  1. 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>
    
  2. 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

0

You should not create the transform every time otherwise you reset the translation.

Create a TranslateTransform if none exist.

Then cumulate the translations inside.

Regards

1

Try 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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Sophia Al-Mansoor

Sophia Al-Mansoor

Global Business & E-Commerce Reporter

Sophia analyzes international trade, startup ecosystems, retail transformation, and supply chain logistics for modern digital publications.

Share this article
Twitter Facebook Pinterest