From 3dab570f5dbb5430d74026b9a987ded64dd697db Mon Sep 17 00:00:00 2001
From: Miguel <miguel.vera.csa@gmail.com>
Date: Tue, 18 Feb 2025 18:08:55 +0100
Subject: [PATCH] Multiseleccion funcionando

---
 MainViewModel.cs             |  19 +-
 MainWindow.xaml              |   5 +-
 MainWindow.xaml.cs           | 884 +++++++----------------------------
 ObjectManipulationManager.cs | 616 ++++++++++++++++++++++++
 ObjetosSim/osBase.cs         |   2 +
 5 files changed, 799 insertions(+), 727 deletions(-)
 create mode 100644 ObjectManipulationManager.cs

diff --git a/MainViewModel.cs b/MainViewModel.cs
index 285a2f6..84e0b19 100644
--- a/MainViewModel.cs
+++ b/MainViewModel.cs
@@ -112,8 +112,17 @@ namespace CtrEditor
         private bool habilitarEliminarUserControl;
 
         private MainWindow mainWindow;
+        private ObjectManipulationManager _objectManager;  // Add this line
 
-        public MainWindow MainWindow { get => mainWindow; set => mainWindow = value; }
+        public MainWindow MainWindow
+        { 
+            get => mainWindow;
+            set
+            {
+                mainWindow = value;
+                _objectManager = mainWindow._objectManager;  // Initialize _objectManager
+            }
+        }
 
         [ObservableProperty]
         public ICollectionView vistaFiltrada;
@@ -273,6 +282,14 @@ namespace CtrEditor
         [ObservableProperty]
         public ObservableCollection<osBase> objetosSimulables;
 
+        [ObservableProperty]
+        private bool isMultiSelectionActive;
+
+        partial void OnIsMultiSelectionActiveChanged(bool value)
+        {
+            _objectManager?.OnMultiSelectionModeChanged(value);
+        }
+
         partial void OnObjetosSimulablesChanged(ObservableCollection<osBase> value)
         {
             VistaFiltrada = CollectionViewSource.GetDefaultView(ObjetosSimulables);
diff --git a/MainWindow.xaml b/MainWindow.xaml
index 921e08e..f54816e 100644
--- a/MainWindow.xaml
+++ b/MainWindow.xaml
@@ -155,8 +155,9 @@
 
                 <ScrollViewer Grid.Row="1" x:Name="ImagenEnTrabajoScrollViewer" HorizontalScrollBarVisibility="Auto"
                         VerticalScrollBarVisibility="Auto" PanningMode="Both">
-                    <Canvas x:Name="ImagenEnTrabajoCanvas" Margin="0">
-                        <!-- El Margin puede ser ajustado según el espacio adicional que quieras proporcionar -->
+                    <Canvas x:Name="ImagenEnTrabajoCanvas" Margin="0" Background="Transparent">
+                        <!-- Agregar Background="Transparent" para que capture los eventos del mouse y -->
+                        <!-- asegurar que el Canvas reciba los eventos del botón derecho -->
                         <Canvas.RenderTransform>
                             <TransformGroup>
                                 <ScaleTransform x:Name="CanvasScaleTransform" ScaleX="1" ScaleY="1" />
diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs
index f4ef896..684027e 100644
--- a/MainWindow.xaml.cs
+++ b/MainWindow.xaml.cs
@@ -4,25 +4,19 @@ using System.Windows.Controls;
 using System.Windows.Input;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
-using MouseEventArgs = System.Windows.Input.MouseEventArgs;
-using UserControl = System.Windows.Controls.UserControl;
-using CtrEditor.ObjetosSim;
 using System.Windows.Threading;
 using System.Diagnostics;
-using CtrEditor.ObjetosSim.UserControls;
-using DocumentFormat.OpenXml.Spreadsheet;
-using System.Windows.Shapes;
-using System.Numerics;
-using CommunityToolkit.Mvvm.Input;
+using CtrEditor.ObjetosSim;
 using CtrEditor.FuncionesBase;
-using Color = System.Windows.Media.Color;
+using Xceed.Wpf.Toolkit.PropertyGrid;
+using MouseEventArgs = System.Windows.Input.MouseEventArgs;
+using UserControl = System.Windows.Controls.UserControl;
 
 namespace CtrEditor
 {
     /// <summary>
     /// Interaction logic for MainWindow.xaml
     /// </summary>
-    /// 
     public partial class MainWindow : Window
     {
         // Para el Canvas
@@ -30,11 +24,9 @@ namespace CtrEditor
         private bool _isDrawingCanvas = false;
         private bool _isDraggingCanvas = false;
         private Image imagenDeFondo;
+        internal ObjectManipulationManager _objectManager;
 
-        private List<Rectangle> resizeRectangles = new List<Rectangle>();
-        private List<Rectangle> highlightRectangles = new List<Rectangle>(); // Nueva lista para los rectángulos de resaltado
-        private System.Threading.Timer timerRemoveHighlight = null;
-
+        // Temporizadores y animación
         private DispatcherTimer _zoomTimer;
         private double _targetZoomFactor;
         private double _initialZoomFactor;
@@ -45,33 +37,32 @@ namespace CtrEditor
         private const double MinZoomScale = 0.1; // Límite mínimo de zoom
         private Stopwatch _stopwatch;
 
-        // Para los UserControl
-        private Point _startPointUserControl;
-        private UserControl _currentDraggingControl;
-        private bool _isRotatingUserControl = false;
-        private bool _isResizingUserControl = false;
-        private bool _isDraggingUserControl = false;
-        private bool _isMovingUserControl = false;
-        private TextBlock _angleDisplayTextBlock;
+        private dataDebug dataDebug = new dataDebug();
 
         public MainWindow()
         {
             InitializeComponent();
 
+            _objectManager = new ObjectManipulationManager(this, ImagenEnTrabajoCanvas);
+
+            // Inicializar temporizador de zoom
             _zoomTimer = new DispatcherTimer();
             _zoomTimer.Interval = TimeSpan.FromMilliseconds(1);
             _zoomTimer.Tick += ZoomTimer_Tick;
             _stopwatch = new Stopwatch();
 
+            // Suscribir eventos
             this.Loaded += MainWindow_Loaded;
             ImagenEnTrabajoScrollViewer.PreviewMouseWheel += ImagenEnTrabajoCanvas_MouseWheel;
             ImagenEnTrabajoCanvas.MouseDown += Canvas_MouseDown_Panning;
             ImagenEnTrabajoCanvas.MouseMove += Canvas_MouseMove_Panning;
             ImagenEnTrabajoCanvas.MouseUp += Canvas_MouseUp_Panning;
-
-            // Agregar el evento KeyDown a la ventana
             this.KeyDown += MainWindow_KeyDown;
             ImagenEnTrabajoCanvas.MouseEnter += Canvas_MouseEnter;
+            this.Closed += MainWindow_Closed;
+
+            // Importante: Agregar el evento para el menú contextual
+            ImagenEnTrabajoCanvas.MouseRightButtonDown += Canvas_MouseRightButtonDown;
         }
 
         private void MainWindow_Loaded(object sender, RoutedEventArgs e)
@@ -80,28 +71,15 @@ namespace CtrEditor
             {
                 viewModel.MainWindow = this;
                 viewModel.ImageSelected += ViewModel_ImageSelected;
-                viewModel?.LoadInitialData(); // Carga la primera imagen por defecto una vez cargada la ventana principal
+                viewModel?.LoadInitialData();
                 viewModel.simulationManager.DebugCanvas = ImagenEnTrabajoCanvas;
                 viewModel.MainCanvas = ImagenEnTrabajoCanvas;
             }
         }
 
-        public (float X, float Y) ObtenerCentroCanvasMeters()
-        {
-            var c = ObtenerCentroCanvasPixels();
-            return (PixelToMeter.Instance.calc.PixelsToMeters(c.X), PixelToMeter.Instance.calc.PixelsToMeters(c.Y));
-        }
-
         public void SuscribirEventos(UserControl userControl)
         {
-            userControl.MouseEnter += UserControl_MouseEnter;
-            userControl.MouseLeave += UserControl_MouseLeave;
-            userControl.Cursor = Cursors.Hand;  // Agregar cursor de mano
-
-            // Suscribir a eventos de mouse para panning
-            userControl.MouseLeftButtonDown += UserControl_MouseLeftButtonDown;
-            userControl.MouseLeftButtonUp += UserControl_MouseLeftButtonUp;
-            userControl.MouseMove += UserControl_MouseMove;
+            _objectManager.SuscribirEventos(userControl);
         }
 
         public void AgregarRegistrarUserControlCanvas(UserControl userControl)
@@ -109,8 +87,6 @@ namespace CtrEditor
             if (userControl is IDataContainer dataContainer)
             {
                 SuscribirEventos(userControl);
-
-                // Añade el UserControl al Canvas
                 Canvas.SetZIndex(userControl, ((int)dataContainer.ZIndex_Base() + dataContainer.zIndex_fromFrames));
                 ImagenEnTrabajoCanvas.Children.Add(userControl);
             }
@@ -124,594 +100,6 @@ namespace CtrEditor
             }
         }
 
