using System.Globalization; using System.Windows; 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.FuncionesBase; namespace CtrEditor { /// /// Interaction logic for MainWindow.xaml /// /// public partial class MainWindow : Window { // Para el Canvas private Point _lastMousePosition; private bool _isDrawingCanvas = false; private bool _isDraggingCanvas = false; private Image imagenDeFondo; private List resizeRectangles = new List(); private DispatcherTimer _zoomTimer; private double _targetZoomFactor; private double _initialZoomFactor; private double _currentZoomStep; private Point _zoomCursorPosition; private const int ZoomDuration = 500; // Duración del zoom en milisegundos private int _ZoomDuration; 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 double _initialAngleUserControl; private TextBlock _angleDisplayTextBlock; public MainWindow() { InitializeComponent(); _zoomTimer = new DispatcherTimer(); _zoomTimer.Interval = TimeSpan.FromMilliseconds(1); _zoomTimer.Tick += ZoomTimer_Tick; _stopwatch = new Stopwatch(); this.Loaded += MainWindow_Loaded; ImagenEnTrabajoScrollViewer.PreviewMouseWheel += ImagenEnTrabajoCanvas_MouseWheel; ImagenEnTrabajoCanvas.MouseDown += Canvas_MouseDown_Panning; ImagenEnTrabajoCanvas.MouseMove += Canvas_MouseMove_Panning; ImagenEnTrabajoCanvas.MouseUp += Canvas_MouseUp_Panning; } private void MainWindow_Loaded(object sender, RoutedEventArgs e) { if (DataContext is MainViewModel viewModel) { viewModel.MainWindow = this; viewModel.ImageSelected += ViewModel_ImageSelected; viewModel?.LoadInitialData(); // Carga la primera imagen por defecto una vez cargada la ventana principal 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; // Suscribir a eventos de mouse para panning userControl.MouseLeftButtonDown += UserControl_MouseLeftButtonDown; userControl.MouseLeftButtonUp += UserControl_MouseLeftButtonUp; userControl.MouseMove += UserControl_MouseMove; } public void AgregarRegistrarUserControlCanvas(UserControl userControl) { if (userControl is IDataContainer dataContainer) { SuscribirEventos(userControl); // Añade el UserControl al Canvas Canvas.SetZIndex(userControl, ((int)dataContainer.ZIndex())); ImagenEnTrabajoCanvas.Children.Add(userControl); } } public void EliminarUserControlDelCanvas(UserControl userControl) { if (ImagenEnTrabajoCanvas.Children.Contains(userControl)) { ImagenEnTrabajoCanvas.Children.Remove(userControl); } } private void UserControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if (!_isDrawingCanvas) { if (resizeRectangles != null && resizeRectangles.Contains(sender)) { _isResizingUserControl = true; lastMousePosition = e.GetPosition(ImagenEnTrabajoCanvas); ((Rectangle)sender).CaptureMouse(); _isMovingUserControl = true; lastAngle = 0; } else { var userControl = sender as UserControl; userControl.CaptureMouse(); // Importante para recibir eventos de movimiento incluso fuera del control _currentDraggingControl = userControl; _isMovingUserControl = true; RemoveResizeRectangles(); if (sender is UserControl control && control.DataContext is osBase datos) { var viewModel = DataContext as MainViewModel; if (viewModel != null) { viewModel.SelectedItemOsList = datos; // Esto desencadenará ListaOs_SelectionChanged } } // ROTACION if (Keyboard.IsKeyDown(Key.LeftShift)) { // Inicializar la rotación _isRotatingUserControl = true; RotateTransform rotateTransform = userControl.RenderTransform as RotateTransform; if (rotateTransform == null) { rotateTransform = new RotateTransform(); userControl.RenderTransform = rotateTransform; } _initialAngleUserControl = rotateTransform.Angle; // 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); } // TAMANO else if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) { // Inicializar el cambio de tamaño _isResizingUserControl = true; _startPointUserControl = e.GetPosition(ImagenEnTrabajoCanvas); } // MOVIMIENTO else { // 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 System.Threading.Timer timerRemoveResizeRectangles = null; public void ResetTimerRemoveResizeRectangles() { if (timerRemoveResizeRectangles == null) timerRemoveResizeRectangles = new System.Threading.Timer(TimerCallbackRemoveResizeRectangles, null, Timeout.Infinite, Timeout.Infinite); timerRemoveResizeRectangles.Change(2000, Timeout.Infinite); } private async void TimerCallbackRemoveResizeRectangles(object state) { Application.Current.Dispatcher.Invoke(() => { // Realiza tus cambios en la interfaz de usuario aquí MakeResizeRectanglesTransparent(); }); } public void PauseTimerRemoveResizeRectangles() { if (timerRemoveResizeRectangles != null) { timerRemoveResizeRectangles.Change(Timeout.Infinite, Timeout.Infinite); } } private void AddResizeRectangles(UserControl userControl) { double rectSize = 10; 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)rectSize; rectBox.Right += (float)rectSize; rectBox.Top -= (float)rectSize; rectBox.Bottom += (float)rectSize; transformedBoundingBoxCenter.X = transformedBoundingBox.Left + transformedBoundingBox.Width / 2; transformedBoundingBoxCenter.Y = transformedBoundingBox.Top + transformedBoundingBox.Height / 2; // 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>() { new Tuple(new Point(rectBox.Left, rectBox.Top), "TopLeft"), new Tuple(new Point(rectBox.Right, rectBox.Top), "TopRight"), new Tuple(new Point(rectBox.Left, rectBox.Bottom), "BottomLeft"), new Tuple(new Point(rectBox.Right, rectBox.Bottom), "BottomRight"), new Tuple(new Point(rectBox.Left + rectBox.Width / 2, rectBox.Top), "TopCenter"), new Tuple(new Point(rectBox.Left + rectBox.Width / 2, rectBox.Bottom), "BottomCenter"), new Tuple(new Point(rectBox.Left, rectBox.Top + rectBox.Height / 2), "CenterLeft"), new Tuple(new Point(rectBox.Right, rectBox.Top + rectBox.Height / 2), "CenterRight") }; 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; } Canvas.SetLeft(rect, position.Item1.X - rectSize / 2); Canvas.SetTop(rect, 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)); ResetTimerRemoveResizeRectangles(); } } } private void ResizeRectangle_MouseLeave(object sender, MouseEventArgs e) { var rect = sender as Rectangle; rect.Fill = Brushes.Transparent; // Volver al color original ResetTimerRemoveResizeRectangles(); } 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 Point lastMousePosition; private void UserControl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { 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(); PauseTimerRemoveResizeRectangles(); } if (_isMovingUserControl && _currentDraggingControl != null) { var currentPosition = e.GetPosition(ImagenEnTrabajoCanvas); 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 (_currentDraggingControl != null) { RemoveResizeRectangles(); } if (sender is UserControl userControl) if (userControl is IDataContainer dataContainer) { dataContainer.Highlight(true); _currentDraggingControl = sender as UserControl; AddResizeRectangles(_currentDraggingControl); } } 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)); if (imagenDeFondo == null) { imagenDeFondo = new Image(); ImagenEnTrabajoCanvas.Children.Add(imagenDeFondo); } 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--) { if (ImagenEnTrabajoCanvas.Children[i] is not Image) { ImagenEnTrabajoCanvas.Children.RemoveAt(i); } } // 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) { if (_isDraggingCanvas) { _isDraggingCanvas = false; ImagenEnTrabajoScrollViewer.ReleaseMouseCapture(); // Finaliza la captura del ratón } } private void Canvas_MouseDown_Panning(object sender, MouseButtonEventArgs e) { if (e.LeftButton == MouseButtonState.Pressed && !_isDrawingCanvas && !_isMovingUserControl) { // Indica que se inicia el panning _isDraggingCanvas = true; // Guarda la posición actual del ratón _lastMousePosition = e.GetPosition(ImagenEnTrabajoScrollViewer); //ImagenEnTrabajoScrollViewer.CaptureMouse(); // Importante para capturar el movimiento } } private void Canvas_MouseMove_Panning(object sender, MouseEventArgs e) { if (_isDraggingCanvas) { var currentPosition = e.GetPosition(ImagenEnTrabajoScrollViewer); var dx = currentPosition.X - _lastMousePosition.X; var dy = currentPosition.Y - _lastMousePosition.Y; MakeResizeRectanglesTransparent(); // Obtener la transformación actual del Canvas var transform = (TranslateTransform)((TransformGroup)ImagenEnTrabajoCanvas.RenderTransform).Children.First(t => t is TranslateTransform); transform.X += dx; transform.Y += dy; _lastMousePosition = currentPosition; } } private void ImagenEnTrabajoCanvas_MouseWheel(object sender, MouseWheelEventArgs e) { MakeResizeRectanglesTransparent(); _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); _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; _stopwatch.Restart(); _zoomTimer.Start(); e.Handled = true; } private void ZoomTimer_Tick(object sender, EventArgs e) { double elapsedMilliseconds = _stopwatch.ElapsedMilliseconds; if (elapsedMilliseconds >= _ZoomDuration) { _zoomTimer.Stop(); _stopwatch.Stop(); // Volver a activar el escalado de alta calidad después de completar el zoom RenderOptions.SetBitmapScalingMode(ImagenEnTrabajoCanvas, BitmapScalingMode.HighQuality); return; } 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); // 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 zoomFactor = _initialZoomFactor + (_targetZoomFactor - _initialZoomFactor) * easeOutT; // Asegúrate de no ir por debajo del zoom mínimo zoomFactor = Math.Max(zoomFactor, MinZoomScale); Point cursorPosition = _zoomCursorPosition; var relativeX = cursorPosition.X * st.ScaleX + tt.X; var relativeY = cursorPosition.Y * st.ScaleY + tt.Y; st.ScaleX = zoomFactor; st.ScaleY = zoomFactor; tt.X = relativeX - cursorPosition.X * st.ScaleX; 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) { //PanelEdicion.Children.Clear(); // Limpiar el panel existente UserControlFactory.LimpiarPropiedadesosDatos(PanelEdicion); if (e.AddedItems.Count > 0 && e.AddedItems[0] is osBase selectedObject) { if (selectedObject.VisualRepresentation != null) { _currentDraggingControl = selectedObject.VisualRepresentation; AddResizeRectangles(_currentDraggingControl); } CargarPropiedadesosDatos(selectedObject); } } } 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 else return new ValidationResult(false, "Ingrese un número válido."); } } }