2025-02-19 10:57:15 -03:00
|
|
|
|
using CtrEditor.ObjetosSim;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using System.Globalization;
|
2024-05-01 14:45:20 -03:00
|
|
|
|
using System.Windows;
|
|
|
|
|
using System.Windows.Controls;
|
|
|
|
|
using System.Windows.Input;
|
|
|
|
|
using System.Windows.Media;
|
|
|
|
|
using System.Windows.Media.Imaging;
|
2025-02-21 18:25:14 -03:00
|
|
|
|
using System.Windows.Shapes;
|
2024-06-09 12:33:09 -03:00
|
|
|
|
using System.Windows.Threading;
|
2025-02-18 14:08:55 -03:00
|
|
|
|
using MouseEventArgs = System.Windows.Input.MouseEventArgs;
|
|
|
|
|
using UserControl = System.Windows.Controls.UserControl;
|
2024-05-11 11:58:55 -03:00
|
|
|
|
|
2024-05-01 14:45:20 -03:00
|
|
|
|
namespace CtrEditor
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Interaction logic for MainWindow.xaml
|
|
|
|
|
/// </summary>
|
|
|
|
|
public partial class MainWindow : Window
|
|
|
|
|
{
|
2024-05-04 09:06:33 -03:00
|
|
|
|
// Para el Canvas
|
2024-05-03 03:58:21 -03:00
|
|
|
|
private Point _lastMousePosition;
|
2024-05-04 09:06:33 -03:00
|
|
|
|
private bool _isDrawingCanvas = false;
|
|
|
|
|
private bool _isDraggingCanvas = false;
|
2024-05-03 03:58:21 -03:00
|
|
|
|
private Image imagenDeFondo;
|
2025-02-18 14:08:55 -03:00
|
|
|
|
internal ObjectManipulationManager _objectManager;
|
2025-02-24 07:37:52 -03:00
|
|
|
|
private Rectangle _panningArea;
|
2024-05-03 03:58:21 -03:00
|
|
|
|
|
2025-02-18 14:08:55 -03:00
|
|
|
|
// Temporizadores y animación
|
2024-06-09 12:33:09 -03:00
|
|
|
|
private DispatcherTimer _zoomTimer;
|
2024-06-09 16:26:09 -03:00
|
|
|
|
private double _targetZoomFactor;
|
|
|
|
|
private double _initialZoomFactor;
|
|
|
|
|
private double _currentZoomStep;
|
|
|
|
|
private Point _zoomCursorPosition;
|
|
|
|
|
private const int ZoomDuration = 500; // Duración del zoom en milisegundos
|
2024-06-28 14:47:08 -03:00
|
|
|
|
private int _ZoomDuration;
|
2024-06-09 16:26:09 -03:00
|
|
|
|
private const double MinZoomScale = 0.1; // Límite mínimo de zoom
|
|
|
|
|
private Stopwatch _stopwatch;
|
2024-06-09 12:33:09 -03:00
|
|
|
|
|
2025-02-18 14:08:55 -03:00
|
|
|
|
private dataDebug dataDebug = new dataDebug();
|
2024-05-04 09:06:33 -03:00
|
|
|
|
|
2024-05-01 14:45:20 -03:00
|
|
|
|
public MainWindow()
|
|
|
|
|
{
|
|
|
|
|
InitializeComponent();
|
2024-06-09 12:33:09 -03:00
|
|
|
|
|
2025-02-18 14:08:55 -03:00
|
|
|
|
_objectManager = new ObjectManipulationManager(this, ImagenEnTrabajoCanvas);
|
|
|
|
|
|
2025-02-24 07:37:52 -03:00
|
|
|
|
_panningArea = new Rectangle
|
|
|
|
|
{
|
|
|
|
|
Fill = Brushes.Transparent,
|
|
|
|
|
IsHitTestVisible = true
|
|
|
|
|
};
|
|
|
|
|
|
2025-02-18 14:08:55 -03:00
|
|
|
|
// Inicializar temporizador de zoom
|
2024-06-09 16:26:09 -03:00
|
|
|
|
_zoomTimer = new DispatcherTimer();
|
|
|
|
|
_zoomTimer.Interval = TimeSpan.FromMilliseconds(1);
|
|
|
|
|
_zoomTimer.Tick += ZoomTimer_Tick;
|
|
|
|
|
_stopwatch = new Stopwatch();
|
|
|
|
|
|
2025-02-18 14:08:55 -03:00
|
|
|
|
// Suscribir eventos
|
2024-05-03 03:58:21 -03:00
|
|
|
|
this.Loaded += MainWindow_Loaded;
|
|
|
|
|
ImagenEnTrabajoScrollViewer.PreviewMouseWheel += ImagenEnTrabajoCanvas_MouseWheel;
|
2025-02-24 07:37:52 -03:00
|
|
|
|
|
2024-05-03 03:58:21 -03:00
|
|
|
|
ImagenEnTrabajoCanvas.MouseDown += Canvas_MouseDown_Panning;
|
|
|
|
|
ImagenEnTrabajoCanvas.MouseMove += Canvas_MouseMove_Panning;
|
|
|
|
|
ImagenEnTrabajoCanvas.MouseUp += Canvas_MouseUp_Panning;
|
2025-02-24 07:37:52 -03:00
|
|
|
|
|
|
|
|
|
_panningArea.MouseDown += Canvas_MouseDown_Panning;
|
|
|
|
|
_panningArea.MouseRightButtonDown += Canvas_MouseRightButtonDown;
|
2025-02-13 12:52:33 -03:00
|
|
|
|
this.KeyDown += MainWindow_KeyDown;
|
2025-02-17 11:16:40 -03:00
|
|
|
|
ImagenEnTrabajoCanvas.MouseEnter += Canvas_MouseEnter;
|
2025-02-18 14:08:55 -03:00
|
|
|
|
this.Closed += MainWindow_Closed;
|
|
|
|
|
|
|
|
|
|
// Importante: Agregar el evento para el menú contextual
|
|
|
|
|
ImagenEnTrabajoCanvas.MouseRightButtonDown += Canvas_MouseRightButtonDown;
|
2025-02-21 18:25:14 -03:00
|
|
|
|
|
|
|
|
|
// Asegurarse de que estos eventos estén conectados
|
|
|
|
|
ImagenEnTrabajoCanvas.MouseUp += Canvas_MouseUp_Panning;
|
2024-05-03 03:58:21 -03:00
|
|
|
|
}
|
2025-02-13 12:52:33 -03:00
|
|
|
|
|
2024-05-03 03:58:21 -03:00
|
|
|
|
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (DataContext is MainViewModel viewModel)
|
|
|
|
|
{
|
2024-05-18 06:49:02 -03:00
|
|
|
|
viewModel.MainWindow = this;
|
2024-06-28 14:47:08 -03:00
|
|
|
|
viewModel.ImageSelected += ViewModel_ImageSelected;
|
2025-02-18 14:08:55 -03:00
|
|
|
|
viewModel?.LoadInitialData();
|
2024-05-14 03:15:54 -03:00
|
|
|
|
viewModel.simulationManager.DebugCanvas = ImagenEnTrabajoCanvas;
|
2024-05-31 14:25:24 -03:00
|
|
|
|
viewModel.MainCanvas = ImagenEnTrabajoCanvas;
|
2024-05-03 03:58:21 -03:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-05-04 06:00:52 -03:00
|
|
|
|
|
2024-05-18 06:49:02 -03:00
|
|
|
|
public void SuscribirEventos(UserControl userControl)
|
2024-05-04 06:00:52 -03:00
|
|
|
|
{
|
2025-02-18 14:08:55 -03:00
|
|
|
|
_objectManager.SuscribirEventos(userControl);
|
2024-05-18 06:49:02 -03:00
|
|
|
|
}
|
2024-05-04 09:06:33 -03:00
|
|
|
|
|
2024-05-18 06:49:02 -03:00
|
|
|
|
public void AgregarRegistrarUserControlCanvas(UserControl userControl)
|
|
|
|
|
{
|
|
|
|
|
if (userControl is IDataContainer dataContainer)
|
|
|
|
|
{
|
|
|
|
|
SuscribirEventos(userControl);
|
2025-02-13 10:00:47 -03:00
|
|
|
|
Canvas.SetZIndex(userControl, ((int)dataContainer.ZIndex_Base() + dataContainer.zIndex_fromFrames));
|
2024-05-14 03:15:54 -03:00
|
|
|
|
ImagenEnTrabajoCanvas.Children.Add(userControl);
|
2024-05-04 06:00:52 -03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-18 09:58:41 -03:00
|
|
|
|
public void EliminarUserControlDelCanvas(UserControl userControl)
|
|
|
|
|
{
|
|
|
|
|
if (ImagenEnTrabajoCanvas.Children.Contains(userControl))
|
2024-06-28 14:47:08 -03:00
|
|
|
|
{
|
2024-05-18 09:58:41 -03:00
|
|
|
|
ImagenEnTrabajoCanvas.Children.Remove(userControl);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-03 03:58:21 -03:00
|
|
|
|
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);
|
|
|
|
|
|
2025-02-24 07:37:52 -03:00
|
|
|
|
// Actualizar dimensiones del canvas
|
|
|
|
|
ImagenEnTrabajoCanvas.Width = bitmap.Width;
|
|
|
|
|
ImagenEnTrabajoCanvas.Height = bitmap.Height;
|
|
|
|
|
|
|
|
|
|
// Configurar el área de panning
|
|
|
|
|
_panningArea.Width = bitmap.Width * 4;
|
|
|
|
|
_panningArea.Height = bitmap.Height * 4;
|
|
|
|
|
|
|
|
|
|
// Posicionar el área de panning centrada respecto a la imagen
|
|
|
|
|
Canvas.SetLeft(_panningArea, -bitmap.Width / 4);
|
|
|
|
|
Canvas.SetTop(_panningArea, -bitmap.Height / 4);
|
|
|
|
|
Canvas.SetZIndex(_panningArea, -1); // Asegurar que está detrás de todo
|
|
|
|
|
|
|
|
|
|
// Asegurarse de que el área de panning está en el canvas
|
|
|
|
|
if (!ImagenEnTrabajoCanvas.Children.Contains(_panningArea))
|
|
|
|
|
{
|
|
|
|
|
ImagenEnTrabajoCanvas.Children.Add(_panningArea);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Posicionar la imagen
|
|
|
|
|
Canvas.SetLeft(imagenDeFondo, 0);
|
|
|
|
|
Canvas.SetTop(imagenDeFondo, 0);
|
|
|
|
|
|
|
|
|
|
// Eliminar solo los ROIs, no la imagen de fondo ni el área de panning
|
2024-05-03 03:58:21 -03:00
|
|
|
|
for (int i = ImagenEnTrabajoCanvas.Children.Count - 1; i >= 0; i--)
|
|
|
|
|
{
|
2025-02-24 07:37:52 -03:00
|
|
|
|
var child = ImagenEnTrabajoCanvas.Children[i];
|
|
|
|
|
if (child != imagenDeFondo && child != _panningArea)
|
2024-05-03 03:58:21 -03:00
|
|
|
|
{
|
|
|
|
|
ImagenEnTrabajoCanvas.Children.RemoveAt(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-05-01 14:45:20 -03:00
|
|
|
|
}
|
2024-05-02 11:06:45 -03:00
|
|
|
|
|
2024-05-03 03:58:21 -03:00
|
|
|
|
private void Canvas_MouseUp_Panning(object sender, MouseButtonEventArgs e)
|
|
|
|
|
{
|
2024-05-04 09:06:33 -03:00
|
|
|
|
if (_isDraggingCanvas)
|
2024-05-03 03:58:21 -03:00
|
|
|
|
{
|
2024-05-04 09:06:33 -03:00
|
|
|
|
_isDraggingCanvas = false;
|
2025-02-18 14:08:55 -03:00
|
|
|
|
ImagenEnTrabajoCanvas.ReleaseMouseCapture();
|
2025-02-18 17:52:27 -03:00
|
|
|
|
Mouse.OverrideCursor = null;
|
2025-02-17 11:16:40 -03:00
|
|
|
|
|
2025-02-18 17:52:27 -03:00
|
|
|
|
if (_objectManager.SelectedObjects.Count > 0)
|
2025-02-17 11:16:40 -03:00
|
|
|
|
{
|
2025-02-18 14:08:55 -03:00
|
|
|
|
_objectManager.MakeResizeRectanglesNormal();
|
2025-02-18 17:52:27 -03:00
|
|
|
|
_objectManager.UpdateSelectionVisuals(); // Agregar esta línea
|
2025-02-17 11:16:40 -03:00
|
|
|
|
}
|
2025-02-18 14:08:55 -03:00
|
|
|
|
e.Handled = true;
|
2024-05-03 03:58:21 -03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Canvas_MouseDown_Panning(object sender, MouseButtonEventArgs e)
|
|
|
|
|
{
|
2025-02-18 14:08:55 -03:00
|
|
|
|
if (e.LeftButton == MouseButtonState.Pressed && !_isDrawingCanvas)
|
2024-05-03 03:58:21 -03:00
|
|
|
|
{
|
2025-02-24 07:37:52 -03:00
|
|
|
|
// Permitir el panning cuando se hace clic en el área de panning o en la imagen
|
|
|
|
|
if (e.Source == _panningArea ||
|
2025-02-21 18:25:14 -03:00
|
|
|
|
e.Source == imagenDeFondo ||
|
2025-02-24 07:37:52 -03:00
|
|
|
|
e.Source == ImagenEnTrabajoCanvas ||
|
2025-02-21 18:25:14 -03:00
|
|
|
|
(e.Source is Rectangle rect && rect == _objectManager._selectionRectangle))
|
2025-02-17 11:16:40 -03:00
|
|
|
|
{
|
2025-02-21 18:25:14 -03:00
|
|
|
|
ImagenEnTrabajoCanvas.Focus();
|
2025-02-18 14:08:55 -03:00
|
|
|
|
|
2025-02-21 18:25:14 -03:00
|
|
|
|
if (_objectManager.IsRectangleSelectionActive)
|
2025-02-17 11:16:40 -03:00
|
|
|
|
{
|
2025-02-21 18:25:14 -03:00
|
|
|
|
if (!_objectManager.HasActiveSelectionRectangle)
|
|
|
|
|
{
|
|
|
|
|
_objectManager.StartRectangleSelection(e.GetPosition(ImagenEnTrabajoCanvas));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_objectManager.FinishRectangleSelection(e.GetPosition(ImagenEnTrabajoCanvas));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_isDraggingCanvas = true;
|
|
|
|
|
_lastMousePosition = e.GetPosition(ImagenEnTrabajoScrollViewer);
|
2025-02-24 07:37:52 -03:00
|
|
|
|
ImagenEnTrabajoCanvas.CaptureMouse(); // Importante: siempre capturar en el Canvas principal
|
2025-02-21 18:25:14 -03:00
|
|
|
|
Mouse.OverrideCursor = Cursors.Hand;
|
|
|
|
|
|
|
|
|
|
if (DataContext is MainViewModel viewModel)
|
|
|
|
|
{
|
|
|
|
|
viewModel.SelectedItemOsList = null;
|
|
|
|
|
_objectManager.RemoveResizeRectangles();
|
|
|
|
|
}
|
2025-02-17 11:16:40 -03:00
|
|
|
|
}
|
2025-02-18 14:08:55 -03:00
|
|
|
|
e.Handled = true;
|
2025-02-17 11:16:40 -03:00
|
|
|
|
}
|
2024-05-03 03:58:21 -03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Canvas_MouseMove_Panning(object sender, MouseEventArgs e)
|
|
|
|
|
{
|
2025-02-21 18:25:14 -03:00
|
|
|
|
if (_objectManager.IsRectangleSelectionActive && _objectManager.HasActiveSelectionRectangle)
|
|
|
|
|
{
|
|
|
|
|
var currentPoint = e.GetPosition(ImagenEnTrabajoCanvas);
|
|
|
|
|
_objectManager.UpdateRectangleSelection(currentPoint);
|
|
|
|
|
}
|
|
|
|
|
else if (_isDraggingCanvas && e.LeftButton == MouseButtonState.Pressed)
|
2024-05-03 03:58:21 -03:00
|
|
|
|
{
|
|
|
|
|
var currentPosition = e.GetPosition(ImagenEnTrabajoScrollViewer);
|
|
|
|
|
var dx = currentPosition.X - _lastMousePosition.X;
|
|
|
|
|
var dy = currentPosition.Y - _lastMousePosition.Y;
|
|
|
|
|
|
2025-02-18 14:08:55 -03:00
|
|
|
|
_objectManager.MakeResizeRectanglesTransparent();
|
|
|
|
|
_objectManager.RemoveHighlightRectangles();
|
2024-06-28 14:47:08 -03:00
|
|
|
|
|
2025-02-18 14:08:55 -03:00
|
|
|
|
var transform = (TranslateTransform)((TransformGroup)ImagenEnTrabajoCanvas.RenderTransform)
|
|
|
|
|
.Children.First(t => t is TranslateTransform);
|
2024-06-09 12:33:09 -03:00
|
|
|
|
transform.X += dx;
|
|
|
|
|
transform.Y += dy;
|
2024-05-03 03:58:21 -03:00
|
|
|
|
|
|
|
|
|
_lastMousePosition = currentPosition;
|
2025-02-18 14:08:55 -03:00
|
|
|
|
e.Handled = true;
|
2024-05-03 03:58:21 -03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ImagenEnTrabajoCanvas_MouseWheel(object sender, MouseWheelEventArgs e)
|
|
|
|
|
{
|
2025-02-18 14:08:55 -03:00
|
|
|
|
_objectManager.MakeResizeRectanglesTransparent();
|
|
|
|
|
_objectManager.RemoveHighlightRectangles();
|
2024-06-28 14:47:08 -03:00
|
|
|
|
|
2025-02-18 14:08:55 -03:00
|
|
|
|
_initialZoomFactor = ((ScaleTransform)((TransformGroup)ImagenEnTrabajoCanvas.RenderTransform)
|
|
|
|
|
.Children.First(t => t is ScaleTransform)).ScaleX;
|
2024-06-09 16:26:09 -03:00
|
|
|
|
|
|
|
|
|
double minZoomFactor = Math.Min(
|
|
|
|
|
ImagenEnTrabajoScrollViewer.ViewportWidth / ImagenEnTrabajoCanvas.ActualWidth,
|
2025-02-24 07:37:52 -03:00
|
|
|
|
ImagenEnTrabajoScrollViewer.ViewportHeight / ImagenEnTrabajoCanvas.ActualHeight)/1.5;
|
2024-06-09 16:26:09 -03:00
|
|
|
|
|
2025-02-18 14:08:55 -03:00
|
|
|
|
_targetZoomFactor = e.Delta > 0 ?
|
|
|
|
|
_initialZoomFactor * 1.4 :
|
|
|
|
|
Math.Max(_initialZoomFactor * 0.75, minZoomFactor);
|
2024-06-09 16:26:09 -03:00
|
|
|
|
|
|
|
|
|
_zoomCursorPosition = e.GetPosition(ImagenEnTrabajoCanvas);
|
|
|
|
|
|
|
|
|
|
RenderOptions.SetBitmapScalingMode(ImagenEnTrabajoCanvas, BitmapScalingMode.LowQuality);
|
|
|
|
|
|
2025-02-18 14:08:55 -03:00
|
|
|
|
_ZoomDuration = !_zoomTimer.IsEnabled ? ZoomDuration : ZoomDuration / 3;
|
2024-06-09 16:26:09 -03:00
|
|
|
|
|
|
|
|
|
_stopwatch.Restart();
|
|
|
|
|
_zoomTimer.Start();
|
|
|
|
|
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ZoomTimer_Tick(object sender, EventArgs e)
|
|
|
|
|
{
|
2025-02-13 10:00:47 -03:00
|
|
|
|
double elapsedMilliseconds = _stopwatch.Elapsed.TotalMilliseconds;
|
2024-06-09 16:26:09 -03:00
|
|
|
|
|
|
|
|
|
if (elapsedMilliseconds >= _ZoomDuration)
|
|
|
|
|
{
|
|
|
|
|
_zoomTimer.Stop();
|
|
|
|
|
_stopwatch.Stop();
|
|
|
|
|
|
|
|
|
|
RenderOptions.SetBitmapScalingMode(ImagenEnTrabajoCanvas, BitmapScalingMode.HighQuality);
|
|
|
|
|
|
2025-02-18 17:52:27 -03:00
|
|
|
|
if (_objectManager.SelectedObjects.Count > 0)
|
2025-02-17 11:16:40 -03:00
|
|
|
|
{
|
2025-02-18 14:08:55 -03:00
|
|
|
|
_objectManager.MakeResizeRectanglesNormal();
|
2025-02-18 17:52:27 -03:00
|
|
|
|
_objectManager.UpdateSelectionVisuals(); // Agregar esta línea
|
2025-02-17 11:16:40 -03:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-09 16:26:09 -03:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-09 12:33:09 -03:00
|
|
|
|
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);
|
|
|
|
|
|
2024-06-09 16:26:09 -03:00
|
|
|
|
double t = elapsedMilliseconds / _ZoomDuration;
|
2025-02-18 14:08:55 -03:00
|
|
|
|
double easeOutT = t * (2 - t);
|
2024-06-09 16:26:09 -03:00
|
|
|
|
double zoomFactor = _initialZoomFactor + (_targetZoomFactor - _initialZoomFactor) * easeOutT;
|
|
|
|
|
|
|
|
|
|
zoomFactor = Math.Max(zoomFactor, MinZoomScale);
|
|
|
|
|
|
|
|
|
|
Point cursorPosition = _zoomCursorPosition;
|
2024-05-03 03:58:21 -03:00
|
|
|
|
|
2024-06-09 12:33:09 -03:00
|
|
|
|
var relativeX = cursorPosition.X * st.ScaleX + tt.X;
|
|
|
|
|
var relativeY = cursorPosition.Y * st.ScaleY + tt.Y;
|
2024-05-03 03:58:21 -03:00
|
|
|
|
|
2024-06-09 16:26:09 -03:00
|
|
|
|
st.ScaleX = zoomFactor;
|
|
|
|
|
st.ScaleY = zoomFactor;
|
2024-05-03 03:58:21 -03:00
|
|
|
|
|
2024-06-09 12:33:09 -03:00
|
|
|
|
tt.X = relativeX - cursorPosition.X * st.ScaleX;
|
|
|
|
|
tt.Y = relativeY - cursorPosition.Y * st.ScaleY;
|
2024-05-03 03:58:21 -03:00
|
|
|
|
}
|
|
|
|
|
|
2025-02-18 14:08:55 -03:00
|
|
|
|
private void ListaOs_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
|
|
|
|
|
{
|
2025-02-19 17:27:33 -03:00
|
|
|
|
ImagenEnTrabajoCanvas.Focus(); // Asegurar que el canvas tiene el foco
|
2025-02-18 14:08:55 -03:00
|
|
|
|
UserControlFactory.LimpiarPropiedadesosDatos(PanelEdicion);
|
|
|
|
|
|
|
|
|
|
if (e.AddedItems.Count > 0 && e.AddedItems[0] is osBase selectedObject)
|
|
|
|
|
{
|
|
|
|
|
// 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
|
|
|
|
|
{
|
|
|
|
|
_objectManager.RemoveResizeRectangles();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-24 07:37:52 -03:00
|
|
|
|
private void ImagePathButton_Click(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
var dlg = new Ookii.Dialogs.Wpf.VistaOpenFileDialog
|
|
|
|
|
{
|
|
|
|
|
Filter = "Image files (*.png;*.jpg;*.bmp)|*.png;*.jpg;*.bmp|All files (*.*)|*.*"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (dlg.ShowDialog() == true)
|
|
|
|
|
{
|
|
|
|
|
// El DataContext de este botón es el PropertyItem asociado
|
|
|
|
|
if ((sender as Button)?.DataContext is Xceed.Wpf.Toolkit.PropertyGrid.PropertyItem propertyItem)
|
|
|
|
|
{
|
|
|
|
|
propertyItem.Value = dlg.FileName;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-02-18 14:08:55 -03:00
|
|
|
|
private void MainWindow_KeyDown(object sender, KeyEventArgs e)
|
|
|
|
|
{
|
2025-02-21 11:12:10 -03:00
|
|
|
|
// Only force canvas focus if PanelEdicion doesn't have focus
|
|
|
|
|
if (!PanelEdicion.IsKeyboardFocusWithin)
|
2025-02-19 17:27:33 -03:00
|
|
|
|
{
|
2025-02-21 11:12:10 -03:00
|
|
|
|
if (!ImagenEnTrabajoCanvas.IsFocused)
|
|
|
|
|
{
|
|
|
|
|
ImagenEnTrabajoCanvas.Focus();
|
|
|
|
|
}
|
2025-02-19 17:27:33 -03:00
|
|
|
|
}
|
|
|
|
|
|
2025-02-18 14:08:55 -03:00
|
|
|
|
if (DataContext is MainViewModel viewModel)
|
|
|
|
|
{
|
|
|
|
|
if (e.Key == Key.Delete)
|
|
|
|
|
{
|
2025-02-21 11:12:10 -03:00
|
|
|
|
_objectManager.EliminarObjetosSeleccionados();
|
2025-02-18 14:08:55 -03:00
|
|
|
|
e.Handled = true;
|
|
|
|
|
}
|
|
|
|
|
else if (e.Key == Key.Escape)
|
|
|
|
|
{
|
|
|
|
|
viewModel.SelectedItemOsList = null;
|
|
|
|
|
_objectManager.ClearSelection();
|
|
|
|
|
_objectManager.RemoveResizeRectangles();
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
}
|
2025-02-19 17:27:33 -03:00
|
|
|
|
else if (_objectManager.SelectedObjects.Any())
|
|
|
|
|
{
|
|
|
|
|
const float moveDistance = 0.1f;
|
|
|
|
|
switch (e.Key)
|
|
|
|
|
{
|
|
|
|
|
case Key.Left:
|
|
|
|
|
MoveSelectedObjects(-moveDistance, 0);
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
break;
|
|
|
|
|
case Key.Right:
|
|
|
|
|
MoveSelectedObjects(moveDistance, 0);
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
break;
|
|
|
|
|
case Key.Up:
|
|
|
|
|
MoveSelectedObjects(0, -moveDistance);
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
break;
|
|
|
|
|
case Key.Down:
|
|
|
|
|
MoveSelectedObjects(0, moveDistance);
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void MoveSelectedObjects(float deltaX, float deltaY)
|
|
|
|
|
{
|
|
|
|
|
// Mover todos los objetos primero
|
|
|
|
|
foreach (var obj in _objectManager.SelectedObjects)
|
|
|
|
|
{
|
|
|
|
|
obj.Left += deltaX;
|
|
|
|
|
obj.Top += deltaY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Forzar una actualización del layout antes de actualizar los visuales
|
|
|
|
|
ImagenEnTrabajoCanvas.UpdateLayout();
|
|
|
|
|
|
|
|
|
|
// Ahora actualizar los visuales de selección
|
|
|
|
|
_objectManager.UpdateSelectionVisuals();
|
|
|
|
|
|
|
|
|
|
if (DataContext is MainViewModel viewModel)
|
|
|
|
|
{
|
|
|
|
|
viewModel.HasUnsavedChanges = true;
|
2025-02-18 14:08:55 -03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Canvas_MouseEnter(object sender, MouseEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (e.OriginalSource == ImagenEnTrabajoCanvas)
|
|
|
|
|
{
|
|
|
|
|
//_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));
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-28 14:47:08 -03:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-18 14:08:55 -03:00
|
|
|
|
private void ViewModel_ImageSelected(object sender, string imagePath)
|
2024-05-04 15:35:06 -03:00
|
|
|
|
{
|
2025-02-18 14:08:55 -03:00
|
|
|
|
LoadImageToCanvas(imagePath);
|
2024-05-04 15:35:06 -03:00
|
|
|
|
}
|
|
|
|
|
|
2025-02-18 14:08:55 -03:00
|
|
|
|
public void DebugWindow()
|
2024-05-03 03:58:21 -03:00
|
|
|
|
{
|
2025-02-18 14:08:55 -03:00
|
|
|
|
var debugWindow = new wDebug
|
2024-05-03 03:58:21 -03:00
|
|
|
|
{
|
2025-02-18 14:08:55 -03:00
|
|
|
|
Data = dataDebug
|
|
|
|
|
};
|
|
|
|
|
debugWindow.Show();
|
2024-05-03 03:58:21 -03:00
|
|
|
|
}
|
2024-05-02 11:06:45 -03:00
|
|
|
|
|
2025-02-18 14:08:55 -03:00
|
|
|
|
private void Canvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
|
2024-06-11 14:43:12 -03:00
|
|
|
|
{
|
2025-02-18 14:08:55 -03:00
|
|
|
|
// Aceptar el evento si viene del canvas o de la imagen de fondo
|
2025-02-19 10:57:15 -03:00
|
|
|
|
if ((e.Source == ImagenEnTrabajoCanvas || e.Source == imagenDeFondo || e.Source is UserControl) &&
|
2025-02-18 17:52:27 -03:00
|
|
|
|
DataContext is MainViewModel viewModel)
|
2025-02-17 11:16:40 -03:00
|
|
|
|
{
|
2025-02-19 17:27:33 -03:00
|
|
|
|
ImagenEnTrabajoCanvas.Focus(); // Asegurar que el canvas tiene el foco
|
2025-02-18 17:52:27 -03:00
|
|
|
|
e.Handled = true;
|
2025-02-18 14:08:55 -03:00
|
|
|
|
ShowContextMenu(e.GetPosition(ImagenEnTrabajoCanvas));
|
2024-06-30 13:17:44 -03:00
|
|
|
|
}
|
2024-06-11 14:43:12 -03:00
|
|
|
|
}
|
2025-02-13 12:52:33 -03:00
|
|
|
|
|
2025-02-18 14:08:55 -03:00
|
|
|
|
private void ShowContextMenu(Point position)
|
2025-02-13 12:52:33 -03:00
|
|
|
|
{
|
2025-02-18 14:08:55 -03:00
|
|
|
|
var contextMenu = new ContextMenu();
|
2025-02-21 18:25:14 -03:00
|
|
|
|
|
2025-02-17 11:16:40 -03:00
|
|
|
|
if (DataContext is MainViewModel viewModel)
|
|
|
|
|
{
|
2025-02-21 18:25:14 -03:00
|
|
|
|
// Multi-selection checkbox
|
|
|
|
|
var multiSelectMenuItem = new MenuItem
|
|
|
|
|
{
|
|
|
|
|
Header = "Modo Multi-Selección",
|
|
|
|
|
IsCheckable = true,
|
|
|
|
|
StaysOpenOnClick = false,
|
|
|
|
|
IsChecked = viewModel.IsMultiSelectionActive
|
|
|
|
|
};
|
|
|
|
|
|
2025-02-19 10:57:15 -03:00
|
|
|
|
multiSelectMenuItem.Click += (s, e) =>
|
2025-02-17 11:16:40 -03:00
|
|
|
|
{
|
2025-02-18 14:08:55 -03:00
|
|
|
|
viewModel.IsMultiSelectionActive = multiSelectMenuItem.IsChecked;
|
2025-02-21 18:25:14 -03:00
|
|
|
|
|
|
|
|
|
// Si se desactiva la multi-selección, desactivar también la selección por rectángulo
|
|
|
|
|
if (!multiSelectMenuItem.IsChecked)
|
|
|
|
|
_objectManager.IsRectangleSelectionActive = false;
|
2025-02-18 14:08:55 -03:00
|
|
|
|
};
|
2025-02-18 17:52:27 -03:00
|
|
|
|
|
2025-02-21 18:25:14 -03:00
|
|
|
|
contextMenu.Items.Add(multiSelectMenuItem);
|
|
|
|
|
|
|
|
|
|
// Mostrar opción de selección por rectángulo solo cuando multi-selección está activa
|
|
|
|
|
if (viewModel.IsMultiSelectionActive)
|
|
|
|
|
{
|
|
|
|
|
var rectangleSelectMenuItem = new MenuItem
|
|
|
|
|
{
|
|
|
|
|
Header = "Selección por Rectángulo",
|
|
|
|
|
IsCheckable = true,
|
|
|
|
|
IsChecked = _objectManager.IsRectangleSelectionActive,
|
|
|
|
|
StaysOpenOnClick = false
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
rectangleSelectMenuItem.Click += (s, e) =>
|
|
|
|
|
{
|
|
|
|
|
_objectManager.IsRectangleSelectionActive = rectangleSelectMenuItem.IsChecked;
|
|
|
|
|
_objectManager.IsDraggingCanvas = false; // Desactivar panning cuando se activa la selección por rectángulo
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
contextMenu.Items.Add(rectangleSelectMenuItem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Si hay objetos seleccionados, agregar opciones de alineación
|
2025-02-18 17:52:27 -03:00
|
|
|
|
if (_objectManager.SelectedObjects.Count > 1)
|
|
|
|
|
{
|
|
|
|
|
contextMenu.Items.Add(new Separator());
|
2025-02-21 18:25:14 -03:00
|
|
|
|
|
|
|
|
|
var alignSubmenu = new MenuItem { Header = "Alinear" };
|
|
|
|
|
|
|
|
|
|
// Alineación horizontal
|
|
|
|
|
var alignLeftItem = new MenuItem { Header = "Izquierda" };
|
|
|
|
|
alignLeftItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.Left);
|
|
|
|
|
alignSubmenu.Items.Add(alignLeftItem);
|
|
|
|
|
|
|
|
|
|
var alignRightItem = new MenuItem { Header = "Derecha" };
|
|
|
|
|
alignRightItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.Right);
|
|
|
|
|
alignSubmenu.Items.Add(alignRightItem);
|
|
|
|
|
|
|
|
|
|
var alignCenterHItem = new MenuItem { Header = "Centro Horizontal" };
|
|
|
|
|
alignCenterHItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.CenterHorizontally);
|
|
|
|
|
alignSubmenu.Items.Add(alignCenterHItem);
|
|
|
|
|
|
|
|
|
|
// Alineación vertical
|
|
|
|
|
alignSubmenu.Items.Add(new Separator());
|
|
|
|
|
|
|
|
|
|
var alignTopItem = new MenuItem { Header = "Arriba" };
|
|
|
|
|
alignTopItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.Top);
|
|
|
|
|
alignSubmenu.Items.Add(alignTopItem);
|
|
|
|
|
|
|
|
|
|
var alignBottomItem = new MenuItem { Header = "Abajo" };
|
|
|
|
|
alignBottomItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.Bottom);
|
|
|
|
|
alignSubmenu.Items.Add(alignBottomItem);
|
|
|
|
|
|
|
|
|
|
var alignCenterVItem = new MenuItem { Header = "Centro Vertical" };
|
|
|
|
|
alignCenterVItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.CenterVertically);
|
|
|
|
|
alignSubmenu.Items.Add(alignCenterVItem);
|
|
|
|
|
|
|
|
|
|
// Distribución
|
|
|
|
|
alignSubmenu.Items.Add(new Separator());
|
|
|
|
|
|
|
|
|
|
var distributeHItem = new MenuItem { Header = "Distribuir Horizontalmente" };
|
|
|
|
|
distributeHItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.DistributeHorizontally);
|
|
|
|
|
alignSubmenu.Items.Add(distributeHItem);
|
|
|
|
|
|
|
|
|
|
var distributeVItem = new MenuItem { Header = "Distribuir Verticalmente" };
|
|
|
|
|
distributeVItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.DistributeVertically);
|
|
|
|
|
alignSubmenu.Items.Add(distributeVItem);
|
|
|
|
|
|
|
|
|
|
// Igualar tamaños
|
|
|
|
|
alignSubmenu.Items.Add(new Separator());
|
|
|
|
|
|
|
|
|
|
var equalWidthItem = new MenuItem { Header = "Igualar Ancho" };
|
|
|
|
|
equalWidthItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.EqualWidth);
|
|
|
|
|
alignSubmenu.Items.Add(equalWidthItem);
|
|
|
|
|
|
|
|
|
|
var equalHeightItem = new MenuItem { Header = "Igualar Alto" };
|
|
|
|
|
equalHeightItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.EqualHeight);
|
|
|
|
|
alignSubmenu.Items.Add(equalHeightItem);
|
|
|
|
|
|
|
|
|
|
var equalAngleItem = new MenuItem { Header = "Igualar Ángulo" };
|
|
|
|
|
equalAngleItem.Click += (s, e) => _objectManager.AlignObjects(AlignmentType.EqualAngle);
|
|
|
|
|
alignSubmenu.Items.Add(equalAngleItem);
|
|
|
|
|
|
|
|
|
|
contextMenu.Items.Add(alignSubmenu);
|
2025-02-18 17:52:27 -03:00
|
|
|
|
}
|
2025-02-17 11:16:40 -03:00
|
|
|
|
|
2025-02-21 18:25:14 -03:00
|
|
|
|
contextMenu.IsOpen = true;
|
2025-02-19 10:57:15 -03:00
|
|
|
|
contextMenu.PlacementTarget = ImagenEnTrabajoCanvas;
|
|
|
|
|
contextMenu.Placement = System.Windows.Controls.Primitives.PlacementMode.MousePoint;
|
|
|
|
|
}
|
2025-02-13 12:52:33 -03:00
|
|
|
|
}
|
2025-02-19 17:27:33 -03:00
|
|
|
|
|
|
|
|
|
private void Canvas_KeyDown(object sender, KeyEventArgs e)
|
|
|
|
|
{
|
2025-02-21 11:12:10 -03:00
|
|
|
|
// Only handle if PanelEdicion doesn't have focus
|
|
|
|
|
if (!PanelEdicion.IsKeyboardFocusWithin)
|
|
|
|
|
{
|
|
|
|
|
HandleKeyDown(e);
|
|
|
|
|
}
|
2025-02-19 17:27:33 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ScrollViewer_PreviewKeyDown(object sender, KeyEventArgs e)
|
|
|
|
|
{
|
2025-02-21 11:12:10 -03:00
|
|
|
|
// Only handle if PanelEdicion doesn't have focus
|
|
|
|
|
if (!PanelEdicion.IsKeyboardFocusWithin &&
|
|
|
|
|
(e.Key == Key.Left || e.Key == Key.Right || e.Key == Key.Up || e.Key == Key.Down))
|
2025-02-19 17:27:33 -03:00
|
|
|
|
{
|
|
|
|
|
HandleKeyDown(e);
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void HandleKeyDown(KeyEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (DataContext is MainViewModel viewModel)
|
|
|
|
|
{
|
|
|
|
|
if (e.Key == Key.Delete)
|
|
|
|
|
{
|
2025-02-21 11:12:10 -03:00
|
|
|
|
_objectManager.EliminarObjetosSeleccionados(); // Cambiar aquí
|
2025-02-19 17:27:33 -03:00
|
|
|
|
e.Handled = true;
|
|
|
|
|
}
|
|
|
|
|
else if (e.Key == Key.Escape)
|
|
|
|
|
{
|
|
|
|
|
viewModel.SelectedItemOsList = null;
|
|
|
|
|
_objectManager.ClearSelection();
|
|
|
|
|
_objectManager.RemoveResizeRectangles();
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
}
|
|
|
|
|
else if (_objectManager.SelectedObjects.Any())
|
|
|
|
|
{
|
|
|
|
|
const float moveDistance = 0.01f;
|
|
|
|
|
switch (e.Key)
|
|
|
|
|
{
|
|
|
|
|
case Key.Left:
|
|
|
|
|
MoveSelectedObjects(-moveDistance, 0);
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
break;
|
|
|
|
|
case Key.Right:
|
|
|
|
|
MoveSelectedObjects(moveDistance, 0);
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
break;
|
|
|
|
|
case Key.Up:
|
|
|
|
|
MoveSelectedObjects(0, -moveDistance);
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
break;
|
|
|
|
|
case Key.Down:
|
|
|
|
|
MoveSelectedObjects(0, moveDistance);
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-02-21 18:25:14 -03:00
|
|
|
|
|
|
|
|
|
public Image ImagenDeFondo => imagenDeFondo;
|
2024-05-01 14:45:20 -03:00
|
|
|
|
}
|
2024-05-11 11:58:55 -03:00
|
|
|
|
|
|
|
|
|
public class FloatValidationRule : ValidationRule
|
|
|
|
|
{
|
|
|
|
|
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(value?.ToString()))
|
|
|
|
|
return new ValidationResult(false, "El campo no puede estar vacío.");
|
|
|
|
|
|
|
|
|
|
if (float.TryParse(value.ToString(), NumberStyles.Float, cultureInfo, out float result))
|
2025-02-18 14:08:55 -03:00
|
|
|
|
return ValidationResult.ValidResult;
|
2024-05-11 11:58:55 -03:00
|
|
|
|
else
|
|
|
|
|
return new ValidationResult(false, "Ingrese un número válido.");
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-05-01 14:45:20 -03:00
|
|
|
|
}
|