-        private void UserControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
-        {
-            if (!_isDrawingCanvas)
-            {
-                // Si el sender es un rectángulo de redimensionamiento
-                if (resizeRectangles != null && resizeRectangles.Contains(sender))
-                {
-                    _isResizingUserControl = true;
-                    lastMousePosition = e.GetPosition(ImagenEnTrabajoCanvas);
-                    ((Rectangle)sender).CaptureMouse();
-                    _isMovingUserControl = true;
-                    lastAngle = 0;
-                    e.Handled = true; // Evitar que el evento se propague
-                    return;
-                }
-
-                var userControl = sender as UserControl;
-                if (userControl != null)
-                {
-                    // Si hacemos clic en un control diferente al seleccionado, remover los rectángulos anteriores
-                    if (_currentDraggingControl != userControl)
-                    {
-                        RemoveResizeRectangles();
-                    }
-
-                    userControl.CaptureMouse();
-                    _currentDraggingControl = userControl;
-                    _isMovingUserControl = true;
-
-                    // Actualizar la selección en el ViewModel
-                    if (userControl.DataContext is osBase datos)
-                    {
-                        var viewModel = DataContext as MainViewModel;
-                        if (viewModel != null)
-                        {
-                            viewModel.SelectedItemOsList = datos;
-                            AddResizeRectangles(userControl);
-                        }
-                    }
-
-                    // Resto del código para manejar rotación, tamaño y movimiento
-                    if (Keyboard.IsKeyDown(Key.LeftShift))
-                    {
-                        // Código de rotación existente
-                        // ROTACION
-                        // Inicializar la rotación
-                        _isRotatingUserControl = true;
-                        RotateTransform rotateTransform = userControl.RenderTransform as RotateTransform;
-                        if (rotateTransform == null)
-                        {
-                            rotateTransform = new RotateTransform();
-                            userControl.RenderTransform = rotateTransform;
-                        }
-
-                        // Establecer el punto inicial de referencia para el cálculo de rotación
-                        _startPointUserControl = new Point(rotateTransform.CenterX, rotateTransform.CenterY);
-
-                        // Ajusta el punto inicial al espacio del Canvas
-                        _startPointUserControl = userControl.TranslatePoint(_startPointUserControl, ImagenEnTrabajoCanvas);
-
-                        // Crear y configurar el TextBlock si no existe
-                        if (_angleDisplayTextBlock == null)
-                        {
-                            _angleDisplayTextBlock = new TextBlock
-                            {
-                                Foreground = Brushes.Black,
-                                Background = Brushes.White,
-                                Opacity = 0.8,
-                                Padding = new Thickness(5)
-                            };
-                            ImagenEnTrabajoCanvas.Children.Add(_angleDisplayTextBlock);
-                        }
-
-                        PositionAngleDisplay(userControl);
-                        _angleDisplayTextBlock.Visibility = Visibility.Visible;
-                        Canvas.SetZIndex(_angleDisplayTextBlock, 15);
-                    }
-                    else if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
-                    {
-                        // Código de cambio de tamaño existente
-                        // Inicializar el cambio de tamaño
-                        _isResizingUserControl = true;
-                        _startPointUserControl = e.GetPosition(ImagenEnTrabajoCanvas);
-                    }
-                    else
-                    {
-                        // Código de movimiento existente
-                        // Inicializar el movimiento/panning
-                        _isDraggingUserControl = true;
-                        _startPointUserControl = e.GetPosition(ImagenEnTrabajoCanvas);
-                    }
-                }
-            }
-        }
-
-        dataDebug dataDebug = new dataDebug();
-
-        public void DebugWindow()
-        {
-            // Crear una instancia de wDebug
-            var debugWindow = new wDebug
-            {
-                Data = dataDebug // Asignar la instancia de Test a la propiedad Data
-            };
-
-            // Mostrar la ventana de depuración
-            debugWindow.Show();
-        }
-
-        private Point transformedBoundingBoxCenter = new Point();
-        private float lastAngle;
-
-        private void AddResizeRectangles(UserControl userControl)
-        {
-            double rectSize = 10;
-            double rectHighlightSize = 1;
-            RemoveResizeRectangles();
-
-            if (userControl is IDataContainer dataContainer && dataContainer.Datos is osBase mvBase && mvBase.Show_On_This_Page)
-            {
-
-                // Obtener el BoundingBox aproximado del UserControl
-                Rect boundingBox = VisualTreeHelper.GetDescendantBounds(userControl);
-
-                // Transformar el BoundingBox a las coordenadas del Canvas
-                GeneralTransform transform = userControl.TransformToAncestor(ImagenEnTrabajoCanvas);
-                Rect transformedBoundingBox = transform.TransformBounds(boundingBox);
-
-                FuncionesBase.MutableRect rectBox = new FuncionesBase.MutableRect(transformedBoundingBox);
-                rectBox.Left -= (float)rectHighlightSize;
-                rectBox.Right += (float)rectHighlightSize;
-                rectBox.Top -= (float)rectHighlightSize;
-                rectBox.Bottom += (float)rectHighlightSize;
-
-                transformedBoundingBoxCenter.X = transformedBoundingBox.Left + transformedBoundingBox.Width / 2;
-                transformedBoundingBoxCenter.Y = transformedBoundingBox.Top + transformedBoundingBox.Height / 2;
-
-                // Agregar primero el rectángulo de selección
-                Rectangle selectionRect = new Rectangle
-                {
-                    Width = rectBox.Width + (rectHighlightSize * 2), // Hacer el rectángulo un poco más grande
-                    Height = rectBox.Height + (rectHighlightSize * 2),
-                    Fill = Brushes.Transparent,
-                    Stroke = new SolidColorBrush(Color.FromArgb(180, 0, 120, 215)), // Azul semi-transparente
-                    StrokeThickness = 1.5,
-                    Tag = "Selection",
-                    IsHitTestVisible = false,
-                    StrokeDashArray = new DoubleCollection(new double[] { 3, 3 }) // Borde punteado más visible
-                };
-
-                Canvas.SetLeft(selectionRect, rectBox.Left - rectHighlightSize);
-                Canvas.SetTop(selectionRect, rectBox.Top - rectHighlightSize);
-                Canvas.SetZIndex(selectionRect, ((int)ZIndexEnum.RectangulosPropiead - 1)); // Un nivel por debajo de los controles de redimensión
-
-                resizeRectangles.Add(selectionRect);
-                ImagenEnTrabajoCanvas.Children.Add(selectionRect);
-
-                // Cargar el cursor personalizado para rotación
-                Cursor rotationCursorRx = new Cursor(Application.GetResourceStream(new Uri("pack://application:,,,/CtrEditor;component/Icons/rotationRx.cur")).Stream);
-                Cursor rotationCursorSx = new Cursor(Application.GetResourceStream(new Uri("pack://application:,,,/CtrEditor;component/Icons/rotationSx.cur")).Stream);
-
-                // Calcular las posiciones de los rectángulos de redimensionamiento
-                var positions = new List<Tuple<Point, string>>()
-    {
-        new Tuple<Point, string>(new Point(rectBox.Left, rectBox.Top), "TopLeft"),
-        new Tuple<Point, string>(new Point(rectBox.Right, rectBox.Top), "TopRight"),
-        new Tuple<Point, string>(new Point(rectBox.Left, rectBox.Bottom), "BottomLeft"),
-        new Tuple<Point, string>(new Point(rectBox.Right, rectBox.Bottom), "BottomRight"),
-        new Tuple<Point, string>(new Point(rectBox.Left + rectBox.Width / 2, rectBox.Top), "TopCenter"),
-        new Tuple<Point, string>(new Point(rectBox.Left + rectBox.Width / 2, rectBox.Bottom), "BottomCenter"),
-        new Tuple<Point, string>(new Point(rectBox.Left, rectBox.Top + rectBox.Height / 2), "CenterLeft"),
-        new Tuple<Point, string>(new Point(rectBox.Right, rectBox.Top + rectBox.Height / 2), "CenterRight")
-    };
-
-                // Add validation before setting Canvas position
-                void SetCanvasPosition(UIElement element, double left, double top)
-                {
-                    if (!double.IsInfinity(left) && !double.IsNaN(left))
-                        Canvas.SetLeft(element, left);
-                    if (!double.IsInfinity(top) && !double.IsNaN(top))
-                        Canvas.SetTop(element, top);
-                }
-
-                foreach (var position in positions)
-                {
-                    Rectangle rect = new Rectangle
-                    {
-                        Width = rectSize,
-                        Height = rectSize,
-                        Fill = Brushes.Transparent,
-                        Stroke = Brushes.Black,
-                        StrokeThickness = 1,
-                        Tag = position.Item2 // Asignar la etiqueta
-                    };
-
-                    // Establecer el cursor adecuado
-                    switch (position.Item2)
-                    {
-                        case "TopLeft":
-                            rect.Cursor = Cursors.Arrow;
-                            rect.Stroke = Brushes.Gray;
-                            break;
-                        case "TopRight":
-                            rect.Cursor = rotationCursorRx; // Cursor de rotación
-                            rect.Stroke = Brushes.Red;
-                            break;
-                        case "BottomLeft":
-                            rect.Cursor = rotationCursorSx; // Cursor de rotación
-                            rect.Stroke = Brushes.DarkRed;
-                            break;
-                        case "BottomRight":
-                            rect.Cursor = Cursors.SizeNWSE; // Cursor de dimensionar altura y anchura
-                            rect.Stroke = Brushes.Blue;
-                            break;
-                        case "TopCenter":
-                            rect.Cursor = Cursors.Arrow;
-                            rect.Stroke = Brushes.Gray;
-                            break;
-                        case "BottomCenter":
-                            rect.Cursor = Cursors.SizeNS; // Cursor de dimensionar altura
-                            rect.Stroke = Brushes.Blue;
-                            break;
-                        case "CenterLeft":
-                            rect.Cursor = rotationCursorRx; // Cursor de rotación
-                            rect.Stroke = Brushes.Red;
-                            break;
-                        case "CenterRight":
-                            rect.Cursor = Cursors.SizeWE; // Cursor de dimensionar anchura
-                            rect.Stroke = Brushes.Blue;
-                            break;
-                    }
-
-                    // Replace direct Canvas.Set calls with the validation method
-                    SetCanvasPosition(rect, position.Item1.X - rectSize / 2, position.Item1.Y - rectSize / 2);
-
-                    rect.MouseLeftButtonDown += UserControl_MouseLeftButtonDown;
-                    rect.MouseMove += UserControl_MouseMove;
-                    rect.MouseLeftButtonUp += UserControl_MouseLeftButtonUp;
-                    rect.MouseLeave += ResizeRectangle_MouseLeave;
-
-                    resizeRectangles.Add(rect);
-                    ImagenEnTrabajoCanvas.Children.Add(rect);
-                    Canvas.SetZIndex(rect, ((int)ZIndexEnum.RectangulosPropiead));
-                }
-            }
-        }
-
-        private void AddHighlightRectangles(UserControl userControl)
-        {
-            double rectSize = 1;
-            RemoveHighlightRectangles(); // Eliminar highlight anterior
-
-            if (userControl is IDataContainer dataContainer && dataContainer.Datos is osBase mvBase && mvBase.Show_On_This_Page)
-            {
-                // Obtener el BoundingBox aproximado del UserControl
-                Rect boundingBox = VisualTreeHelper.GetDescendantBounds(userControl);
-
-                // Transformar el BoundingBox a las coordenadas del Canvas
-                GeneralTransform transform = userControl.TransformToAncestor(ImagenEnTrabajoCanvas);
-                Rect transformedBoundingBox = transform.TransformBounds(boundingBox);
-
-                FuncionesBase.MutableRect rectBox = new FuncionesBase.MutableRect(transformedBoundingBox);
-                rectBox.Left -= (float)rectSize;
-                rectBox.Right += (float)rectSize;
-                rectBox.Top -= (float)rectSize;
-                rectBox.Bottom += (float)rectSize;
-
-                // Crear un rectángulo de resaltado alrededor del objeto con estilo más sutil
-                Rectangle highlightRect = new Rectangle
-                {
-                    Width = rectBox.Width,
-                    Height = rectBox.Height,
-                    Fill = Brushes.Transparent,
-                    Stroke = new SolidColorBrush(Color.FromArgb(128, 0, 0, 0)), // Borde semitransparente
-                    StrokeThickness = 1,
-                    Tag = "Highlight",
-                    IsHitTestVisible = false,
-                    StrokeDashArray = new DoubleCollection(new double[] { 2, 2 }) // Borde punteado
-                };
-
-                Canvas.SetLeft(highlightRect, rectBox.Left);
-                Canvas.SetTop(highlightRect, rectBox.Top);
-
-                highlightRectangles.Add(highlightRect); // Usar la nueva lista
-                ImagenEnTrabajoCanvas.Children.Add(highlightRect);
-                Canvas.SetZIndex(highlightRect, ((int)ZIndexEnum.RectangulosPropiead));
-
-                // Reiniciar el timer
-                if (timerRemoveHighlight == null)
-                    timerRemoveHighlight = new System.Threading.Timer(TimerCallbackRemoveHighlight, null, Timeout.Infinite, Timeout.Infinite);
-
-                timerRemoveHighlight.Change(2000, Timeout.Infinite);
-            }
-        }
-
-        private void ResizeRectangle_MouseLeave(object sender, MouseEventArgs e)
-        {
-            var rect = sender as Rectangle;
-            rect.Fill = Brushes.Transparent; // Volver al color original
-        }
-
-        private void MakeResizeRectanglesTransparent()
-        {
-            if (resizeRectangles == null || resizeRectangles.Count == 0)
-                return;
-
-            foreach (var rect in resizeRectangles)
-            {
-                rect.Opacity = 0; // Hacer transparente
-            }
-        }
-
-        private void MakeResizeRectanglesNormal()
-        {
-            if (resizeRectangles == null || resizeRectangles.Count == 0)
-                return;
-
-            foreach (var rect in resizeRectangles)
-            {
-                rect.Opacity = 1;
-            }
-        }
-
-
-        private void RemoveResizeRectangles()
-        {
-            if (resizeRectangles == null || resizeRectangles.Count == 0)
-                return;
-
-            foreach (var rect in resizeRectangles)
-            {
-                ImagenEnTrabajoCanvas.Children.Remove(rect);
-            }
-            resizeRectangles.Clear();
-        }
-
-        private void RemoveHighlightRectangles()
-        {
-            if (highlightRectangles == null || highlightRectangles.Count == 0)
-                return;
-
-            foreach (var rect in highlightRectangles)
-            {
-                ImagenEnTrabajoCanvas.Children.Remove(rect);
-            }
-            highlightRectangles.Clear();
-        }
-
-        private Point lastMousePosition;
-
-        private void MarkUnsavedChanges()
-        {
-            if (DataContext is MainViewModel viewModel)
-            {
-                if (_isMovingUserControl || _isRotatingUserControl || _isResizingUserControl || _isDraggingUserControl)
-                {
-                    viewModel.HasUnsavedChanges = true;
-                }
-            }
-        }
-
-        private void UserControl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
-        {
-            MarkUnsavedChanges();
-            if (_isResizingUserControl && resizeRectangles != null && resizeRectangles.Contains(sender))
-            {
-                _isResizingUserControl = false;
-                _isMovingUserControl = false;
-                ((Rectangle)sender).ReleaseMouseCapture();
-                AddResizeRectangles(_currentDraggingControl);                
-            }
-            else if (_isMovingUserControl)
-            {
-                var userControl = sender as UserControl;
-
-                userControl.ReleaseMouseCapture();
-                // _currentDraggingControl = null;
-                AddResizeRectangles(_currentDraggingControl);
-                _isResizingUserControl = _isRotatingUserControl = _isDraggingUserControl = false;
-                _isMovingUserControl = false;                
-                
-                // Ocultar el TextBlock de ángulo
-                if (_angleDisplayTextBlock != null)
-                {
-                    _angleDisplayTextBlock.Visibility = Visibility.Collapsed;
-                }
-            }
-        }
-
-        Rectangle _currentDraggingRectangle;
-
-        private void UserControl_MouseMove(object sender, MouseEventArgs e)
-        {
-            if (!_isMovingUserControl && resizeRectangles != null && resizeRectangles.Contains(sender))
-            {
-                var rect = sender as Rectangle;
-                rect.Fill = Brushes.Black; // Pintar de negro el rectángulo bajo el ratón
-                _currentDraggingRectangle = rect; // Asignar el rectángulo actual que se está arrastrando
-                _startPointUserControl = new Point(Canvas.GetLeft(rect), Canvas.GetTop(rect));
-                MakeResizeRectanglesNormal();
-            }
-            if (_isMovingUserControl && _currentDraggingControl != null)
-            {
-                var currentPosition = e.GetPosition(ImagenEnTrabajoCanvas);
-                MakeResizeRectanglesTransparent(); // Hacer transparente durante cualquier manipulación
-
-                if (_isDraggingUserControl)
-                {
-                    // Código para mover el control
-                    var dx = currentPosition.X - _startPointUserControl.X;
-                    var dy = currentPosition.Y - _startPointUserControl.Y;
-                    var newX = Canvas.GetLeft(_currentDraggingControl) + dx;
-                    var newY = Canvas.GetTop(_currentDraggingControl) + dy;
-
-                    if (_currentDraggingControl is IDataContainer dataContainer && dataContainer.Datos is osBase mvBase)
-                        mvBase.Move((float)newX, (float)newY);
-
-                    _startPointUserControl = currentPosition;  // Actualiza el punto inicial para el siguiente movimiento
-                }
-                else if (_isRotatingUserControl)
-                {
-                    // Código para rotar el control
-                    RotateControl(_currentDraggingControl, currentPosition);
-                }
-                else if (_isResizingUserControl)
-                {
-                    // Código para cambiar el tamaño del control
-                    ResizeControl(_currentDraggingControl, currentPosition);
-                }
-            }
-        }
-
-        private void RotateControl(UserControl control, Point currentPosition)
-        {
-            double deltaX = currentPosition.X - _startPointUserControl.X;
-            double deltaY = currentPosition.Y - _startPointUserControl.Y;
-            double angle = Math.Atan2(deltaY, deltaX) * (180 / Math.PI);
-
-            //RotateTransform rotateTransform = control.RenderTransform as RotateTransform;
-            //rotateTransform.Angle = angle; // - _initialAngleUserControl;  // Asegúrate de ajustar esta parte según cómo calcules el ángulo inicial
-
-            if (control is IDataContainer dataContainer && dataContainer.Datos is osBase mvBase)
-                mvBase.Rotate((float)angle);
-
-            // Actualizar el ángulo mostrado
-            _angleDisplayTextBlock.Text = $"Ángulo: {angle:F2}°";
-            PositionAngleDisplay(control);
-        }
-
-        private void PositionAngleDisplay(UserControl control)
-        {
-            // Posicionar el TextBlock sobre el control
-            Canvas.SetLeft(_angleDisplayTextBlock, Canvas.GetLeft(control));
-            Canvas.SetTop(_angleDisplayTextBlock, Canvas.GetTop(control));
-        }
-
-        private void ResizeControl(UserControl control, Point currentPosition)
-        {
-            bool ActivateRotation = false, ActivateSizeWidth = false, ActivateSizeHeight = false, ActivateMove = false;
-
-            if (control is IDataContainer dataContainer && dataContainer.Datos is osBase mvBase)
-            {
-                MakeResizeRectanglesTransparent();
-
-                // Obtener el rectángulo desde el que se inició la redimensión
-                var resizeRect = _currentDraggingRectangle as Rectangle;
-                if (resizeRect == null) return;
-
-                string resizeDirection = resizeRect.Tag as string;
-                if (resizeDirection == null) return;
-
-                switch (resizeDirection)
-                {
-                    case "TopLeft":
-                        break;
-                    case "TopRight":
-                        ActivateRotation = true;
-                        break;
-                    case "BottomLeft":
-                        ActivateRotation = true;
-                        break;
-                    case "BottomRight":
-                        ActivateSizeHeight = true;
-                        ActivateSizeWidth = true;
-                        break;
-                    case "TopCenter":
-                        break;
-                    case "BottomCenter":
-                        ActivateSizeHeight = true;
-                        break;
-                    case "CenterLeft":
-                        ActivateRotation = true;
-                        break;
-                    case "CenterRight":
-                        ActivateSizeWidth = true;
-                        break;
-                }
-                if (ActivateMove)
-                {
-                    // Código para mover el control
-                    var dx = currentPosition.X - _startPointUserControl.X;
-                    var dy = currentPosition.Y - _startPointUserControl.Y;
-
-                    var newX = Canvas.GetLeft(_currentDraggingControl) + dx;
-                    var newY = Canvas.GetTop(_currentDraggingControl) + dy;
-
-                    mvBase.Move(newX, newY);
-                    //                    dataContainer.Move((float)newX, (float)newY);
-                }
-                if (ActivateRotation)
-                {
-                    double deltaX = currentPosition.X - transformedBoundingBoxCenter.X;
-                    double deltaY = currentPosition.Y - transformedBoundingBoxCenter.Y;
-                    double angle = Math.Atan2(deltaY, deltaX) * (180 / Math.PI);
-                    if ((lastAngle == 0 && angle != 0) || Math.Abs(angle - lastAngle) > 45) lastAngle = (float)angle;
-                    else
-                    {
-                        mvBase.Rotate(angle - lastAngle);
-                        lastAngle = (float)angle;
-
-                        dataDebug.TransformedBoundingBoxCenter = transformedBoundingBoxCenter;
-                        dataDebug.LastAngle = lastAngle;
-                        dataDebug.CurrentPosition = currentPosition;
-                        dataDebug.Angle = (float)angle;
-                    }
-                }
-                if (ActivateSizeWidth || ActivateSizeHeight)
-                {
-                    // Calcular la diferencia en la posición X desde el punto de inicio
-                    double widthChange = currentPosition.X - _startPointUserControl.X;
-
-                    // Calcular la diferencia en la posición Y desde el punto de inicio
-                    double heightChange = currentPosition.Y - _startPointUserControl.Y;
-
-                    if (!ActivateSizeHeight) heightChange = 0;
-                    if (!ActivateSizeWidth) widthChange = 0;
-
-                    mvBase.Resize(widthChange, heightChange);
-                    // dataContainer.Resize((float)widthChange, (float)heightChange);
-                }
-                _startPointUserControl = currentPosition;  // Actualiza el punto inicial para el siguiente movimiento
-            }
-        }
-
-
-        private void UserControl_MouseEnter(object sender, MouseEventArgs e)
-        {
-            if (sender is UserControl userControl && userControl is IDataContainer dataContainer)
-            {
-                dataContainer.Highlight(true);
-                
-                if (DataContext is MainViewModel viewModel)
-                {
-                    var selectedObject = viewModel.SelectedItemOsList;
-                    
-                    // Si el control actual no es el seleccionado
-                    if (selectedObject?.VisualRepresentation != userControl)
-                    {
-                        // Siempre mostrar el highlight temporal
-                        AddHighlightRectangles(userControl);
-                    }
-                }
-            }
-        }
-
-        private async void TimerCallbackRemoveHighlight(object state)
-        {
-            if (Application.Current != null)
-            {
-                Application.Current.Dispatcher.Invoke(() =>
-                {
-                    RemoveHighlightRectangles();
-                });
-            }
-        }
-
-        private void UserControl_MouseLeave(object sender, MouseEventArgs e)
-        {
-            if (sender is UserControl userControl)
-                if (userControl is IDataContainer dataContainer)
-                    dataContainer.Highlight(false);
-        }
-
-        private void ViewModel_ImageSelected(object sender, string imagePath)
-        {
-            LoadImageToCanvas(imagePath);
-        }
-
         private void LoadImageToCanvas(string imagePath)
         {
             BitmapImage bitmap = new BitmapImage(new Uri(imagePath, UriKind.Absolute));
@@ -724,7 +112,6 @@ namespace CtrEditor
             imagenDeFondo.Source = bitmap;
             RenderOptions.SetBitmapScalingMode(imagenDeFondo, BitmapScalingMode.HighQuality);
 
-
             // Elimina solo los ROIs, no la imagen de fondo
             for (int i = ImagenEnTrabajoCanvas.Children.Count - 1; i >= 0; i--)
             {
@@ -734,14 +121,11 @@ namespace CtrEditor
                 }
             }
 
