using System.ComponentModel; using System.Windows.Controls; using System.Windows.Input; using Ookii.Dialogs.Wpf; using System.Collections.ObjectModel; using System.Windows.Threading; using CtrEditor.ObjetosSim; using LibS7Adv; using System.IO; using Newtonsoft.Json; using System.Windows; using CtrEditor.Simulacion; using System.Diagnostics; using System.Reflection; using CommunityToolkit.Mvvm.ComponentModel; using Xceed.Wpf.Toolkit.PropertyGrid; using CtrEditor.ObjetosSim.Extraccion_Datos; using ClosedXML.Excel; using CtrEditor.PopUps; using System.Windows.Data; using CommunityToolkit.Mvvm.Input; using System.Text.RegularExpressions; using System.Collections.Specialized; using CtrEditor.Serialization; // Add this line namespace CtrEditor { public partial class MainViewModel : ObservableObject { private readonly StateSerializer _stateSerializer; public Stopwatch stopwatch_Sim; private double stopwatch_SimPLC_last; private double stopwatch_SimModel_last; private double accumulatedSimTime; private double accumulatedPlcTime; private int simSampleCount; private int plcSampleCount; private float TiempoDesdeStartSimulacion; private bool Debug_SimulacionCreado = false; public SimulationManagerFP simulationManager = new SimulationManagerFP(); private readonly DispatcherTimer _timerSimulacion; private readonly DispatcherTimer _timerPLCUpdate; private readonly DispatcherTimer _timerDisplayUpdate; public Canvas MainCanvas; [ObservableProperty] private bool isConnected; partial void OnPLCViewModelChanged(PLCViewModel value) { if (value != null) { value.PropertyChanged += PLCViewModel_PropertyChanged; IsConnected = value.IsConnected; } } private void PLCViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(PLCViewModel.IsConnected)) { IsConnected = PLCViewModel.IsConnected; } } [ObservableProperty] private DatosDeTrabajo datosDeTrabajo; [ObservableProperty] private ObservableCollection listaImagenes; // Publicación de las claves del diccionario [ObservableProperty] public ObservableCollection listaOsBase; public ICommand StartSimulationCommand { get; } public ICommand StopSimulationCommand { get; } public ICommand ItemDoubleClickCommand { get; private set; } public ICommand TBStartSimulationCommand { get; } public ICommand TBStopSimulationCommand { get; } public ICommand TBSaveCommand { get; } public ICommand TBExtractTagsCommand { get; } public ICommand TBEliminarUserControlCommand { get; } public ICommand TBDuplicarUserControlCommand { get; } public ICommand OpenWorkDirectoryCommand { get; } public ICommand TBEliminarTodosCommand { get; } public ICommand TBEliminarAutoCreatedCommand { get; } public ICommand TBEliminarClonedCommand { get; } public ICommand TBAssingPagesCommand { get; } public ICommand TBMultiPageExtractTagsCommand { get; } public ICommand TBMultiPageAnalizeCommand { get; } public ICommand TBMultiPageMatrixCommand { get; } public ICommand TBTogglePLCConnectionCommand => new RelayCommand(() => { if (IsConnected) DisconnectPLC(); else ConnectPLC(); }); // Evento que se dispara cuando se selecciona una nueva imagen public event EventHandler ImageSelected; public event EventHandler TickSimulacion; // Propiedades private bool habilitarEliminarUserControl; private MainWindow mainWindow; private ObjectManipulationManager _objectManager; // Add this line public MainWindow MainWindow { get => mainWindow; set { mainWindow = value; _objectManager = mainWindow._objectManager; // Initialize _objectManager } } [ObservableProperty] public ICollectionView vistaFiltrada; [ObservableProperty] private float canvasLeft; [ObservableProperty] private float canvasTop; [ObservableProperty] private bool isSimulationRunning; [ObservableProperty] private bool hasUnsavedChanges; [ObservableProperty] private ObservableCollection recentDirectories; private bool inhibitSaveChangesControl; public ICommand OpenRecentDirectoryCommand { get; private set; } partial void OnIsSimulationRunningChanged(bool value) { CommandManager.InvalidateRequerySuggested(); // Notificar que el estado de los comandos ha cambiado } partial void OnHasUnsavedChangesChanged(bool value) { // Notificar el cambio del título indirectamente a través de directorioTrabajo OnPropertyChanged(nameof(directorioTrabajo)); } public string directorioTrabajo { get => EstadoPersistente.Instance.directorio; set { if (value != null) { EstadoPersistente.Instance.directorio = value; // Actualizar el estado persistente EstadoPersistente.Instance.GuardarEstado(); // Guardar el estado actualizado DatosDeTrabajo.CargarImagenes(); ListaImagenes = new ObservableCollection(DatosDeTrabajo.Imagenes.Keys); // Actualizar claves // Si no hay imágenes en el directorio, copiar base.png desde los recursos if (!ListaImagenes.Any()) { CopiarImagenBase(value); // Recargar las imágenes después de copiar la imagen base DatosDeTrabajo.CargarImagenes(); ListaImagenes = new ObservableCollection(DatosDeTrabajo.Imagenes.Keys); // Actualizar claves nuevamente } SelectedImage = null; var x = ListaImagenes.FirstOrDefault(o => o == EstadoPersistente.Instance.imagen, null); if (EstadoPersistente.Instance.imagen != null && EstadoPersistente.Instance.imagen.Length > 0 && x != null) SelectedImage = EstadoPersistente.Instance.imagen; else if (ListaImagenes.FirstOrDefault() != null) SelectedImage = ListaImagenes.FirstOrDefault(); OnPropertyChanged(nameof(directorioTrabajo)); // Notificar el cambio de propiedad OnPropertyChanged(nameof(ListaImagenes)); // Notificar que la lista de imágenes ha cambiado AddToRecentDirectories(value); } } } // Función para copiar la imagen base desde los recursos private void CopiarImagenBase(string directorio) { try { // Obtener el path del archivo base.png desde los recursos (dentro del ensamblado) var assembly = Assembly.GetExecutingAssembly(); var resourcePath = "CtrEditor.Images.base.png"; // Ajusta el namespace y el path según tu proyecto using (Stream resourceStream = assembly.GetManifestResourceStream(resourcePath)) { if (resourceStream != null) { string destino = Path.Combine(directorio, "base.png"); using (FileStream fileStream = new FileStream(destino, FileMode.Create, FileAccess.Write)) { resourceStream.CopyTo(fileStream); } } } } catch (Exception ex) { MessageBox.Show($"Error al copiar la imagen base: {ex.Message}"); } } [RelayCommand] public void DebugWindow() { MainWindow.DebugWindow(); } [ObservableProperty] private PLCViewModel pLCViewModel; [ObservableProperty] private string selectedImage; partial void OnSelectedImageChanging(string? oldValue, string newValue) { if (HasUnsavedChanges && !inhibitSaveChangesControl) { var result = MessageBox.Show("There are unsaved changes. Do you want to save them?", "Save Changes", MessageBoxButton.YesNo); if (result == MessageBoxResult.Yes) { SaveStateObjetosSimulables(); } } } partial void OnSelectedImageChanged(string value) { if (value != null) { StopSimulation(); ImageSelected?.Invoke(this, datosDeTrabajo.Imagenes[value]); LoadStateObjetosSimulables(); EstadoPersistente.Instance.imagen = value; EstadoPersistente.Instance.GuardarEstado(); HasUnsavedChanges = false; } } [ObservableProperty] private osBase selectedItemOsList; partial void OnSelectedItemOsListChanged(osBase value) { if (value != null) habilitarEliminarUserControl = true; else habilitarEliminarUserControl = false; } [ObservableProperty] public ObjetosSimulablesFilterTypes osListFilter; [ObservableProperty] private TipoSimulable selectedItem; public ICollectionView ObjetosSimulablesFiltered { get; } public ICollectionView ObjetosSimulablesAllPages { get; } [ObservableProperty] public ObservableCollection objetosSimulables; [ObservableProperty] private bool isMultiSelectionActive; partial void OnIsMultiSelectionActiveChanged(bool value) { _objectManager?.OnMultiSelectionModeChanged(value); } partial void OnObjetosSimulablesChanged(ObservableCollection value) { VistaFiltrada = CollectionViewSource.GetDefaultView(ObjetosSimulables); VistaFiltrada.Filter = FiltrarObjetos; ObjetosSimulables.CollectionChanged += (s, e) => { VistaFiltrada.Refresh(); if (!inhibitSaveChangesControl && e.Action != NotifyCollectionChangedAction.Move) HasUnsavedChanges = true; }; } // // Constructor // private StateManager _stateManager; public MainViewModel() { OpenWorkDirectoryCommand = new RelayCommand(OpenWorkDirectory); datosDeTrabajo = new DatosDeTrabajo(); OsListFilter = new ObjetosSimulablesFilterTypes(); OsListFilter.All = true; osListFilter.PropertyChanged += OsListFilter_PropertyChanged; ObjetosSimulables = new ObservableCollection(); ListaOsBase = new ObservableCollection(); // Inicializa el PLCViewModel PLCViewModel = new PLCViewModel(); _timerPLCUpdate = new DispatcherTimer(); _timerPLCUpdate.Interval = TimeSpan.FromMilliseconds(10); // Restaurado a 10ms _timerPLCUpdate.Tick += OnRefreshEvent; InitializeTipoSimulableList(); ItemDoubleClickCommand = new ParameterizedRelayCommand(ExecuteDoubleClick); _timerSimulacion = new DispatcherTimer(); _timerSimulacion.Interval = TimeSpan.FromMilliseconds(10); // Restaurado a 10ms _timerSimulacion.Tick += OnTickSimulacion; // Nuevo timer para actualización de display _timerDisplayUpdate = new DispatcherTimer(); _timerDisplayUpdate.Interval = TimeSpan.FromMilliseconds(250); _timerDisplayUpdate.Tick += OnDisplayUpdate; _timerDisplayUpdate.Start(); StartSimulationCommand = new RelayCommand(StartSimulation); StopSimulationCommand = new RelayCommand(StopSimulation); TBStartSimulationCommand = new RelayCommand(StartSimulation, () => !IsSimulationRunning); TBStopSimulationCommand = new RelayCommand(StopSimulation, () => IsSimulationRunning); TBSaveCommand = new RelayCommand(Save); TBEliminarUserControlCommand = new RelayCommand(EliminarUserControl, () => habilitarEliminarUserControl); TBDuplicarUserControlCommand = new RelayCommand(DuplicarUserControl, () => habilitarEliminarUserControl); TBEliminarTodosCommand = new RelayCommand(EliminarTodosCommand); TBEliminarAutoCreatedCommand = new RelayCommand(EliminarAutoCreatedCommand); TBEliminarClonedCommand = new RelayCommand(EliminarClonedCommand); TBAssingPagesCommand = new RelayCommand(AssingPagesCommand); TBMultiPageAnalizeCommand = new RelayCommand(MultiPageAnalizeCommand); TBMultiPageMatrixCommand = new RelayCommand(MultiPageMatrixCommand); stopwatch_Sim = new Stopwatch(); stopwatch_Sim.Start(); ObjetosSimulables.CollectionChanged += (s, e) => { if (e.Action != NotifyCollectionChangedAction.Move) HasUnsavedChanges = true; }; recentDirectories = new ObservableCollection(EstadoPersistente.Instance.RecentDirectories); OpenRecentDirectoryCommand = new RelayCommand(OpenRecentDirectory); _stateManager = new StateManager(EstadoPersistente.Instance.directorio, this); _stateSerializer = new StateSerializer(this, datosDeTrabajo, simulationManager); } private void OsListFilter_PropertyChanged(object? sender, PropertyChangedEventArgs e) { VistaFiltrada.Refresh(); } private bool FiltrarObjetos(object item) { var objeto = item as osBase; return osListFilter.All || (objeto.Cloned == osListFilter.Cloned) && (objeto.Enable_On_All_Pages == osListFilter.Enable_On_All_Pages) && (objeto.Show_On_This_Page == osListFilter.Show_On_This_Page); } public void LoadInitialData() { // Suponiendo que "SelectedImage" es una propiedad que al establecerse dispara "ImageSelected" directorioTrabajo = EstadoPersistente.Instance.directorio; } // Crear un nuevo Objeto private void ExecuteDoubleClick(object parameter) { if (parameter is TipoSimulable tipoSimulable) { CrearObjetoSimulableEnCentroCanvas(tipoSimulable.Tipo); } } public void CrearObjetoSimulableEnCentroCanvas(Type tipoSimulable) { var CentroCanvas = MainWindow.ObtenerCentroCanvasMeters(); CrearObjetoSimulable(tipoSimulable, CentroCanvas.X, CentroCanvas.Y); } public osBase CrearObjetoSimulable(Type tipoSimulable, float Left, float Top) { // Crear una nueva instancia del osBase correspondiente osBase? NuevoOsBase = UserControlFactory.GetInstanceForType(tipoSimulable); NuevoOsBase.Left = Left; NuevoOsBase.Top = Top; if (NuevoOsBase != null) { if (CrearUserControlDesdeObjetoSimulable(NuevoOsBase)) { // Añadir el nuevo osBase a la colección de objetos simulables ObjetosSimulables.Add(NuevoOsBase); HasUnsavedChanges = true; } } return NuevoOsBase; } // Crear UserControl desde osBase : Nuevo o desde Deserealizacion public bool CrearUserControlDesdeObjetoSimulable(osBase osObjeto) { Type tipoObjeto = osObjeto.GetType(); // Obtén el UserControl correspondiente para el tipo de objeto UserControl? userControl = UserControlFactory.GetControlForType(tipoObjeto); if (userControl != null) { // Asignar los datos al UserControl UserControlFactory.AssignDatos(userControl, osObjeto, simulationManager); osObjeto._mainViewModel = this; if (osObjeto.Id == null) // Para los objetos salvados antes de usar UniqueID osObjeto.Id = new UniqueId().ObtenerNuevaID(); MainWindow.AgregarRegistrarUserControlCanvas(userControl); return true; } return false; } public void RemoverObjetoSimulable(osBase osObjeto) { if (osObjeto != null && ObjetosSimulables.Contains(osObjeto)) { ObjetosSimulables.Remove(osObjeto); if (osObjeto.VisualRepresentation != null) MainWindow.EliminarUserControlDelCanvas(osObjeto.VisualRepresentation); HasUnsavedChanges = true; } } private void DuplicarUserControl() { if (SelectedItemOsList is osBase objDuplicar) DuplicarObjeto(objDuplicar, 0.5f, 0.5f); } public osBase DuplicarObjeto(osBase objDuplicar, float OffsetX, float OffsetY) { StopSimulation(); DisconnectPLC(); osBase? NuevoObjetoDuplicado = null; objDuplicar.SalvarDatosNoSerializables(); var settings = new JsonSerializerSettings { Formatting = Formatting.Indented, NullValueHandling = NullValueHandling.Ignore, TypeNameHandling = TypeNameHandling.All }; try { // Serializar var serializedData = JsonConvert.SerializeObject(objDuplicar, settings); // Duplicar NuevoObjetoDuplicado = JsonConvert.DeserializeObject(serializedData, settings); if (NuevoObjetoDuplicado != null) { NuevoObjetoDuplicado.Id.ObtenerNuevaID(); string nombre = Regex.IsMatch(NuevoObjetoDuplicado.Nombre, @"_\d+$") ? Regex.Replace(NuevoObjetoDuplicado.Nombre, @"_\d+$", $"_{NuevoObjetoDuplicado.Id.Value}") : NuevoObjetoDuplicado.Nombre + "_" + NuevoObjetoDuplicado.Id.Value; NuevoObjetoDuplicado.Nombre = nombre; NuevoObjetoDuplicado.Left += OffsetX; NuevoObjetoDuplicado.Top += OffsetY; ObjetosSimulables.Add(NuevoObjetoDuplicado); CrearUserControlDesdeObjetoSimulable(NuevoObjetoDuplicado); HasUnsavedChanges = true; } } catch { // Log error or handle it accordingly } finally { objDuplicar.RestaurarDatosNoSerializables(); } return NuevoObjetoDuplicado; } private void EliminarUserControl() { _objectManager.EliminarObjetosSeleccionados(); } private void EliminarTodosCommand() { var objetosSimulablesCopy = new List(ObjetosSimulables); foreach (var obj in objetosSimulablesCopy) RemoverObjetoSimulable(obj); } private void EliminarAutoCreatedCommand() { var osAutoCreated_List = ObjetosSimulables .Where(o => o.Show_On_This_Page && o.AutoCreated) .ToList(); foreach (var obj in osAutoCreated_List) RemoverObjetoSimulable(obj); } private void EliminarClonedCommand() { var osCloned_List = ObjetosSimulables .Where(o => o.Show_On_This_Page && o.Cloned) .ToList(); foreach (var obj in osCloned_List) RemoverObjetoSimulable(obj); } private void AssingPagesCommand() { var assignImagesWindow = new AssignImagesWindow(); var assignImagesViewModel = new AssignImagesViewModel(); assignImagesViewModel.Initialize(this, assignImagesWindow); assignImagesWindow.DataContext = assignImagesViewModel; assignImagesWindow.ShowDialog(); if (assignImagesWindow.DataContext is AssignImagesViewModel dialog && dialog.CloseOK) SaveStateObjetosSimulables(); } public async Task WaitForUIUpdateAsync() { await Task.Yield(); Application.Current.Dispatcher.Invoke(() => { }, DispatcherPriority.ApplicationIdle); } private async void MultiPageAnalizeCommand() { var ImagenesSeleccionadas = new ObservableCollection { SelectedImage }; StopSimulation(); var selectPagesWindow = new SelectPages(); var selectPagesViewModel = new SelectPagesViewModel(); selectPagesViewModel.Initialize(this, selectPagesWindow, ref ImagenesSeleccionadas); selectPagesWindow.DataContext = selectPagesViewModel; selectPagesWindow.ShowDialog(); bool originalHasUnsavedChanges = HasUnsavedChanges; HasUnsavedChanges = false; try { if (selectPagesWindow.DataContext is SelectPagesViewModel dialog && dialog.CloseOK) { SaveStateObjetosSimulables(); // Guarda el estado antes de cambiar la imagen foreach (var page in ImagenesSeleccionadas) { SelectedImage = page; await WaitForUIUpdateAsync(); // Espera a que la UI se actualice AnalizePageCommand(); await WaitForUIUpdateAsync(); // Espera a que la UI se actualice SaveStateObjetosSimulables(); // Guarda el estado antes de cambiar la imagen } } } finally { HasUnsavedChanges = originalHasUnsavedChanges; } } private void AnalizePageCommand() { foreach (var obj in ObjetosSimulables) if (obj is osBuscarCoincidencias objBC) if (objBC.Show_On_This_Page) objBC.BuscarCoincidencias(); } private void InitializeTipoSimulableList() { var baseType = typeof(osBase); var types = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(assembly => assembly.GetTypes()) .Where(type => type.IsSubclassOf(baseType) && !type.IsAbstract && typeof(IosBase).IsAssignableFrom(type)); foreach (var type in types) { var methodInfo = type.GetMethod("NombreClase", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy); string nombre = methodInfo != null ? methodInfo.Invoke(null, null)?.ToString() : "Nombre no encontrado"; ListaOsBase.Add(new TipoSimulable { Nombre = nombre, Tipo = type }); } } private void StartSimulation() { IsSimulationRunning = true; // Ocultar rectángulos de selección antes de iniciar la simulación _objectManager.UpdateSelectionVisuals(); foreach (var objetoSimulable in ObjetosSimulables) objetoSimulable.UpdateGeometryStart(); simulationManager.Debug_DrawInitialBodies(); TiempoDesdeStartSimulacion = 0; Debug_SimulacionCreado = true; _timerSimulacion.Start(); simulationManager.Start(); } public void StopSimulation() { IsSimulationRunning = false; foreach (var objetoSimulable in ObjetosSimulables) objetoSimulable.SimulationStop(); if (Debug_SimulacionCreado) { simulationManager.Debug_ClearSimulationShapes(); Debug_SimulacionCreado = false; } _timerSimulacion.Stop(); // Restaurar los rectángulos de selección si hay objetos seleccionados _objectManager.UpdateSelectionVisuals(); } private void OnTickSimulacion(object sender, EventArgs e) { var stopwatch = Stopwatch.StartNew(); // Start measuring time // Detener el cronómetro y obtener el tiempo transcurrido en milisegundos var elapsedMilliseconds = stopwatch_Sim.Elapsed.TotalMilliseconds - stopwatch_SimModel_last; stopwatch_SimModel_last = stopwatch_Sim.Elapsed.TotalMilliseconds; // Acumular tiempo para el promedio accumulatedSimTime += elapsedMilliseconds; simSampleCount++; // Eliminar el diseño de Debug luego de 2 segundos if (TiempoDesdeStartSimulacion > 12000) simulationManager.Debug_ClearSimulationShapes(); else TiempoDesdeStartSimulacion += (float)elapsedMilliseconds; foreach (var objetoSimulable in ObjetosSimulables) objetoSimulable.UpdateGeometryStep(); simulationManager.Step(); var objetosSimulablesCopy = new List(ObjetosSimulables); foreach (var objetoSimulable in objetosSimulablesCopy) { if (!objetoSimulable.RemoverDesdeSimulacion) objetoSimulable.UpdateControl((int)elapsedMilliseconds); else RemoverObjetoSimulable(objetoSimulable); } stopwatch.Stop(); // Stop measuring time //Debug.WriteLine($"OnTickSimulacion execution time: {stopwatch.TotalMilliseconds} ms"); } private void ConnectPLC() { _timerPLCUpdate.Start(); PLCViewModel.Connect(); foreach (var objetoSimulable in ObjetosSimulables) objetoSimulable.SetPLC(PLCViewModel); } public void DisconnectPLC() { PLCViewModel.Disconnect(); _timerPLCUpdate.Stop(); foreach (var objetoSimulable in ObjetosSimulables) objetoSimulable.SetPLC(null); } private List objetosSimulablesLlamados = new List(); private void OnRefreshEvent(object sender, EventArgs e) { var stopwatch = Stopwatch.StartNew(); // Start measuring time if (PLCViewModel.IsConnected) { // Detener el cronómetro y obtener el tiempo transcurrido en milisegundos var elapsedMilliseconds = stopwatch_Sim.Elapsed.TotalMilliseconds - stopwatch_SimPLC_last; stopwatch_SimPLC_last = stopwatch_Sim.Elapsed.TotalMilliseconds; // Acumular tiempo para el promedio accumulatedPlcTime += elapsedMilliseconds; plcSampleCount++; // Reiniciar el cronómetro para la próxima medición var remainingObjetosSimulables = ObjetosSimulables.Except(objetosSimulablesLlamados).ToList(); foreach (var objetoSimulable in remainingObjetosSimulables) { var objStopwatch = Stopwatch.StartNew(); objetoSimulable.UpdatePLC(PLCViewModel, (int)elapsedMilliseconds); objStopwatch.Stop(); objetosSimulablesLlamados.Add(objetoSimulable); if (stopwatch.Elapsed.TotalMilliseconds >= 10) break; } if (remainingObjetosSimulables.Count == 0) { objetosSimulablesLlamados.Clear(); } } stopwatch.Stop(); // Stop measuring time // Debug.WriteLine($"OnRefreshEvent: {stopwatch.Elapsed.TotalMilliseconds} ms"); } private void OpenWorkDirectory() { var dialog = new VistaFolderBrowserDialog(); if (dialog.ShowDialog() == true) // Mostrar el diálogo y comprobar si el resultado es positivo { directorioTrabajo = dialog.SelectedPath; // Actualizar la propiedad que también actualiza el estado persistente } } private void OpenRecentDirectory(string path) { if (Directory.Exists(path)) { directorioTrabajo = path; } else { MessageBox.Show($"Directory not found: {path}", "Error", MessageBoxButton.OK, MessageBoxImage.Error); RecentDirectories.Remove(path); UpdateRecentDirectories(); } } private void UpdateRecentDirectories() { EstadoPersistente.Instance.RecentDirectories = RecentDirectories.ToList(); EstadoPersistente.Instance.GuardarEstado(); } private void AddToRecentDirectories(string path) { // Remove the path if it already exists RecentDirectories.Remove(path); // Add the new path at the beginning RecentDirectories.Insert(0, path); // Keep only the last 10 entries while (RecentDirectories.Count > 10) { RecentDirectories.RemoveAt(RecentDirectories.Count - 1); } UpdateRecentDirectories(); } // // Lista de osBase // public void Save() { SaveStateObjetosSimulables(); } public void SaveStateObjetosSimulables() { if (SelectedImage != null) { _stateSerializer.SaveState(SelectedImage); HasUnsavedChanges = false; } } public void LoadStateObjetosSimulables() { if (SelectedImage != null) { _stateSerializer.LoadState(SelectedImage); } } // Se cargan los datos de cada UserControl en el StackPanel public void CargarPropiedadesosDatos(osBase selectedObject, Controls.PanelEdicionControl PanelEdicion, ResourceDictionary Resources) { PanelEdicion.CargarPropiedades(selectedObject); } private RelayCommand saveCommand; public ICommand SaveCommand => saveCommand ??= new RelayCommand(Save); private void Save(object commandParameter) { } private RelayCommand exitCommand; public ICommand ExitCommand => exitCommand ??= new RelayCommand(Exit); private void Exit() { if (HasUnsavedChanges) { var result = MessageBox.Show("There are unsaved changes. Do you want to save them?", "Save Changes", MessageBoxButton.YesNoCancel); if (result == MessageBoxResult.Cancel) return; else if (result == MessageBoxResult.Yes) SaveStateObjetosSimulables(); } Application.Current.Shutdown(); } private async void MultiPageMatrixCommand() { if (HasUnsavedChanges) { var result = MessageBox.Show("There are unsaved changes. Do you want to save them?", "Save Changes", MessageBoxButton.YesNoCancel); if (result == MessageBoxResult.Cancel) return; else if (result == MessageBoxResult.Yes) SaveStateObjetosSimulables(); } var ImagenesSeleccionadas = new ObservableCollection { SelectedImage }; StopSimulation(); var selectPagesWindow = new SelectPages(); var selectPagesViewModel = new SelectPagesViewModel(); selectPagesViewModel.Initialize(this, selectPagesWindow, ref ImagenesSeleccionadas); selectPagesWindow.DataContext = selectPagesViewModel; selectPagesWindow.ShowDialog(); inhibitSaveChangesControl = true; try { if (selectPagesWindow.DataContext is SelectPagesViewModel dialog && dialog.CloseOK) { var matrixPreviewWindow = new MatrixPreviewWindow(); var matrixPreviewViewModel = new MatrixPreviewViewModel(); matrixPreviewViewModel.Initialize(this, matrixPreviewWindow, ImagenesSeleccionadas); matrixPreviewWindow.DataContext = matrixPreviewViewModel; matrixPreviewWindow.ShowDialog(); } } finally { inhibitSaveChangesControl = false; } } [ObservableProperty] private double simulationSpeed; [ObservableProperty] private double plcUpdateSpeed; private void OnDisplayUpdate(object? sender, EventArgs e) { if (simSampleCount > 0) { SimulationSpeed = accumulatedSimTime / simSampleCount; accumulatedSimTime = 0; simSampleCount = 0; } if (plcSampleCount > 0) { PlcUpdateSpeed = accumulatedPlcTime / plcSampleCount; accumulatedPlcTime = 0; plcSampleCount = 0; } } // Reemplazar la propiedad _multiPropertyEditorWindow por un diccionario private Dictionary _propertyEditorWindows = new(); public void ShowMultiPropertyEditor(IEnumerable selectedObjects) { var objectsList = selectedObjects.ToList(); // Crear una clave única basada en los IDs de los objetos seleccionados string key = string.Join("_", objectsList.Select(o => o.Id.Value).OrderBy(id => id)); // Verificar si ya existe una ventana para esta selección if (_propertyEditorWindows.TryGetValue(key, out var existingWindow) && existingWindow.IsVisible) { existingWindow.Activate(); return; } // Crear nueva ventana var window = new Windows.MultiPropertyEditorWindow(objectsList, MainWindow); window.Closed += (s, e) => _propertyEditorWindows.Remove(key); _propertyEditorWindows[key] = window; window.Show(); HasUnsavedChanges = true; _objectManager.UpdateSelectionVisuals(); } } public class SimulationData { public ObservableCollection? ObjetosSimulables { get; set; } public UnitConverter? UnitConverter { get; set; } public PLCViewModel? PLC_ConnectionData { get; set; } } public class TipoSimulable { public string? Nombre { get; set; } public Type? Tipo { get; set; } } public class TickSimulacionEventArgs : EventArgs { // Aquí puedes agregar propiedades o campos para pasar información adicional // en el evento TickSimulacion } }