Se añadió la capacidad de gestionar datos de imágenes en la clase DatosDeTrabajo, permitiendo la carga de datos desde archivos JSON y la integración con MainViewModel. Se implementó un nuevo método para establecer el ViewModel principal y se mejoró la lógica de renombrado de imágenes en la interfaz de usuario, incluyendo un comando para renombrar imágenes desde el contexto del ListBox. Además, se incorporó un convertidor para mostrar nombres de imágenes personalizados en la interfaz.

This commit is contained in:
Miguel 2025-06-18 13:40:49 +02:00
parent 9b710fcb00
commit 909e438f5b
9 changed files with 301 additions and 8 deletions

View File

@ -0,0 +1,28 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace CtrEditor.Converters
{
public class ImageDisplayNameConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string imageName)
{
// Buscar el MainViewModel en el Application.Current.MainWindow.DataContext
if (Application.Current?.MainWindow?.DataContext is MainViewModel viewModel)
{
return viewModel.GetImageDisplayName(imageName);
}
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@ -11,6 +11,7 @@ namespace CtrEditor
public class DatosDeTrabajo
{
public Dictionary<string, string> Imagenes { get; private set; }
private MainViewModel _mainViewModel;
public DatosDeTrabajo()
{
@ -18,6 +19,11 @@ namespace CtrEditor
CargarImagenes(); // Inicializar la carga de imágenes basada en el directorio persistente
}
public void SetMainViewModel(MainViewModel mainViewModel)
{
_mainViewModel = mainViewModel;
}
public string ObtenerPathImagenConExtension(string key, string extension)
{
if (Imagenes.TryGetValue(key, out string imagePath))
@ -51,6 +57,34 @@ namespace CtrEditor
Imagenes[nombreArchivo] = archivo;
}
}
// Cargar datos de imágenes existentes desde archivos JSON
if (_mainViewModel != null)
{
CargarDatosImagenesExistentes();
}
}
}
private void CargarDatosImagenesExistentes()
{
foreach (var imageName in Imagenes.Keys)
{
string jsonPath = ObtenerPathImagenConExtension(imageName, ".json");
if (!string.IsNullOrEmpty(jsonPath) && File.Exists(jsonPath))
{
try
{
// Solo cargamos los datos de imagen si existe el archivo JSON
// El StateSerializer se encargará de cargar los datos completos
_mainViewModel.GetOrCreateImageData(imageName);
}
catch (Exception ex)
{
// Log del error pero no fallar la carga completa
System.Diagnostics.Debug.WriteLine($"Error al pre-cargar datos de imagen {imageName}: {ex.Message}");
}
}
}
}
}

View File

@ -81,6 +81,12 @@ namespace CtrEditor
[ObservableProperty]
public ObservableCollection<TipoSimulable> listaOsBase;
// Diccionario para almacenar datos expandidos de imágenes
public Dictionary<string, Models.ImageData> _imageDataDictionary = new Dictionary<string, Models.ImageData>();
// Comando para renombrar imagen
public ICommand RenameImageCommand { get; private set; }
public ICommand StartSimulationCommand { get; }
public ICommand StopSimulationCommand { get; }
public ICommand ItemDoubleClickCommand { get; private set; }
@ -383,6 +389,7 @@ namespace CtrEditor
TBMultiPageAnalizeCommand = new RelayCommand(MultiPageAnalizeCommand);
TBMultiPageMatrixCommand = new RelayCommand(MultiPageMatrixCommand);
TBLibraryManagerCommand = new RelayCommand(ShowLibraryManager);
RenameImageCommand = new RelayCommand<string>(RenameImage);
stopwatch_Sim = new Stopwatch();
stopwatch_Sim.Start();
@ -398,6 +405,68 @@ namespace CtrEditor
_stateManager = new StateManager(EstadoPersistente.Instance.directorio, this);
_stateSerializer = new StateSerializer(this, datosDeTrabajo, simulationManager);
// Conectar DatosDeTrabajo con este ViewModel para el escaneo de imágenes
datosDeTrabajo.SetMainViewModel(this);
}
// Métodos para manejo de datos de imágenes
private void RenameImage(string imageName)
{
if (string.IsNullOrEmpty(imageName)) return;
var imageData = GetOrCreateImageData(imageName);
var currentCustomName = imageData.CustomName;
var dialog = new PopUps.RenameImageWindow(imageName, currentCustomName);
dialog.Owner = MainWindow;
if (dialog.ShowDialog() == true)
{
string newName = dialog.NewImageName?.Trim() ?? string.Empty;
if (newName != currentCustomName)
{
if (string.IsNullOrEmpty(newName))
{
// Si el nuevo nombre está vacío, removemos la entrada personalizada
imageData.CustomName = string.Empty;
if (string.IsNullOrEmpty(imageData.CustomName) && imageData.Tags.Count == 0)
{
_imageDataDictionary.Remove(imageName);
}
}
else
{
imageData.CustomName = newName;
}
// Forzar actualización del UI usando CollectionViewSource
var collectionView = System.Windows.Data.CollectionViewSource.GetDefaultView(ListaImagenes);
collectionView?.Refresh();
HasUnsavedChanges = true;
}
}
}
public string GetImageDisplayName(string imageName)
{
if (_imageDataDictionary.TryGetValue(imageName, out var imageData))
{
return imageData.DisplayName;
}
return imageName;
}
public Models.ImageData GetOrCreateImageData(string imageName)
{
if (!_imageDataDictionary.TryGetValue(imageName, out var imageData))
{
imageData = new Models.ImageData(imageName);
_imageDataDictionary[imageName] = imageData;
}
return imageData;
}
private void OnVisFilterChanged(osVisFilterViewModel filter) // Changed signature to accept viewmodel directly
@ -1202,6 +1271,13 @@ namespace CtrEditor
// Nueva propiedad para almacenar los datos locales de objetos globales
public ObservableCollection<DatosLocales>? DatosLocalesObjetos { get; set; }
// Diccionario para almacenar datos expandidos de imágenes
public Dictionary<string, Models.ImageData>? ImageDataDictionary { get; set; }
// Compatibilidad con versiones anteriores - OBSOLETO
[System.Obsolete("Use ImageDataDictionary instead")]
public Dictionary<string, string>? ImageCustomNames { get; set; }
}
public class TipoSimulable