-            // Ajusta el tamaño del Canvas a la imagen, si es necesario
             ImagenEnTrabajoCanvas.Width = bitmap.Width;
             ImagenEnTrabajoCanvas.Height = bitmap.Height;
 
-            // Posiciona la imagen correctamente dentro del Canvas
             Canvas.SetLeft(imagenDeFondo, 0);
             Canvas.SetTop(imagenDeFondo, 0);
-
         }
 
         private void Canvas_MouseUp_Panning(object sender, MouseButtonEventArgs e)
@@ -749,81 +133,79 @@ namespace CtrEditor
             if (_isDraggingCanvas)
             {
                 _isDraggingCanvas = false;
-                ImagenEnTrabajoScrollViewer.ReleaseMouseCapture(); // Finaliza la captura del ratón
+                ImagenEnTrabajoCanvas.ReleaseMouseCapture();
 
-                // Restaurar la visibilidad de los rectángulos de selección si hay un objeto seleccionado
                 if (DataContext is MainViewModel viewModel && viewModel.SelectedItemOsList != null)
                 {
-                    MakeResizeRectanglesNormal();
+                    _objectManager.MakeResizeRectanglesNormal();
                 }
+                e.Handled = true;
             }
         }
 
         private void Canvas_MouseDown_Panning(object sender, MouseButtonEventArgs e)
         {
-            if (e.LeftButton == MouseButtonState.Pressed && !_isDrawingCanvas && !_isMovingUserControl)
+            if (e.LeftButton == MouseButtonState.Pressed && !_isDrawingCanvas)
             {
-                _isDraggingCanvas = true;
-                _lastMousePosition = e.GetPosition(ImagenEnTrabajoScrollViewer);
-                
-                // Asegurarnos de que el clic fue directamente en el canvas y no en un elemento hijo
+                // Solo activar el panning si el clic fue directamente en el canvas
                 if (e.Source == ImagenEnTrabajoCanvas)
                 {
+                    _isDraggingCanvas = true;
+                    _lastMousePosition = e.GetPosition(ImagenEnTrabajoScrollViewer);
+                    ImagenEnTrabajoCanvas.CaptureMouse();
+
                     if (DataContext is MainViewModel viewModel)
                     {
-                        viewModel.SelectedItemOsList = null; // Deseleccionar el objeto actual
-                        RemoveResizeRectangles(); // Eliminar los rectángulos después de deseleccionar
+                        viewModel.SelectedItemOsList = null;
+                        _objectManager.RemoveResizeRectangles();
                     }
+                    e.Handled = true;
                 }
             }
         }
 
         private void Canvas_MouseMove_Panning(object sender, MouseEventArgs e)
         {
-            if (_isDraggingCanvas)
+            if (_isDraggingCanvas && e.LeftButton == MouseButtonState.Pressed)
             {
                 var currentPosition = e.GetPosition(ImagenEnTrabajoScrollViewer);
                 var dx = currentPosition.X - _lastMousePosition.X;
                 var dy = currentPosition.Y - _lastMousePosition.Y;
 
-                // Ocultar rectángulos y eliminar highlights durante el panning
-                MakeResizeRectanglesTransparent();
-                RemoveHighlightRectangles();
+                _objectManager.MakeResizeRectanglesTransparent();
+                _objectManager.RemoveHighlightRectangles();
 
-                // Obtener la transformación actual del Canvas
-                var transform = (TranslateTransform)((TransformGroup)ImagenEnTrabajoCanvas.RenderTransform).Children.First(t => t is TranslateTransform);
+                var transform = (TranslateTransform)((TransformGroup)ImagenEnTrabajoCanvas.RenderTransform)
+                    .Children.First(t => t is TranslateTransform);
                 transform.X += dx;
                 transform.Y += dy;
 
                 _lastMousePosition = currentPosition;
+                e.Handled = true;
             }
         }
 
         private void ImagenEnTrabajoCanvas_MouseWheel(object sender, MouseWheelEventArgs e)
         {
-            // Ocultar rectángulos y eliminar highlights
-            MakeResizeRectanglesTransparent();
-            RemoveHighlightRectangles();
+            _objectManager.MakeResizeRectanglesTransparent();
+            _objectManager.RemoveHighlightRectangles();
 
-            _initialZoomFactor = ((ScaleTransform)((TransformGroup)ImagenEnTrabajoCanvas.RenderTransform).Children.First(t => t is ScaleTransform)).ScaleX;
+            _initialZoomFactor = ((ScaleTransform)((TransformGroup)ImagenEnTrabajoCanvas.RenderTransform)
+                .Children.First(t => t is ScaleTransform)).ScaleX;
 
-            // Calcula el factor de escala mínimo para que toda la imagen sea visible
             double minZoomFactor = Math.Min(
                 ImagenEnTrabajoScrollViewer.ViewportWidth / ImagenEnTrabajoCanvas.ActualWidth,
                 ImagenEnTrabajoScrollViewer.ViewportHeight / ImagenEnTrabajoCanvas.ActualHeight);
 
-            // Ajusta el targetZoomFactor pero no permite que baje del mínimo
-            _targetZoomFactor = e.Delta > 0 ? _initialZoomFactor * 1.4 : Math.Max(_initialZoomFactor * 0.75, minZoomFactor);
+            _targetZoomFactor = e.Delta > 0 ?
+                _initialZoomFactor * 1.4 :
+                Math.Max(_initialZoomFactor * 0.75, minZoomFactor);
 
             _zoomCursorPosition = e.GetPosition(ImagenEnTrabajoCanvas);
 
-            // Desactivar el escalado de alta calidad para mejorar el rendimiento
             RenderOptions.SetBitmapScalingMode(ImagenEnTrabajoCanvas, BitmapScalingMode.LowQuality);
 
-            if (!_zoomTimer.IsEnabled)
-                _ZoomDuration = ZoomDuration;
-            else
-                _ZoomDuration = ZoomDuration / 3;
+            _ZoomDuration = !_zoomTimer.IsEnabled ? ZoomDuration : ZoomDuration / 3;
 
             _stopwatch.Restart();
             _zoomTimer.Start();
@@ -831,7 +213,6 @@ namespace CtrEditor
             e.Handled = true;
         }
 
