Взаимное перемещение UIElement на Canvas. wpf c#

Я создаю линию на канвасе к которой привязываю события перемещения мышью. введите сюда описание изображения

При клике на которую на концах появляются эллипсы за которые можно тянуть линию изменяя ее положение на канвасе. При перемещении эллипса он тянет за собой конец к которому привязан.

Также при перемещении самой линии, эллипсы перемещаются вместе с ним.

В коде у меня это выглядит так:

// класс для хранения фигур
abstract class BlockSchemeElement
    {
        public abstract void Move(Canvas canvas, Point currentMousePosition, Point lastMousePosition);

// Линия
class ProcessLine : BlockSchemeElement
    {
        public static double DEFAULT_X1 = 200,
                             DEFAULT_X2 = 300,
                             DEFAULT_Y1 = 200,
                             DEFAULT_Y2 = 300,
                             DEFAULT_THICKNESS = 4;

        public Line Line { get; set; }

        public override void Move(Canvas canvas, Point currentMousePosition, Point lastMousePosition)
        {
            double dx = currentMousePosition.X - lastMousePosition.X;
            double dy = currentMousePosition.Y - lastMousePosition.Y;

            // перемещение линии
            Canvas.SetLeft(Line, dx);
            Canvas.SetTop(Line, dy);

            // привязанные эллипсы
            List<MovingEllipse> movingEllipses = Line.Tag as List<MovingEllipse>;

            // перемещение эллипсов
            Canvas.SetLeft(movingEllipses[0].Ellipse, Line.X1 + Canvas.GetLeft(Line) - MovingEllipse.ELLIPSE_SIZE / 2);
            Canvas.SetTop(movingEllipses[0].Ellipse, Line.Y1 + Canvas.GetTop(Line) - MovingEllipse.ELLIPSE_SIZE / 2);

            Canvas.SetLeft(movingEllipses[1].Ellipse, Line.X2 + dx - MovingEllipse.ELLIPSE_SIZE / 2);
            Canvas.SetTop(movingEllipses[1].Ellipse, Line.Y2 + dy - MovingEllipse.ELLIPSE_SIZE / 2);
            
        }
    }

// Эллипс
class MovingEllipse : BlockSchemeElement
    {
        public static double ELLIPSE_SIZE = 16;

        public Ellipse Ellipse { get; set; }

        public override void Move(Canvas canvas, Point currentMousePosition, Point lastMousePosition)
        {
            double dx = currentMousePosition.X - lastMousePosition.X;
            double dy = currentMousePosition.Y - lastMousePosition.Y;

            // перемещение эллипса
            Canvas.SetLeft(Ellipse, dx);
            Canvas.SetTop(Ellipse, dy);

            if(Ellipse.Tag.GetType() == typeof(ProcessLine))
            {              
                ProcessLine processLine = Ellipse.Tag as ProcessLine;

                // определеляем конец линии и перемещаем
                if (Ellipse.Name == "ellipse0")
                {
                    processLine.Line.X1 = Canvas.GetLeft(Ellipse);
                    processLine.Line.Y1 = Canvas.GetTop(Ellipse);
                }
                else if(Ellipse.Name == "ellipse1")
                {
                    processLine.Line.X2 = Canvas.GetLeft(Ellipse);
                    processLine.Line.Y2 = Canvas.GetTop(Ellipse);
                }
            } 
        }
    }

public partial class MainWindow : Window
    {

        private bool isAttached = false;
        private Point currentMousePosition, lastMousePosition;

        private ProcessLine processLine;
        private ProcessBlock processBlock;
        private BlockSchemeElement blockSchemeElement;

        private List<MovingEllipse> movingEllipses;
       
        public MainWindow()
        {
            InitializeComponent();

            //привязка событий канвасу
            canvas.MouseLeftButtonUp += CanvasOnMouseLeftButtonUp;
            canvas.MouseMove += CanvasOnMouseMove;

            processLine = new ProcessLine();
            processBlock = new ProcessBlock();

            //------------инициализировать эллипсы
            movingEllipses = new List<MovingEllipse>();          

            movingEllipses.Add(new MovingEllipse());
            movingEllipses.Add(new MovingEllipse());

            movingEllipses[0].Ellipse = DrawingFigures.DrawEllipse();
            movingEllipses[1].Ellipse = DrawingFigures.DrawEllipse();

            movingEllipses[0].Ellipse.MouseLeftButtonDown += ElementOnMouseLeftButtonDown;
            movingEllipses[1].Ellipse.MouseLeftButtonDown += ElementOnMouseLeftButtonDown;

            movingEllipses[0].Ellipse.Name = "ellipse0";
            movingEllipses[1].Ellipse.Name = "ellipse1";
        }

        //----------Events--------------

        // отпускаем мышь от фигуры
        private void CanvasOnMouseLeftButtonUp(object sender, MouseButtonEventArgs mouseButtonEventArgs)
        {
            isAttached = false;
        }

        // движение мыши по канвасу
        private void CanvasOnMouseMove(object sender, MouseEventArgs mouseEventArgs)
        {
            if (isAttached)
            {
                currentMousePosition = mouseEventArgs.GetPosition(sender as FrameworkElement);
                blockSchemeElement.Move(canvas,currentMousePosition, lastMousePosition);      
            }
        }

        private void ElementOnMouseLeftButtonDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
        {
            lastMousePosition = mouseButtonEventArgs.GetPosition(sender as FrameworkElement);

            // определяем тип элемента
            switch (sender.GetType().Name)
            {
                case nameof(Line):
                    processLine.Line = sender as Line;

                    blockSchemeElement = processLine;

                    //------------привязать эллипсы

                    // установить координаты эллипсов на канвасе
                    Canvas.SetLeft(movingEllipses[0].Ellipse, processLine.Line.X1 - MovingEllipse.ELLIPSE_SIZE/2);
                    Canvas.SetTop(movingEllipses[0].Ellipse, processLine.Line.Y1 - MovingEllipse.ELLIPSE_SIZE / 2);

                    Canvas.SetLeft(movingEllipses[1].Ellipse, processLine.Line.X2 - MovingEllipse.ELLIPSE_SIZE / 2);
                    Canvas.SetTop(movingEllipses[1].Ellipse, processLine.Line.Y2 - MovingEllipse.ELLIPSE_SIZE / 2);

                    // если уже были добавлены на канвас не добавлять
                    if (!canvas.Children.Contains(movingEllipses[0].Ellipse) && !canvas.Children.Contains(movingEllipses[1].Ellipse))
                    {
                        canvas.Children.Add(movingEllipses[0].Ellipse);
                        canvas.Children.Add(movingEllipses[1].Ellipse);
                    }

                    // привязать ссылку на линию обоим эллипсам
                    movingEllipses[0].Ellipse.Tag = processLine;
                    movingEllipses[1].Ellipse.Tag = processLine;

                    // привязать ссылку на эллипсы линии
                    processLine.Line.Tag = movingEllipses;

                    break;

                
                case nameof(Ellipse):

                    // поиск выбранного эллипса
                    Ellipse desiredEllipse = sender as Ellipse;
                    MovingEllipse movingEllipse = movingEllipses.Find(ellipse => ellipse.Ellipse == desiredEllipse);
                    
                    blockSchemeElement = movingEllipse;
                    break;
            }

            isAttached = true;
        }      


        // --------Buttons--------------

        private void ButtonDrawLine_Click(object sender, RoutedEventArgs e)
        {
            Line line = DrawingFigures.DrawLine();

            canvas.Children.Add(line);

            line.MouseLeftButtonDown += ElementOnMouseLeftButtonDown;
        }

    }

static class DrawingFigures
    {
        public static Line DrawLine()
        {
            Line line = new Line()
            {
                X1 = ProcessLine.DEFAULT_X1,
                X2 = ProcessLine.DEFAULT_X2,
                Y1 = ProcessLine.DEFAULT_Y1,
                Y2 = ProcessLine.DEFAULT_Y2,

                StrokeThickness = ProcessLine.DEFAULT_THICKNESS,
                Stroke = System.Windows.Media.Brushes.Black,
            };

            return line;
        }

        public static Rectangle DrawRectangle()
        {
            Rectangle rectangle = new Rectangle()
            {
                Width = ProcessBlock.DEFAULT_WIDTH,
                Height = ProcessBlock.DEFAULT_HEIGHT,
                Fill = System.Windows.Media.Brushes.White,
                Stroke = System.Windows.Media.Brushes.Black,
                StrokeThickness = ProcessBlock.DEFAULT_THICKNESS,
            };

            return rectangle;
        }


        public static Ellipse DrawEllipse()
        {
            Ellipse ellipse = new Ellipse()
            {
                Height = MovingEllipse.ELLIPSE_SIZE,
                Width = MovingEllipse.ELLIPSE_SIZE,
                Fill = new SolidColorBrush(Colors.Blue)
            };

            return ellipse; 
        }

    }

Проблема в том, что я уже долгое время не могу добиться корректного взаимного перемещения. Может быть можно как-то проще, или ошибка в самих выражениях установки координат для фигур. После перемещения самой линии при попытке потянуть ее за эллипс происходит такая картина.

введите сюда описание изображения

При чем эллипс улетает от линии на то расстояние, на которое была перемещена линия до этого.


Ответы (0 шт):