View File

@ -6,6 +6,7 @@
xmlns:local="clr-namespace:CtrEditor"
xmlns:controls="clr-namespace:CtrEditor.Controls"
xmlns:uc="clr-namespace:CtrEditor.ObjetosSim.UserControls"
xmlns:converters="clr-namespace:CtrEditor.Converters"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:ObjetosSim="clr-namespace:CtrEditor.ObjetosSim"
@ -48,6 +49,9 @@
<!-- Converter for PLC Connect/Disconnect image -->
<local:ConnectStateToImageConverter x:Key="ConnectStateToImageConverter" />
<!-- Converter for Image Display Names -->
<converters:ImageDisplayNameConverter x:Key="ImageDisplayNameConverter" />
</Window.Resources>
<Grid>
@ -87,7 +91,18 @@
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox x:Name="ListaImagenes" Grid.Row="0" Margin="5" ItemsSource="{Binding ListaImagenes}"
SelectedItem="{Binding SelectedImage}" />
SelectedItem="{Binding SelectedImage}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource ImageDisplayNameConverter}}"/>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Renombrar" Command="{Binding RenameImageCommand}" CommandParameter="{Binding PlacementTarget.SelectedItem, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
<ListBox x:Name="ListaFunciones" Grid.Row="1" Margin="5" ItemsSource="{Binding ListaOsBase}"
DisplayMemberPath="Nombre" SelectedItem="{Binding SelectedItem}">
<i:Interaction.Triggers>

57
Models/ImageData.cs Normal file
View File

@ -0,0 +1,57 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace CtrEditor.Models
{
public class ImageData : INotifyPropertyChanged
{
private string _customName;
private List<string> _tags;
public string FileName { get; set; }
public string CustomName
{
get => _customName;
set
{
_customName = value;
OnPropertyChanged();
}
}
public List<string> Tags
{
get => _tags ?? new List<string>();
set
{
_tags = value;
OnPropertyChanged();
}
}
public string DisplayName => !string.IsNullOrEmpty(CustomName) ? CustomName : FileName;
public ImageData()
{
FileName = string.Empty;
_customName = string.Empty;
_tags = new List<string>();
}
public ImageData(string fileName, string customName = null, List<string> tags = null)
{
FileName = fileName;
_customName = customName ?? string.Empty;
_tags = tags ?? new List<string>();
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@ -202,7 +202,7 @@ namespace CtrEditor
PurgeDeletedObjects();
// Asegurarse de que el canvas haya actualizado su layout
_canvas.UpdateLayout();
RemoveResizeRectangles();
if (_selectedObjects.Any())
{

View File

@ -0,0 +1,26 @@
<Window x:Class="CtrEditor.PopUps.RenameImageWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Renombrar Imagen" Height="200" Width="400"
WindowStartupLocation="CenterOwner" ResizeMode="NoResize">
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Archivo original:" FontWeight="Bold" Margin="0,0,0,5"/>
<TextBlock Grid.Row="1" x:Name="OriginalNameTextBlock" Margin="0,0,0,10" Foreground="Gray"/>
<TextBlock Grid.Row="2" Text="Nombre personalizado:" FontWeight="Bold" Margin="0,0,0,5"/>
<TextBox Grid.Row="3" x:Name="NewNameTextBox" Margin="0,0,0,15" VerticalAlignment="Top"/>
<StackPanel Grid.Row="4" Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Name="OkButton" Content="Aceptar" Width="80" Margin="0,0,10,0" IsDefault="True" Click="OkButton_Click"/>
<Button x:Name="CancelButton" Content="Cancelar" Width="80" IsCancel="True" Click="CancelButton_Click"/>
</StackPanel>
</Grid>
</Window>

View File

@ -0,0 +1,34 @@
using System.Windows;
namespace CtrEditor.PopUps
{
public partial class RenameImageWindow : Window
{
public string? NewImageName { get; private set; }
public bool IsOkClicked { get; private set; }
public RenameImageWindow(string originalName, string currentCustomName = null)
{
InitializeComponent();
OriginalNameTextBlock.Text = originalName;
NewNameTextBox.Text = currentCustomName ?? string.Empty;
NewNameTextBox.SelectAll();
NewNameTextBox.Focus();
}
private void OkButton_Click(object sender, RoutedEventArgs e)
{
NewImageName = NewNameTextBox.Text?.Trim();
IsOkClicked = true;
DialogResult = true;
Close();
}
private void CancelButton_Click(object sender, RoutedEventArgs e)
{
IsOkClicked = false;
DialogResult = false;
Close();
}
}
}

View File

@ -50,7 +50,7 @@ namespace CtrEditor.Serialization
obj.RestaurarDesdeSnapshotGlobal();
}
// Prepara para serialización
// Prepara para serializaci<EFBFBD>n
obj.SalvarDatosNoSerializables();
// Separa objetos globales y locales
@ -64,13 +64,14 @@ namespace CtrEditor.Serialization
}
}
// Paso 2: Guardar datos de la página actual
// Paso 2: Guardar datos de la p<EFBFBD>gina actual
var dataToSerialize = new SimulationData
{
ObjetosSimulables = objetosSimulables.Count > 0 ? objetosSimulables : null,
UnitConverter = PixelToMeter.Instance.calc,
PLC_ConnectionData = _mainViewModel.PLCViewModel,
DatosLocalesObjetos = datosLocalesObjetos.Count > 0 ? datosLocalesObjetos : null
DatosLocalesObjetos = datosLocalesObjetos.Count > 0 ? datosLocalesObjetos : null,
ImageDataDictionary = _mainViewModel._imageDataDictionary.Count > 0 ? _mainViewModel._imageDataDictionary : null
};
var path = _datosDeTrabajo.ObtenerPathImagenConExtension(selectedImage, ".json");
@ -112,11 +113,11 @@ namespace CtrEditor.Serialization
// Paso 1: Cargar objetos globales
LoadAllPagesState(settings);
// Paso 2: Cargar datos de la página actual
// Paso 2: Cargar datos de la p<EFBFBD>gina actual
var simulationData = LoadCurrentPageState(selectedImage, settings);
// Paso 3: Crear snapshots de los objetos globales
// Nota: No filtramos por Enable_Local_Data aquí ya que se establecerá después
// Nota: No filtramos por Enable_Local_Data aqu<EFBFBD> ya que se establecer<65> despu<70>s
foreach (var obj in _mainViewModel.ObjetosSimulables)
{
if (obj.Enable_On_All_Pages)
@ -134,7 +135,7 @@ namespace CtrEditor.Serialization
obj.Enable_Local_Data = false;
}
// Luego aplicar los datos locales, esto activará Enable_Local_Data automáticamente
// Luego aplicar los datos locales, esto activar<EFBFBD> Enable_Local_Data autom<6F>ticamente
foreach (var datosLocales in simulationData.DatosLocalesObjetos)
{
var objetoGlobal = _mainViewModel.ObjetosSimulables.FirstOrDefault(
@ -187,6 +188,28 @@ namespace CtrEditor.Serialization
_mainViewModel.PLCViewModel = new PLCViewModel();
PixelToMeter.Instance.calc = simulationData.UnitConverter;
// Cargar datos de imágenes
if (simulationData.ImageDataDictionary != null)
{
_mainViewModel._imageDataDictionary.Clear();
foreach (var imageDataEntry in simulationData.ImageDataDictionary)
{
_mainViewModel._imageDataDictionary[imageDataEntry.Key] = imageDataEntry.Value;
}
}
// Compatibilidad con versiones anteriores (ImageCustomNames)
#pragma warning disable CS0618 // Type or member is obsolete
if (simulationData.ImageCustomNames != null)
{
foreach (var customName in simulationData.ImageCustomNames)
{
var imageData = _mainViewModel.GetOrCreateImageData(customName.Key);
imageData.CustomName = customName.Value;
}
}
#pragma warning restore CS0618
}
}
return simulationData;