-
         private void ZoomTimer_Tick(object sender, EventArgs e)
         {
             double elapsedMilliseconds = _stopwatch.Elapsed.TotalMilliseconds;
@@ -841,13 +222,11 @@ namespace CtrEditor
                 _zoomTimer.Stop();
                 _stopwatch.Stop();
 
-                // Volver a activar el escalado de alta calidad después de completar el zoom
                 RenderOptions.SetBitmapScalingMode(ImagenEnTrabajoCanvas, BitmapScalingMode.HighQuality);
 
-                // Restaurar la visibilidad de los rectángulos de selección si hay un objeto seleccionado
                 if (DataContext is MainViewModel viewModel && viewModel.SelectedItemOsList != null)
                 {
-                    MakeResizeRectanglesNormal();
+                    _objectManager.MakeResizeRectanglesNormal();
                 }
 
                 return;
@@ -857,12 +236,10 @@ namespace CtrEditor
             var st = (ScaleTransform)tg.Children.First(t => t is ScaleTransform);
             var tt = (TranslateTransform)tg.Children.First(t => t is TranslateTransform);
 
-            // Interpolación cuadrática para la desaceleración
             double t = elapsedMilliseconds / _ZoomDuration;
-            double easeOutT = t * (2 - t); // Función de interpolación cuadrática (ease-out)
+            double easeOutT = t * (2 - t);
             double zoomFactor = _initialZoomFactor + (_targetZoomFactor - _initialZoomFactor) * easeOutT;
 
-            // Asegúrate de no ir por debajo del zoom mínimo
             zoomFactor = Math.Max(zoomFactor, MinZoomScale);
 
             Point cursorPosition = _zoomCursorPosition;
@@ -877,64 +254,29 @@ namespace CtrEditor
             tt.Y = relativeY - cursorPosition.Y * st.ScaleY;
         }
 
-        public (float X, float Y) ObtenerCentroCanvasPixels()
-        {
-            // Obtener las transformaciones actuales del Canvas
-            var tg = (TransformGroup)ImagenEnTrabajoCanvas.RenderTransform;
-            var st = (ScaleTransform)tg.Children.First(t => t is ScaleTransform);
-            var tt = (TranslateTransform)tg.Children.First(t => t is TranslateTransform);
-
-            // Obtener el tamaño del ScrollViewer visible
-            double visibleWidth = ImagenEnTrabajoScrollViewer.ViewportWidth;
-            double visibleHeight = ImagenEnTrabajoScrollViewer.ViewportHeight;
-
-            // Obtener el desplazamiento del ScrollViewer
-            double offsetX = ImagenEnTrabajoScrollViewer.HorizontalOffset;
-            double offsetY = ImagenEnTrabajoScrollViewer.VerticalOffset;
-
-            // Calcular las coordenadas del centro visible del ScrollViewer
-            double centerX = offsetX + (visibleWidth / 2);
-            double centerY = offsetY + (visibleHeight / 2);
-
-            // Ajustar las coordenadas del centro para tener en cuenta las transformaciones del Canvas
-            double canvasCenterX = (centerX - tt.X) / st.ScaleX;
-            double canvasCenterY = (centerY - tt.Y) / st.ScaleY;
-
-            return ((float)canvasCenterX, (float)canvasCenterY);
-        }
-
-
-        private void CargarPropiedadesosDatos(osBase selectedObject)
-        {
-            if (DataContext is MainViewModel viewModel)
-                viewModel.CargarPropiedadesosDatos(selectedObject, PanelEdicion, Resources);
-        }
-
-        private void MainWindow_Closed(object sender, EventArgs e)
-        {
-            if (DataContext is MainViewModel viewModel)
-            {
-                viewModel.ImageSelected -= ViewModel_ImageSelected;
-            }
-        }
-
         private void ListaOs_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
         {
             UserControlFactory.LimpiarPropiedadesosDatos(PanelEdicion);
 
             if (e.AddedItems.Count > 0 && e.AddedItems[0] is osBase selectedObject)
             {
-                if (selectedObject.VisualRepresentation != null)
-                {
-                    _currentDraggingControl = selectedObject.VisualRepresentation;
-                    RemoveResizeRectangles();
-                    AddResizeRectangles(_currentDraggingControl);
-                }
+                // Siempre trabajar con selección única para las propiedades
                 CargarPropiedadesosDatos(selectedObject);
+
+                // No modificar la selección múltiple aquí, solo actualizar los rectángulos de manipulación
+                // si el objeto seleccionado no está en la selección actual
+                if (!_objectManager.SelectedObjects.Contains(selectedObject))
+                {
+                    if (!((MainViewModel)DataContext).IsMultiSelectionActive)
+                    {
+                        _objectManager.ClearSelection();
+                        _objectManager.SelectObject(selectedObject);
+                    }
+                }
             }
             else
             {
-                RemoveResizeRectangles();
+                _objectManager.RemoveResizeRectangles();
             }
         }
 
@@ -949,9 +291,16 @@ namespace CtrEditor
                 }
                 else if (e.Key == Key.Escape)
                 {
-                    // Deseleccionar el objeto actual y eliminar los rectángulos de selección
+                    // Limpiar la selección en el ListBox
                     viewModel.SelectedItemOsList = null;
-                    RemoveResizeRectangles();
+                    
+                    // Limpiar la selección múltiple
+                    _objectManager.ClearSelection();
+                    _objectManager.RemoveResizeRectangles();
+                    
+                    // Desactivar el modo de selección múltiple
+                    viewModel.IsMultiSelectionActive = false;
+                    
                     e.Handled = true;
                 }
             }
@@ -959,28 +308,115 @@ namespace CtrEditor
 
         private void Canvas_MouseEnter(object sender, MouseEventArgs e)
         {
-            // Solo remover los rectángulos si el mouse entra directamente al Canvas
             if (e.OriginalSource == ImagenEnTrabajoCanvas)
             {
-               // RemoveResizeRectangles();
+                //_objectManager.RemoveResizeRectangles();
             }
         }
+
+        private void MainWindow_Closed(object sender, EventArgs e)
+        {
+            if (DataContext is MainViewModel viewModel)
+            {
+                viewModel.ImageSelected -= ViewModel_ImageSelected;
+            }
+        }
+
+        private void CargarPropiedadesosDatos(osBase selectedObject)
+        {
+            if (DataContext is MainViewModel viewModel)
+                viewModel.CargarPropiedadesosDatos(selectedObject, PanelEdicion, Resources);
+        }
+
+        public (float X, float Y) ObtenerCentroCanvasMeters()
+        {
+            var c = ObtenerCentroCanvasPixels();
+            return (PixelToMeter.Instance.calc.PixelsToMeters(c.X),
+                   PixelToMeter.Instance.calc.PixelsToMeters(c.Y));
+        }
+
+        public (float X, float Y) ObtenerCentroCanvasPixels()
+        {
+            var tg = (TransformGroup)ImagenEnTrabajoCanvas.RenderTransform;
+            var st = (ScaleTransform)tg.Children.First(t => t is ScaleTransform);
+            var tt = (TranslateTransform)tg.Children.First(t => t is TranslateTransform);
+
+            double visibleWidth = ImagenEnTrabajoScrollViewer.ViewportWidth;
+            double visibleHeight = ImagenEnTrabajoScrollViewer.ViewportHeight;
+
+            double offsetX = ImagenEnTrabajoScrollViewer.HorizontalOffset;
+            double offsetY = ImagenEnTrabajoScrollViewer.VerticalOffset;
+
+            double centerX = offsetX + (visibleWidth / 2);
+            double centerY = offsetY + (visibleHeight / 2);
+
+            double canvasCenterX = (centerX - tt.X) / st.ScaleX;
+            double canvasCenterY = (centerY - tt.Y) / st.ScaleY;
+
+            return ((float)canvasCenterX, (float)canvasCenterY);
+        }
+
+        private void ViewModel_ImageSelected(object sender, string imagePath)
+        {
+            LoadImageToCanvas(imagePath);
+        }
+
+        public void DebugWindow()
+        {
+            var debugWindow = new wDebug
+            {
+                Data = dataDebug
+            };
+            debugWindow.Show();
+        }
+
+        private void Canvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
+        {
+            // Aceptar el evento si viene del canvas o de la imagen de fondo
+            if ((e.Source == ImagenEnTrabajoCanvas || e.Source == imagenDeFondo) && DataContext is MainViewModel viewModel)
+            {
+                e.Handled = true; // Importante: marcar el evento como manejado
+                ShowContextMenu(e.GetPosition(ImagenEnTrabajoCanvas));
+            }
+        }
+
+        private void ShowContextMenu(Point position)
+        {
+            var contextMenu = new ContextMenu();
+            var multiSelectMenuItem = new MenuItem 
+            { 
+                Header = "Modo Multi-Selección",
+                IsCheckable = true,
+                StaysOpenOnClick = false  // Cerrar el menú al hacer clic
+            };
+
+            if (DataContext is MainViewModel viewModel)
+            {
+                multiSelectMenuItem.IsChecked = viewModel.IsMultiSelectionActive;
+                multiSelectMenuItem.Click += (s, e) => 
+                {
+                    viewModel.IsMultiSelectionActive = multiSelectMenuItem.IsChecked;
+                };
+            }
+
+            contextMenu.Items.Add(multiSelectMenuItem);
+            contextMenu.PlacementTarget = ImagenEnTrabajoCanvas;
+            contextMenu.Placement = System.Windows.Controls.Primitives.PlacementMode.MousePoint;
+            contextMenu.IsOpen = true;
+        }
     }
 
     public class FloatValidationRule : ValidationRule
     {
         public override ValidationResult Validate(object value, CultureInfo cultureInfo)
         {
-            // Comprobamos si el valor es nulo o está vacío
             if (string.IsNullOrEmpty(value?.ToString()))
                 return new ValidationResult(false, "El campo no puede estar vacío.");
 
-            // Intentamos convertir el valor a un tipo float
             if (float.TryParse(value.ToString(), NumberStyles.Float, cultureInfo, out float result))
-                return ValidationResult.ValidResult; // La validación es exitosa
+                return ValidationResult.ValidResult;
             else
                 return new ValidationResult(false, "Ingrese un número válido.");
         }
     }
-
 }
\ No newline at end of file
diff --git a/ObjectManipulationManager.cs b/ObjectManipulationManager.cs
new file mode 100644
index 0000000..c1ec8a4
--- /dev/null
+++ b/ObjectManipulationManager.cs
@@ -0,0 +1,616 @@
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Shapes;
+using System.Windows.Threading;
+using CtrEditor.FuncionesBase;
+using CtrEditor.ObjetosSim;
+using Color = System.Windows.Media.Color;
+using System.Collections.ObjectModel;
+
+namespace CtrEditor
+{
+    public class ObjectManipulationManager
+    {
+        private enum ResizeMode
+        {
+            None,
+            Move,
+            Rotate,
+            ResizeWidth,
+            ResizeHeight,
+            ResizeBoth
+        }
+
+        private enum HandleMode
+        {
+            None,
+            Move,
+            ResizeWidth,
+            ResizeHeight,
+            ResizeBoth,
+            Rotate
+        }
+
+        private ResizeMode _currentResizeMode;
+        private readonly MainWindow _mainWindow;
+        private readonly Canvas _canvas;
+        private Point _lastMousePosition;
+        private bool _isDrawingCanvas = false;
+        private bool _isDraggingUserControl = false;
+        private bool _isMovingUserControl = false;
+        private UserControl _currentDraggingControl;
+        private Point _startPointUserControl;
+        private Point _transformedBoundingBoxCenter;
+        private float _lastAngle;
+        private List<Rectangle> _resizeRectangles = new List<Rectangle>();
+        private List<Rectangle> _highlightRectangles = new List<Rectangle>();
+        private System.Threading.Timer _timerRemoveHighlight = null;
+        private Rectangle _currentDraggingRectangle;
+        private dataDebug _dataDebug = new dataDebug();
+        private ObservableCollection<osBase> _selectedObjects = new ObservableCollection<osBase>();
+
+        public ObjectManipulationManager(MainWindow mainWindow, Canvas canvas)
+        {
+            _mainWindow = mainWindow;
+            _canvas = canvas;
+        }
+
+        public ObservableCollection<osBase> SelectedObjects
+        {
+            get => _selectedObjects;
+            private set
+            {
+                _selectedObjects = value;
+                UpdateSelectionVisuals();
+            }
+        }
+
+        private void UpdateSelectionVisuals()
+        {
+            RemoveResizeRectangles();
+            if (_selectedObjects.Any())
+            {
+                AddResizeRectangles(_selectedObjects);
+            }
+        }
+
+        public void SuscribirEventos(UserControl userControl)
+        {
+            userControl.MouseEnter += UserControl_MouseEnter;
+            userControl.MouseLeave += UserControl_MouseLeave;
+            userControl.Cursor = Cursors.Hand;
+
+            userControl.MouseLeftButtonDown += UserControl_MouseLeftButtonDown;
+            userControl.MouseLeftButtonUp += UserControl_MouseLeftButtonUp;
+            userControl.MouseMove += UserControl_MouseMove;
+        }
+
+        private void UserControl_MouseEnter(object sender, MouseEventArgs e)
+        {
+            if (sender is UserControl userControl && userControl is IDataContainer dataContainer)
+            {
+                dataContainer.Highlight(true);
+
+                if (_mainWindow.DataContext is MainViewModel viewModel)
+                {
+                    var selectedObject = viewModel.SelectedItemOsList;
+
+                    if (selectedObject?.VisualRepresentation != userControl)
+                    {
+                        AddHighlightRectangles(userControl);
+                    }
+                }
+            }
+        }
+
+        private void UserControl_MouseLeave(object sender, MouseEventArgs e)
+        {
+            if (sender is UserControl userControl && userControl is IDataContainer dataContainer)
+                dataContainer.Highlight(false);
+        }
+
+        public void AddResizeRectangles(IEnumerable<osBase> selectedObjects)
+        {
+            double rectHighlightSize = 1;
+            RemoveResizeRectangles();
+
+            // Calcular el bounding box que contenga todos los objetos seleccionados
+            Rect boundingBox = CalculateTotalBoundingBox(selectedObjects);
+            
+            FuncionesBase.MutableRect rectBox = new FuncionesBase.MutableRect(boundingBox);
+            rectBox.Left -= (float)rectHighlightSize;
+            rectBox.Right += (float)rectHighlightSize;
+            rectBox.Top -= (float)rectHighlightSize;
+            rectBox.Bottom += (float)rectHighlightSize;
+
+            _transformedBoundingBoxCenter = new Point(
+                boundingBox.Left + boundingBox.Width / 2,
+                boundingBox.Top + boundingBox.Height / 2
+            );
+
+            // Selection rectangle
+            Rectangle selectionRect = CreateSelectionRectangle(rectBox, rectHighlightSize);
+            _resizeRectangles.Add(selectionRect);
+            _canvas.Children.Add(selectionRect);
+
+            // Load rotation cursors
+            Cursor rotationCursorRx = new Cursor(Application.GetResourceStream(
+                new Uri("pack://application:,,,/CtrEditor;component/Icons/rotationRx.cur")).Stream);
+            Cursor rotationCursorSx = new Cursor(Application.GetResourceStream(
+                new Uri("pack://application:,,,/CtrEditor;component/Icons/rotationSx.cur")).Stream);
+
+            // Add resize/rotation handles
+            AddResizeHandles(rectBox, 10, rotationCursorRx, rotationCursorSx);
+        }
+
+        private Rect CalculateTotalBoundingBox(IEnumerable<osBase> selectedObjects)
+        {
+            double left = double.MaxValue;
+            double top = double.MaxValue;
+            double right = double.MinValue;
+            double bottom = double.MinValue;
+
+            foreach (var obj in selectedObjects)
+            {
+                if (obj.VisualRepresentation != null && obj.Show_On_This_Page)
+                {
+                    // Obtener el bounding box del objeto actual
+                    Rect objectBounds = VisualTreeHelper.GetDescendantBounds(obj.VisualRepresentation);
+                    GeneralTransform transform = obj.VisualRepresentation.TransformToAncestor(_canvas);
+                    Rect transformedBounds = transform.TransformBounds(objectBounds);
+
+                    // Actualizar los límites del bounding box total
+                    left = Math.Min(left, transformedBounds.Left);
+                    top = Math.Min(top, transformedBounds.Top);
+                    right = Math.Max(right, transformedBounds.Right);
+                    bottom = Math.Max(bottom, transformedBounds.Bottom);
+                }
+            }
+
+            return new Rect(
+                new Point(left, top),
+                new Point(right, bottom)
+            );
+        }
+
+        private Rectangle CreateSelectionRectangle(FuncionesBase.MutableRect rectBox, double rectHighlightSize)
+        {
+            var rect = new Rectangle
+            {
+                Width = rectBox.Width + (rectHighlightSize * 2),
+                Height = rectBox.Height + (rectHighlightSize * 2),
+                Fill = Brushes.Transparent,
+                Stroke = new SolidColorBrush(Color.FromArgb(180, 0, 120, 215)),
+                StrokeThickness = 1.5,
+                Tag = "Selection",
+                IsHitTestVisible = false,
+                StrokeDashArray = new DoubleCollection(new double[] { 3, 3 })
+            };
+
+            Canvas.SetLeft(rect, rectBox.Left - rectHighlightSize);
+            Canvas.SetTop(rect, rectBox.Top - rectHighlightSize);
+            Canvas.SetZIndex(rect, ((int)ZIndexEnum.RectangulosPropiead - 1));
+
+            return rect;
+        }
+
+        private void AddResizeHandles(FuncionesBase.MutableRect rectBox, double rectSize,
+            Cursor rotationCursorRx, Cursor rotationCursorSx)
+        {
+            var positions = new List<(Point Position, string Tag, Cursor Cursor, Brush Stroke)>
+            {
+                (new Point(rectBox.Left, rectBox.Top), "TopLeft", Cursors.Arrow, Brushes.Gray),
+                (new Point(rectBox.Right, rectBox.Top), "TopRight", rotationCursorRx, Brushes.Red),
+                (new Point(rectBox.Left, rectBox.Bottom), "BottomLeft", rotationCursorSx, Brushes.DarkRed),
+                (new Point(rectBox.Right, rectBox.Bottom), "BottomRight", Cursors.SizeNWSE, Brushes.Blue),
+                (new Point(rectBox.Left + rectBox.Width / 2, rectBox.Top), "TopCenter", Cursors.Arrow, Brushes.Gray),
+                (new Point(rectBox.Left + rectBox.Width / 2, rectBox.Bottom), "BottomCenter", Cursors.SizeNS, Brushes.Blue),
+                (new Point(rectBox.Left, rectBox.Top + rectBox.Height / 2), "CenterLeft", rotationCursorRx, Brushes.Red),
+                (new Point(rectBox.Right, rectBox.Top + rectBox.Height / 2), "CenterRight", Cursors.SizeWE, Brushes.Blue)
+            };
+
+            foreach (var (Position, Tag, Cursor, Stroke) in positions)
+            {
+                var handle = CreateResizeHandle(rectSize, Tag, Cursor, Stroke);
+                SetHandlePosition(handle, Position, rectSize);
+                _resizeRectangles.Add(handle);
+                _canvas.Children.Add(handle);
+            }
+        }
+
+        private Rectangle CreateResizeHandle(double rectSize, string tag, Cursor cursor, Brush stroke)
+        {
+            var handle = new Rectangle
+            {
+                Width = rectSize,
+                Height = rectSize,
+                Fill = Brushes.Transparent,
+                Stroke = stroke,
+                StrokeThickness = 1,
+                Tag = tag,
+                Cursor = cursor
+            };
+
+            handle.MouseLeftButtonDown += UserControl_MouseLeftButtonDown;
+            handle.MouseMove += UserControl_MouseMove;
+            handle.MouseLeftButtonUp += UserControl_MouseLeftButtonUp;
+            handle.MouseLeave += ResizeRectangle_MouseLeave;
+
+            Canvas.SetZIndex(handle, (int)ZIndexEnum.RectangulosPropiead);
+            return handle;
+        }
+
+        private void SetHandlePosition(Rectangle handle, Point position, double rectSize)
+        {
+            if (!double.IsInfinity(position.X) && !double.IsNaN(position.X))
+                Canvas.SetLeft(handle, position.X - rectSize / 2);
+            if (!double.IsInfinity(position.Y) && !double.IsNaN(position.Y))
+                Canvas.SetTop(handle, position.Y - rectSize / 2);
+        }
+
+        public void RemoveResizeRectangles()
+        {
+            if (_resizeRectangles == null || _resizeRectangles.Count == 0)
+                return;
+
+            foreach (var rect in _resizeRectangles)
+            {
+                _canvas.Children.Remove(rect);
+            }
+            _resizeRectangles.Clear();
+        }
+
+        public void MakeResizeRectanglesTransparent()
+        {
+            if (_resizeRectangles == null || _resizeRectangles.Count == 0)
+                return;
+
+            foreach (var rect in _resizeRectangles)
+            {
+                rect.Opacity = 0;
+            }
+        }
+
+        public void MakeResizeRectanglesNormal()
+        {
+            if (_resizeRectangles == null || _resizeRectangles.Count == 0)
+                return;
+
+            foreach (var rect in _resizeRectangles)
+            {
+                rect.Opacity = 1;
+            }
+        }
+
+        private void HandleObjectSelection(UserControl userControl, osBase datos)
+        {
+            var viewModel = _mainWindow.DataContext as MainViewModel;
+            if (viewModel == null) return;
+
+            bool isControlPressed = Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl);
+
+            if (viewModel.IsMultiSelectionActive)
+            {
+                if (isControlPressed)
+                {
+                    // Deseleccionar objeto si está presionada la tecla Ctrl
+                    if (_selectedObjects.Contains(datos))
+                    {
+                        DeselectObject(datos);
+                    }
+                }
+                else if (!_selectedObjects.Contains(datos))
+                {
+                    // Seleccionar objeto si no está ya seleccionado
+                    SelectObject(datos);
+                }
+            }
+            else
+            {
+                // Modo selección única
+                ClearSelection();
+                SelectObject(datos);
+            }
+
+            viewModel.SelectedItemOsList = datos;
+            UpdateSelectionVisuals();
+        }
+
+        public void SelectObject(osBase obj)
+        {
+            if (!_selectedObjects.Contains(obj))
+            {
+                _selectedObjects.Add(obj);
+                obj.IsSelected = true;
+            }
+        }
+
+        public void DeselectObject(osBase obj)
+        {
+            if (_selectedObjects.Contains(obj))
+            {
+                _selectedObjects.Remove(obj);
+                obj.IsSelected = false;
+            }
+        }
+
+        public void ClearSelection()
+        {
+            foreach (var obj in _selectedObjects.ToList())
+            {
+                DeselectObject(obj);
+            }
+            RemoveResizeRectangles();
+            
+            if (_mainWindow.DataContext is MainViewModel viewModel)
+            {
+                viewModel.SelectedItemOsList = null;
+            }
+        }
+
+        public void OnMultiSelectionModeChanged(bool isActive)
+        {
+            if (!isActive)
+            {
+                // Mantener solo el último objeto seleccionado
+                if (_selectedObjects.Count > 1)
+                {
+                    var lastSelected = _selectedObjects.Last();
+                    ClearSelection();
+                    SelectObject(lastSelected);
+                }
+            }
+        }
+
+        private void UserControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+        {
+            if (!_isDrawingCanvas)
+            {
+                if (_resizeRectangles != null && _resizeRectangles.Contains(sender))
+                {
+                    _currentDraggingRectangle = sender as Rectangle;
+                    _lastMousePosition = e.GetPosition(_canvas);
+                    _currentDraggingRectangle.CaptureMouse();
+                    _isMovingUserControl = true;
+                    _startPointUserControl = e.GetPosition(_canvas);
+                    e.Handled = true;
+                    return;
+                }
+
+                var userControl = sender as UserControl;
+                if (userControl != null)
+                {
+                    if (userControl.DataContext is osBase datos)
+                    {
+                        HandleObjectSelection(userControl, datos);
+                    }
+
+                    // Solo iniciar el arrastre si no se presionó Ctrl
+                    if (!(Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)))
+                    {
+                        userControl.CaptureMouse();
+                        _currentDraggingControl = userControl;
+                        _isMovingUserControl = true;
+                        InitializeDrag(e);
+                    }
+                }
+            }
+        }
+
+        private void InitializeDrag(MouseButtonEventArgs e)
+        {
+            _isDraggingUserControl = true;
+            _startPointUserControl = e.GetPosition(_canvas);
+        }
+
+        private void UserControl_MouseMove(object sender, MouseEventArgs e)
+        {
+            if (_isMovingUserControl)
+            {
+                var currentPosition = e.GetPosition(_canvas);
+                MakeResizeRectanglesTransparent();
+
+                if (_isDraggingUserControl)
+                {
+                    HandleDrag(currentPosition);
+                }
+                else if (_currentDraggingRectangle != null)
+                {
+                    string resizeDirection = _currentDraggingRectangle.Tag as string;
+                    if (resizeDirection != null)
+                    {
+                        var mode = DetermineHandleMode(resizeDirection);
+                        switch (mode)
+                        {
+                            case HandleMode.Rotate:
+                                HandleRotation(currentPosition);
+                                break;
+                            case HandleMode.ResizeWidth:
+                            case HandleMode.ResizeHeight:
+                            case HandleMode.ResizeBoth:
+                                HandleResize(currentPosition, mode);
+                                break;
+                        }
+                    }
+                }
+            }
+        }
+
+        private void HandleDrag(Point currentPosition)
+        {
+            var dx = currentPosition.X - _startPointUserControl.X;
+            var dy = currentPosition.Y - _startPointUserControl.Y;
+
+            foreach (var selectedObject in _selectedObjects)
+            {
+                var newX = Canvas.GetLeft(selectedObject.VisualRepresentation) + dx;
+                var newY = Canvas.GetTop(selectedObject.VisualRepresentation) + dy;
+                selectedObject.Move((float)newX, (float)newY);
+            }
+            _startPointUserControl = currentPosition;
+        }
+
+        private void UserControl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
+        {
+            if (_isMovingUserControl)
+            {
+                if (sender is UserControl userControl)
+                {
+                    userControl.ReleaseMouseCapture();
+                }
+                else if (sender is Rectangle rectangle)
+                {
+                    rectangle.ReleaseMouseCapture();
+                }
+
+                UpdateSelectionVisuals();
+                _isDraggingUserControl = false;
+                _isMovingUserControl = false;
+                _currentDraggingRectangle = null;
+                _currentResizeMode = ResizeMode.None;
+                _lastAngle = 0;
+            }
+            MarkUnsavedChanges();
+        }
+
+        private void MarkUnsavedChanges()
+        {
+            if (_mainWindow.DataContext is MainViewModel viewModel)
+            {
+                if (_isMovingUserControl || _isDraggingUserControl)
+                {
+                    viewModel.HasUnsavedChanges = true;
+                }
+            }
+        }
+
+        private void ResizeRectangle_MouseLeave(object sender, MouseEventArgs e)
+        {
+            var rect = sender as Rectangle;
+            if (rect != null)
+            {
+                rect.Fill = Brushes.Transparent;
+            }
+        }
+
+        public void AddHighlightRectangles(UserControl userControl)
+        {
+            double rectSize = 1;
+            RemoveHighlightRectangles();
+
+            if (userControl is IDataContainer dataContainer && dataContainer.Datos is osBase mvBase && mvBase.Show_On_This_Page)
+            {
+                Rect boundingBox = VisualTreeHelper.GetDescendantBounds(userControl);
+                GeneralTransform transform = userControl.TransformToAncestor(_canvas);
+                Rect transformedBoundingBox = transform.TransformBounds(boundingBox);
+
+                FuncionesBase.MutableRect rectBox = new FuncionesBase.MutableRect(transformedBoundingBox);
+                rectBox.Left -= (float)rectSize;
+                rectBox.Right += (float)rectSize;
+                rectBox.Top -= (float)rectSize;
+                rectBox.Bottom += (float)rectSize;
+
+                Rectangle highlightRect = new Rectangle
+                {
+                    Width = rectBox.Width,
+                    Height = rectBox.Height,
+                    Fill = Brushes.Transparent,
+                    Stroke = new SolidColorBrush(Color.FromArgb(128, 0, 0, 0)),
+                    StrokeThickness = 1,
+                    Tag = "Highlight",
+                    IsHitTestVisible = false,
+                    StrokeDashArray = new DoubleCollection(new double[] { 2, 2 })
+                };
+
+                Canvas.SetLeft(highlightRect, rectBox.Left);
+                Canvas.SetTop(highlightRect, rectBox.Top); // Corregido: usando rectBox.Top en lugar de highlightRect.Top
+
+                _highlightRectangles.Add(highlightRect);
+                _canvas.Children.Add(highlightRect);
+                Canvas.SetZIndex(highlightRect, ((int)ZIndexEnum.RectangulosPropiead));
+
+                if (_timerRemoveHighlight == null)
+                    _timerRemoveHighlight = new System.Threading.Timer(TimerCallbackRemoveHighlight, null, Timeout.Infinite, Timeout.Infinite);
+
+                _timerRemoveHighlight.Change(2000, Timeout.Infinite);
+            }
+        }
+
+        private void TimerCallbackRemoveHighlight(object state)
+        {
+            if (Application.Current != null)
+            {
+                Application.Current.Dispatcher.Invoke(RemoveHighlightRectangles);
+            }
+        }
+
+        public void RemoveHighlightRectangles()
+        {
+            if (_highlightRectangles == null || _highlightRectangles.Count == 0)
+                return;
+
+            foreach (var rect in _highlightRectangles)
+            {
+                _canvas.Children.Remove(rect);
+            }
+            _highlightRectangles.Clear();
+        }
+
+        private HandleMode DetermineHandleMode(string resizeDirection)
+        {
+            return resizeDirection switch
+            {
+                "TopLeft" => HandleMode.None,
+                "TopRight" or "BottomLeft" or "CenterLeft" => HandleMode.Rotate,
+                "BottomRight" => HandleMode.ResizeBoth,
+                "TopCenter" => HandleMode.None,
+                "BottomCenter" => HandleMode.ResizeHeight,
+                "CenterRight" => HandleMode.ResizeWidth,
+                _ => HandleMode.None
+            };
+        }
+
+        private void HandleResize(Point currentPosition, HandleMode mode)
+        {
+            foreach (var selectedObject in _selectedObjects)
+            {
+                bool resizeWidth = mode == HandleMode.ResizeWidth || mode == HandleMode.ResizeBoth;
+                bool resizeHeight = mode == HandleMode.ResizeHeight || mode == HandleMode.ResizeBoth;
+                HandleResizeForObject(selectedObject, currentPosition, resizeWidth, resizeHeight);
+            }
+            _startPointUserControl = currentPosition;
+        }
+
+        private void HandleResizeForObject(osBase obj, Point currentPosition, bool activateSizeWidth, bool activateSizeHeight)
+        {
+            double widthChange = activateSizeWidth ? currentPosition.X - _startPointUserControl.X : 0;
+            double heightChange = activateSizeHeight ? currentPosition.Y - _startPointUserControl.Y : 0;
+
+            obj.Resize(widthChange, heightChange);
+        }
+
+        private void HandleRotation(Point currentPosition)
+        {
+            // Calcular el ángulo respecto al centro del bounding box que contiene todos los objetos seleccionados
+            double deltaX = currentPosition.X - _transformedBoundingBoxCenter.X;
+            double deltaY = currentPosition.Y - _transformedBoundingBoxCenter.Y;
+            double angle = Math.Atan2(deltaY, deltaX) * (180 / Math.PI);
+
+            if ((_lastAngle == 0 && angle != 0) || Math.Abs(angle - _lastAngle) > 45)
+            {
+                _lastAngle = (float)angle;
+            }
+            else
+            {
+                double deltaAngle = angle - _lastAngle;
+                foreach (var selectedObject in _selectedObjects)
+                {
+                    selectedObject.Rotate(deltaAngle);
+                }
+                _lastAngle = (float)angle;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/ObjetosSim/osBase.cs b/ObjetosSim/osBase.cs
index df4cdce..984749b 100644
--- a/ObjetosSim/osBase.cs
+++ b/ObjetosSim/osBase.cs
@@ -509,6 +509,8 @@ namespace CtrEditor.ObjetosSim
         [property: Hidden]
         UniqueId cloned_from;
 
+        [ObservableProperty]
+        private bool isSelected;
 
         private async void TimerCallback(object state)
         {