diff --git a/Converters/ConnectionConverters.cs b/Converters/ConnectionConverters.cs
new file mode 100644
index 0000000..e69de29
diff --git a/CtrEditor.csproj b/CtrEditor.csproj
index 787b3d7..81cb2ab 100644
--- a/CtrEditor.csproj
+++ b/CtrEditor.csproj
@@ -44,6 +44,8 @@
+
+
@@ -121,6 +123,8 @@
+
+
diff --git a/DataStates/StateManager.cs b/DataStates/StateManager.cs
new file mode 100644
index 0000000..7387186
--- /dev/null
+++ b/DataStates/StateManager.cs
@@ -0,0 +1,383 @@
+using System;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+using System.Linq;
+using System.Windows.Controls;
+using System.Windows;
+using CtrEditor.ObjetosSim;
+using LibS7Adv;
+
+namespace CtrEditor
+{
+ ///
+ /// Metadata de una página, incluyendo información sobre la imagen y configuración
+ ///
+ public class PageMetadata
+ {
+ public string PageId { get; set; }
+ public string PageName { get; set; }
+ public string ImagePath { get; set; }
+ public DateTime LastModified { get; set; }
+ public Dictionary CustomMetadata { get; set; } = new();
+ }
+
+ ///
+ /// Estado de un objeto dentro de una página específica
+ ///
+ public class PageObjectState
+ {
+ public string GlobalObjectId { get; set; }
+ public bool IsVisible { get; set; } = true;
+ public float Left { get; set; }
+ public float Top { get; set; }
+ public float Rotation { get; set; }
+ public Dictionary CustomProperties { get; set; } = new();
+ }
+
+ ///
+ /// Estado de una página específica
+ ///
+ public class PageState
+ {
+ public string PageId { get; set; }
+ public string PageName { get; set; }
+ public List LocalObjects { get; set; } = new();
+ public Dictionary GlobalObjectStates { get; set; } = new();
+ public Dictionary PageMetadata { get; set; } = new();
+ }
+
+ ///
+ /// Estado global de la aplicación
+ ///
+ public class GlobalState
+ {
+ public List SharedObjects { get; set; } = new();
+ public Dictionary PagesMetadata { get; set; } = new();
+ public PLCViewModel PLCConfiguration { get; set; }
+ public UnitConverter UnitConverter { get; set; }
+ public int LastUsedId { get; set; }
+ }
+
+ ///
+ /// Gestor principal de estados de la aplicación
+ ///
+ public class StateManager : IDisposable
+ {
+ private readonly string _basePath;
+ private GlobalState _globalState;
+ private Dictionary _loadedPages;
+ private string _currentPageId;
+ private readonly object _lockObject = new();
+ private bool _hasUnsavedChanges;
+ private MainViewModel _mainViewModel;
+
+ public event EventHandler PageStateChanged;
+ public event EventHandler GlobalStateChanged;
+
+ public StateManager(string basePath, MainViewModel mainViewModel)
+ {
+ _basePath = basePath;
+ _mainViewModel = mainViewModel;
+ _loadedPages = new Dictionary();
+ Directory.CreateDirectory(basePath);
+ }
+
+ public async Task InitializeAsync()
+ {
+ await LoadGlobalStateAsync();
+ if (_mainViewModel.SelectedImage != null)
+ {
+ await LoadPageStateAsync(_mainViewModel.SelectedImage);
+ }
+ }
+
+ private JsonSerializerSettings GetSerializerSettings()
+ {
+ return new JsonSerializerSettings
+ {
+ TypeNameHandling = TypeNameHandling.Auto,
+ ObjectCreationHandling = ObjectCreationHandling.Replace,
+ Formatting = Formatting.Indented,
+ NullValueHandling = NullValueHandling.Ignore
+ };
+ }
+
+ public async Task LoadGlobalStateAsync()
+ {
+ var path = Path.Combine(_basePath, "global.json");
+ if (File.Exists(path))
+ {
+ try
+ {
+ var json = await File.ReadAllTextAsync(path);
+ _globalState = JsonConvert.DeserializeObject(json, GetSerializerSettings());
+
+ // Restaurar configuración global
+ _mainViewModel.PLCViewModel = _globalState.PLCConfiguration ?? new PLCViewModel();
+ if (_globalState.UnitConverter != null)
+ PixelToMeter.Instance.calc = _globalState.UnitConverter;
+ else
+ PixelToMeter.Instance.calc = new UnitConverter(1.0f); // Valor por defecto
+
+ // Restaurar objetos globales
+ foreach (var obj in _globalState.SharedObjects)
+ {
+ await RestoreObjectAsync(obj);
+ }
+ }
+ catch (Exception ex)
+ {
+ // Log error y crear nuevo estado global
+ _globalState = new GlobalState();
+ }
+ }
+ else
+ {
+ _globalState = new GlobalState();
+ }
+ }
+
+ public async Task SaveGlobalStateAsync()
+ {
+ var path = Path.Combine(_basePath, "global.json");
+ var backupPath = Path.ChangeExtension(path, ".bak");
+
+ // Actualizar estado global
+ _globalState.PLCConfiguration = _mainViewModel.PLCViewModel;
+ _globalState.UnitConverter = PixelToMeter.Instance.calc;
+
+ // Crear backup
+ if (File.Exists(path))
+ {
+ File.Copy(path, backupPath, true);
+ }
+
+ try
+ {
+ // Preparar objetos para serialización
+ foreach (var obj in _globalState.SharedObjects)
+ {
+ obj.SalvarDatosNoSerializables();
+ }
+
+ var json = JsonConvert.SerializeObject(_globalState, GetSerializerSettings());
+ await File.WriteAllTextAsync(path, json);
+ _hasUnsavedChanges = false;
+ GlobalStateChanged?.Invoke(this, EventArgs.Empty);
+ }
+ finally
+ {
+ // Restaurar estado de objetos
+ foreach (var obj in _globalState.SharedObjects)
+ {
+ obj.RestaurarDatosNoSerializables();
+ }
+ }
+ }
+
+ public async Task LoadPageStateAsync(string pageId)
+ {
+ // Retornar página cacheada si existe
+ if (_loadedPages.TryGetValue(pageId, out var cachedState))
+ {
+ return cachedState;
+ }
+
+ var path = Path.Combine(_basePath, $"page_{pageId}.json");
+ try
+ {
+ PageState pageState;
+ if (File.Exists(path))
+ {
+ var json = await File.ReadAllTextAsync(path);
+ pageState = JsonConvert.DeserializeObject(json, GetSerializerSettings());
+ }
+ else
+ {
+ pageState = new PageState
+ {
+ PageId = pageId,
+ PageName = _globalState.PagesMetadata.GetValueOrDefault(pageId)?.PageName ?? pageId
+ };
+ }
+
+ _loadedPages[pageId] = pageState;
+
+ // Restaurar objetos locales
+ foreach (var obj in pageState.LocalObjects)
+ {
+ await RestoreObjectAsync(obj);
+ }
+
+ // Aplicar estados de objetos globales
+ ApplyGlobalObjectStates(pageState);
+
+ return pageState;
+ }
+ catch (Exception ex)
+ {
+ // Log error
+ throw;
+ }
+ }
+
+ private void ApplyGlobalObjectStates(PageState pageState)
+ {
+ foreach (var kvp in pageState.GlobalObjectStates)
+ {
+ var globalObj = _globalState.SharedObjects.FirstOrDefault(o => o.Id.Value.ToString() == kvp.Key);
+ if (globalObj != null)
+ {
+ var state = kvp.Value;
+ globalObj.Left = state.Left;
+ globalObj.Top = state.Top;
+ globalObj.Angulo = state.Rotation;
+
+ // Actualizar Show_On_This_Page que ahora maneja internamente showOnThisPagesList
+ if (state.IsVisible && !globalObj.Show_On_This_Page)
+ globalObj.Show_On_This_Page = true;
+ else if (!state.IsVisible && globalObj.Show_On_This_Page)
+ globalObj.Show_On_This_Page = false;
+
+ // Aplicar propiedades personalizadas
+ foreach (var prop in state.CustomProperties)
+ {
+ var property = globalObj.GetType().GetProperty(prop.Key);
+ if (property != null && property.CanWrite)
+ {
+ property.SetValue(globalObj, prop.Value);
+ }
+ }
+ }
+ }
+ }
+
+ private async Task RestoreObjectAsync(osBase obj)
+ {
+ if (obj != null)
+ {
+ obj.CheckData();
+ await Task.Run(() =>
+ {
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ CrearUserControlDesdeObjetoSimulable(obj);
+ });
+ });
+ }
+ }
+
+ private bool CrearUserControlDesdeObjetoSimulable(osBase osObjeto)
+ {
+ Type tipoObjeto = osObjeto.GetType();
+ UserControl userControl = UserControlFactory.GetControlForType(tipoObjeto);
+
+ if (userControl != null)
+ {
+ UserControlFactory.AssignDatos(userControl, osObjeto, _mainViewModel.simulationManager);
+ osObjeto._mainViewModel = _mainViewModel;
+
+ if (osObjeto.Id == null)
+ {
+ osObjeto.Id = new UniqueId().ObtenerNuevaID();
+ }
+
+ _mainViewModel.MainWindow.AgregarRegistrarUserControlCanvas(userControl);
+ return true;
+ }
+ return false;
+ }
+
+ public async Task SavePageStateAsync(string pageId)
+ {
+ if (!_loadedPages.TryGetValue(pageId, out var pageState))
+ return;
+
+ var path = Path.Combine(_basePath, $"page_{pageId}.json");
+ var backupPath = Path.ChangeExtension(path, ".bak");
+
+ // Crear backup
+ if (File.Exists(path))
+ {
+ File.Copy(path, backupPath, true);
+ }
+
+ // Actualizar estado de objetos globales
+ pageState.GlobalObjectStates.Clear();
+ foreach (var obj in _mainViewModel.ObjetosSimulables.Where(o => o.Enable_On_All_Pages))
+ {
+ var currentPageId = _mainViewModel.SelectedImage;
+ pageState.GlobalObjectStates[obj.Id.Value.ToString()] = new PageObjectState
+ {
+ GlobalObjectId = obj.Id.Value.ToString(),
+ IsVisible = obj.Show_On_This_Page,
+ Left = obj.Left,
+ Top = obj.Top,
+ Rotation = obj.Angulo,
+ CustomProperties = CaptureCustomProperties(obj)
+ };
+ }
+
+ try
+ {
+ // Preparar objetos para serialización
+ foreach (var obj in pageState.LocalObjects)
+ {
+ obj.SalvarDatosNoSerializables();
+ }
+
+ var json = JsonConvert.SerializeObject(pageState, GetSerializerSettings());
+ await File.WriteAllTextAsync(path, json);
+ _hasUnsavedChanges = false;
+ PageStateChanged?.Invoke(this, pageId);
+ }
+ finally
+ {
+ // Restaurar estado de objetos
+ foreach (var obj in pageState.LocalObjects)
+ {
+ obj.RestaurarDatosNoSerializables();
+ }
+ }
+ }
+
+ private Dictionary CaptureCustomProperties(osBase obj)
+ {
+ var customProps = new Dictionary();
+ var properties = obj.GetType().GetProperties()
+ .Where(p => p.GetCustomAttributes(typeof(SerializeAttribute), true).Any());
+
+ foreach (var prop in properties)
+ {
+ customProps[prop.Name] = prop.GetValue(obj);
+ }
+
+ return customProps;
+ }
+
+ public async Task SaveAllAsync()
+ {
+ foreach (var pageId in _loadedPages.Keys)
+ {
+ await SavePageStateAsync(pageId);
+ }
+ await SaveGlobalStateAsync();
+ }
+
+ public void Dispose()
+ {
+ if (_hasUnsavedChanges)
+ {
+ SaveAllAsync().Wait();
+ }
+ }
+ }
+
+ ///
+ /// Atributo para marcar propiedades que deben ser serializadas como propiedades personalizadas
+ ///
+ [AttributeUsage(AttributeTargets.Property)]
+ public class SerializeAttribute : Attribute { }
+}
\ No newline at end of file
diff --git a/FuncionesBase/Idiomas.cs b/FuncionesBase/Idiomas.cs
new file mode 100644
index 0000000..8d2756a
--- /dev/null
+++ b/FuncionesBase/Idiomas.cs
@@ -0,0 +1,68 @@
+using LanguageDetection;
+using System.Collections.Generic;
+using System.ComponentModel;
+using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
+
+namespace CtrEditor.FuncionesBase
+{
+ public class Idiomas
+ {
+ public const string DEFAULT_LANGUAGE = "English";
+
+ private static readonly Dictionary _languageMap = new Dictionary
+ {
+ { "en", "English" },
+ { "es", "Spanish" },
+ { "it", "Italian" },
+ { "pt", "Portuguese" },
+ { "fr", "French" },
+ { "de", "German" }
+ };
+
+ private static readonly LanguageDetector _languageDetector;
+
+ static Idiomas()
+ {
+ _languageDetector = new LanguageDetector();
+ _languageDetector.AddLanguages("en", "es", "it", "pt", "fr", "de");
+ }
+
+ public static List GetLanguageList()
+ {
+ return new List(_languageMap.Values);
+ }
+
+ public static string DetectarIdioma(string texto)
+ {
+ if (string.IsNullOrEmpty(texto)) return DEFAULT_LANGUAGE;
+
+ try
+ {
+ string detectedLanguageCode = _languageDetector.Detect(texto);
+ return _languageMap.GetValueOrDefault(detectedLanguageCode, DEFAULT_LANGUAGE);
+ }
+ catch
+ {
+ return DEFAULT_LANGUAGE;
+ }
+ }
+
+ public static string GetLanguageCode(string languageName)
+ {
+ return _languageMap.FirstOrDefault(x => x.Value == languageName).Key ?? "en";
+ }
+ }
+
+ public class IdiomasItemsSource : IItemsSource
+ {
+ public ItemCollection GetValues()
+ {
+ ItemCollection items = new ItemCollection();
+ foreach (string language in Idiomas.GetLanguageList())
+ {
+ items.Add(language);
+ }
+ return items;
+ }
+ }
+}
diff --git a/FuncionesBase/TagPattern.cs b/FuncionesBase/TagPattern.cs
new file mode 100644
index 0000000..649defa
--- /dev/null
+++ b/FuncionesBase/TagPattern.cs
@@ -0,0 +1,99 @@
+using System;
+using System.Text.RegularExpressions;
+using System.Collections.Generic;
+using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
+
+namespace CtrEditor.FuncionesBase
+{
+ public class TagPattern
+ {
+ public const string DEFAULT_PATTERN = "DESCRIPCION";
+
+ private static readonly Dictionary _patternDescriptions = new Dictionary
+ {
+ { "Descripcion", "First letter capitalized" },
+ { "DESCRIPCION", "All uppercase text" },
+ { "Siemens IO Input", "Format: Exxxxx.y (x: 0-65535, y: 0-7)" },
+ { "Siemens IO Output", "Format: Axxxxx.y (x: 0-65535, y: 0-7)" },
+ { "LETRASNUMEROS", "Format: ABC...123... (1-10 letters + numbers)" },
+ { "Numero", "Numeric value" }
+ };
+
+ public static List GetPatternList()
+ {
+ return new List(_patternDescriptions.Keys);
+ }
+
+ public static string ApplyPattern(string text, string pattern)
+ {
+ if (string.IsNullOrEmpty(text)) return text;
+
+ return pattern switch
+ {
+ "Descripcion" => ApplyDescripcionPattern(text),
+ "DESCRIPCION" => text.ToUpper(),
+ "Siemens IO Input" => ApplySiemensPattern(text, "E"),
+ "Siemens IO Output" => ApplySiemensPattern(text, "A"),
+ "LETRASNUMEROS" => ApplyLetrasNumerosPattern(text),
+ "Numero" => ApplyNumberPattern(text),
+ _ => text
+ };
+ }
+
+ private static string ApplyDescripcionPattern(string text)
+ {
+ if (string.IsNullOrEmpty(text)) return text;
+ return char.ToUpper(text[0]) + (text.Length > 1 ? text.Substring(1).ToLower() : "");
+ }
+
+ private static string ApplySiemensPattern(string text, string prefix)
+ {
+ var match = Regex.Match(text, $"{prefix}?(\\d{{1,5}})\\.?(\\d)?", RegexOptions.IgnoreCase);
+ if (match.Success)
+ {
+ int address = Math.Min(int.Parse(match.Groups[1].Value), 65535);
+ int bit = match.Groups[2].Success ?
+ Math.Min(int.Parse(match.Groups[2].Value), 7) : 0;
+ return $"{prefix}{address}.{bit}";
+ }
+ return $"{prefix}0.0"; // Default value if pattern doesn't match
+ }
+
+ private static string ApplyLetrasNumerosPattern(string text)
+ {
+ // Extract letters and numbers from the text
+ var letters = new string(text.Where(c => char.IsLetter(c)).Take(10).ToArray());
+ var numbers = new string(text.Where(c => char.IsDigit(c)).ToArray());
+
+ // If no letters found, return "A" as default
+ if (string.IsNullOrEmpty(letters))
+ letters = "A";
+
+ // If no numbers found, return "0" as default
+ if (string.IsNullOrEmpty(numbers))
+ numbers = "0";
+
+ // Combine letters (uppercase) and numbers
+ return $"{letters.ToUpper()}{numbers}";
+ }
+
+ private static string ApplyNumberPattern(string text)
+ {
+ var match = Regex.Match(text, @"-?\d+\.?\d*");
+ return match.Success ? match.Value : "0";
+ }
+ }
+
+ public class TagPatternItemsSource : IItemsSource
+ {
+ public ItemCollection GetValues()
+ {
+ ItemCollection items = new ItemCollection();
+ foreach (string pattern in TagPattern.GetPatternList())
+ {
+ items.Add(pattern);
+ }
+ return items;
+ }
+ }
+}
diff --git a/IA/gtpask.cs b/IA/gtpask.cs
index 3afbaf2..c08a982 100644
--- a/IA/gtpask.cs
+++ b/IA/gtpask.cs
@@ -9,6 +9,7 @@ using LanguageDetection;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Diagnostics;
+using CtrEditor.FuncionesBase;
namespace GTPCorrgir
{
@@ -70,15 +71,6 @@ namespace GTPCorrgir
private string _grokApiKey;
private readonly HttpClient _httpClient;
private bool _disposed;
- private readonly LanguageDetector _languageDetector;
-
- private readonly Dictionary _languageMap = new Dictionary
- {
- { "en", "English" },
- { "es", "Spanish" },
- { "it", "Italian" },
- { "pt", "Portuguese" }
- };
public string IdiomaDetectado { get; private set; }
public string TextoACorregir { get; set; }
@@ -90,8 +82,6 @@ namespace GTPCorrgir
try
{
_httpClient = new HttpClient();
- _languageDetector = new LanguageDetector();
- _languageDetector.AddLanguages("en", "es", "it", "pt");
LoadApiKeys();
InitializeHttpClient();
@@ -153,20 +143,15 @@ namespace GTPCorrgir
{
try
{
-
- string detectedLanguageCode = _languageDetector.Detect(TextoACorregir);
- IdiomaDetectado = _languageMap.GetValueOrDefault(detectedLanguageCode, "Desconocido");
-
- return IdiomaDetectado != "Desconocido";
+ IdiomaDetectado = Idiomas.DetectarIdioma(TextoACorregir);
+ return IdiomaDetectado != "Unknown";
}
- catch (Exception ex)
+ catch
{
return false;
}
}
-
-
private async Task ProcesarTextoConLLM( Opciones Modelo)
{
try
diff --git a/Icons/match.png b/Icons/match.png
new file mode 100644
index 0000000..358ddf4
Binary files /dev/null and b/Icons/match.png differ
diff --git a/Icons/ocr.png b/Icons/ocr.png
new file mode 100644
index 0000000..3ecec7d
Binary files /dev/null and b/Icons/ocr.png differ
diff --git a/MainViewModel.cs b/MainViewModel.cs
index 65f49a0..285a2f6 100644
--- a/MainViewModel.cs
+++ b/MainViewModel.cs
@@ -43,9 +43,24 @@ namespace CtrEditor
public Canvas MainCanvas;
- public bool IsConnected
+ [ObservableProperty]
+ private bool isConnected;
+
+ partial void OnPLCViewModelChanged(PLCViewModel value)
{
- get => PLCViewModel.IsConnected;
+ 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]
@@ -64,8 +79,6 @@ namespace CtrEditor
public ICommand TBStartSimulationCommand { get; }
public ICommand TBStopSimulationCommand { get; }
public ICommand TBSaveCommand { get; }
- public ICommand TBConnectPLCCommand { get; }
- public ICommand TBDisconnectPLCCommand { get; }
public ICommand TBExtractTagsCommand { get; }
public ICommand TBEliminarUserControlCommand { get; }
@@ -79,9 +92,17 @@ namespace CtrEditor
public ICommand TBAssingPagesCommand { get; }
public ICommand TBMultiPageExtractTagsCommand { get; }
public ICommand TBMultiPageAnalizeCommand { get; }
- public ICommand TBAnalyzeMatrixCommand { 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;
@@ -202,27 +223,24 @@ namespace CtrEditor
[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)
{
- if (HasUnsavedChanges && !inhibitSaveChangesControl)
- {
- var result = MessageBox.Show("There are unsaved changes. Do you want to save them?",
- "Save Changes",
- MessageBoxButton.YesNoCancel);
-
- if (result == MessageBoxResult.Cancel)
- {
- OnPropertyChanged(nameof(SelectedImage)); // Restore previous selection
- return;
- }
- else if (result == MessageBoxResult.Yes)
- {
- SaveStateObjetosSimulables();
- }
- }
-
StopSimulation();
ImageSelected?.Invoke(this, datosDeTrabajo.Imagenes[value]);
LoadStateObjetosSimulables();
@@ -271,6 +289,8 @@ namespace CtrEditor
// Constructor
//
+ private StateManager _stateManager;
+
public MainViewModel()
{
OpenWorkDirectoryCommand = new RelayCommand(OpenWorkDirectory);
@@ -289,7 +309,7 @@ namespace CtrEditor
PLCViewModel = new PLCViewModel();
_timerPLCUpdate = new DispatcherTimer();
- _timerPLCUpdate.Interval = TimeSpan.FromMilliseconds(50); // ajusta el intervalo según sea necesario
+ _timerPLCUpdate.Interval = TimeSpan.FromMilliseconds(10); // ajusta el intervalo según sea necesario
_timerPLCUpdate.Tick += OnRefreshEvent;
InitializeTipoSimulableList();
@@ -306,21 +326,15 @@ namespace CtrEditor
TBStartSimulationCommand = new RelayCommand(StartSimulation, () => !IsSimulationRunning);
TBStopSimulationCommand = new RelayCommand(StopSimulation, () => IsSimulationRunning);
TBSaveCommand = new RelayCommand(Save);
- TBConnectPLCCommand = new RelayCommand(ConnectPLC, () => !PLCViewModel.IsConnected);
- TBDisconnectPLCCommand = new RelayCommand(DisconnectPLC, () => PLCViewModel.IsConnected);
TBEliminarUserControlCommand = new RelayCommand(EliminarUserControl, () => habilitarEliminarUserControl);
TBDuplicarUserControlCommand = new RelayCommand(DuplicarUserControl, () => habilitarEliminarUserControl);
- TBExtractTagsCommand = new RelayCommand(ExtraerTags);
-
TBEliminarTodosCommand = new RelayCommand(EliminarTodosCommand);
TBEliminarAutoCreatedCommand = new RelayCommand(EliminarAutoCreatedCommand);
TBEliminarClonedCommand = new RelayCommand(EliminarClonedCommand);
TBAssingPagesCommand = new RelayCommand(AssingPagesCommand);
- TBMultiPageExtractTagsCommand = new RelayCommand(MultiPageExtractTagsCommand);
TBMultiPageAnalizeCommand = new RelayCommand(MultiPageAnalizeCommand);
- TBAnalyzeMatrixCommand = new RelayCommand(AnalyzeMatrixCommand);
TBMultiPageMatrixCommand = new RelayCommand(MultiPageMatrixCommand);
stopwatch_Sim = new Stopwatch();
@@ -334,6 +348,8 @@ namespace CtrEditor
recentDirectories = new ObservableCollection(EstadoPersistente.Instance.RecentDirectories);
OpenRecentDirectoryCommand = new RelayCommand(OpenRecentDirectory);
+
+ _stateManager = new StateManager(EstadoPersistente.Instance.directorio, this);
}
private void OsListFilter_PropertyChanged(object? sender, PropertyChangedEventArgs e)
@@ -544,51 +560,6 @@ namespace CtrEditor
Application.Current.Dispatcher.Invoke(() => { }, DispatcherPriority.ApplicationIdle);
}
-
- private async void MultiPageExtractTagsCommand()
- {
- 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)
- foreach (var page in ImagenesSeleccionadas)
- {
- SelectedImage = page;
- await WaitForUIUpdateAsync(); // Espera a que la UI se actualice
- ExtraerTags();
- }
- }
- finally
- {
- inhibitSaveChangesControl = false;
- }
- }
-
private async void MultiPageAnalizeCommand()
{
var ImagenesSeleccionadas = new ObservableCollection
@@ -641,148 +612,6 @@ namespace CtrEditor
}
-
- ///
- /// Extrae y formatea las etiquetas de los objetos simulables y las guarda en un archivo Excel.
- ///
- private void ExtraerTags()
- {
- // Obtiene la ruta del archivo Excel donde se guardarán los datos.
- var filePath = DatosDeTrabajo.ObtenerPathAllPages(".xlsx");
-
- try
- {
- // Crea o abre un libro de Excel.
- XLWorkbook workbook = File.Exists(filePath) ? new XLWorkbook(filePath) : new XLWorkbook();
- var sheetName = "TagsExtracted";
- // Obtiene o crea la hoja de trabajo "TagsExtracted".
- var worksheet = workbook.Worksheets.Contains(sheetName) ? workbook.Worksheet(sheetName) : workbook.Worksheets.Add(sheetName);
- var lastRowUsed = worksheet.LastRowUsed();
- // Determina la fila en la que se empezarán a escribir los datos.
- int rowOffset = lastRowUsed == null ? 2 : lastRowUsed.RowNumber() + 1;
-
- // Determina la columna fija más alta.
- List columnasOcupadas = new List();
- int actualMaxCol = 0;
- int col = 0;
-
- // Filtrar los objetos de tipo osExtraccionTag y crear una nueva lista
- var osBuscarCoincidencias_List = ObjetosSimulables
- .OfType()
- .Where(tag => tag.Show_On_This_Page)
- .ToList();
- var osExtraccionTagBaseGrouped_List = ObjetosSimulables
- .OfType()
- .Where(tag => tag.Show_On_This_Page && !tag.Cloned && tag.Id_Search_Templates != null && tag.Id_Search_Templates != "")
- .ToList();
- var osExtraccionTagBaseFix_List = ObjetosSimulables
- .OfType()
- .Where(tag => tag.Show_On_This_Page && !tag.Cloned && (tag.Id_Search_Templates == null || tag.Id_Search_Templates == ""))
- .ToList();
-
- var osExtraccionTagCloned_List = ObjetosSimulables
- .OfType()
- .Where(tag => tag.Show_On_This_Page && tag.Cloned)
- .ToList();
-
- // Columnas Fijas para los Tags no agrupados que no son clonados
- foreach (var objExtraccionTag in osExtraccionTagBaseFix_List)
- if ((string.IsNullOrEmpty(objExtraccionTag.Id_Search_Templates)) && !objExtraccionTag.Cloned)
- {
- col = objExtraccionTag.Collumn_number;
- if (col == 0 || columnasOcupadas.Contains(col))
- col = ++actualMaxCol;
- else
- actualMaxCol = Math.Max(actualMaxCol, col);
-
- columnasOcupadas.Add(col);
- objExtraccionTag.Collumn_number = col;
- }
-
- // Tags Agrupados no Clonados
- foreach (var objBC in osBuscarCoincidencias_List)
- foreach (var objExtraccionTag in osExtraccionTagBaseGrouped_List)
- if (objExtraccionTag.Id_Search_Templates == objBC.Nombre && !objExtraccionTag.Cloned)
- {
- col = objExtraccionTag.Collumn_number;
- if (col == 0 || columnasOcupadas.Contains(col))
- col = ++actualMaxCol;
- else
- actualMaxCol = Math.Max(actualMaxCol, col);
-
- columnasOcupadas.Add(col);
- objExtraccionTag.Collumn_number = col;
- }
-
- int RowToRender = 0;
- // Cloned Tag - Asignar las mismas columnas
- foreach (var oFrom in osExtraccionTagBaseGrouped_List)
- foreach (var oCloned in osExtraccionTagCloned_List)
- {
- if (oCloned.Cloned_from == oFrom.Id)
- oCloned.Collumn_number = oFrom.Collumn_number;
- RowToRender = Math.Max(RowToRender, oCloned.Copy_Number);
- }
-
- // Render Rows
- for (int row = 0; row < RowToRender; row++)
- {
- // Render Fix tags
- foreach (var TagFixs in osExtraccionTagBaseFix_List)
- {
- col = TagFixs.Collumn_number;
- if (worksheet.Cell(1, col).IsEmpty())
- worksheet.Cell(1, col).Value = TagFixs.Collumn_name;
- TagFixs.CaptureImageAreaAndDoOCR();
- worksheet.Cell(row + rowOffset, col).Value = TagFixs.Tag_extract;
- }
- // Render Cloned tags
- foreach (var TagCloned in osExtraccionTagCloned_List)
- {
- if (TagCloned.Copy_Number == row) // Estamos en la fila correcta
- {
- col = TagCloned.Collumn_number;
- if (worksheet.Cell(1, col).IsEmpty())
- worksheet.Cell(1, col).Value = TagCloned.Collumn_name;
-
- TagCloned.CaptureImageAreaAndDoOCR();
- worksheet.Cell(row + rowOffset, col).Value = TagCloned.Tag_extract;
- }
- }
- }
-
-
- // Formatear los títulos en la fila 1
- var titleRow = worksheet.Row(1);
- titleRow.Style.Font.Bold = true;
- titleRow.Style.Fill.BackgroundColor = XLColor.LightGray;
- titleRow.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
-
- // Auto dimensionado de las columnas utilizadas
- worksheet.Columns().AdjustToContents();
-
- // Guarda el libro de Excel.
- workbook.SaveAs(filePath);
- HasUnsavedChanges = false;
- }
- catch (IOException ex)
- {
- // Muestra un diálogo de error si ocurre una excepción de IO.
- var dialog = new TaskDialog
- {
- WindowTitle = "IOException",
- MainInstruction = "Error al acceder al archivo",
- Content = $"El proceso no puede acceder al archivo '{filePath}' porque está siendo utilizado por otro proceso.",
- ExpandedInformation = ex.ToString(),
- MainIcon = TaskDialogIcon.Error,
- ButtonStyle = TaskDialogButtonStyle.Standard
- };
- dialog.Buttons.Add(new TaskDialogButton(ButtonType.Ok));
- dialog.ShowDialog();
- }
- }
-
-
private void InitializeTipoSimulableList()
{
var baseType = typeof(osBase);
@@ -1142,15 +971,6 @@ namespace CtrEditor
Application.Current.Shutdown();
}
- private void AnalyzeMatrixCommand()
- {
- var matrixPreviewWindow = new MatrixPreviewWindow();
- var matrixPreviewViewModel = new MatrixPreviewViewModel();
- matrixPreviewViewModel.Initialize(this, matrixPreviewWindow);
- matrixPreviewWindow.DataContext = matrixPreviewViewModel;
- matrixPreviewWindow.ShowDialog();
- }
-
private async void MultiPageMatrixCommand()
{
if (HasUnsavedChanges)
diff --git a/MainWindow.xaml b/MainWindow.xaml
index 761712c..921e08e 100644
--- a/MainWindow.xaml
+++ b/MainWindow.xaml
@@ -27,13 +27,21 @@
+
+
+
+
+
+
@@ -116,42 +124,29 @@
-
-
-
-
+
diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs
index fc4802f..a1719f3 100644
--- a/MainWindow.xaml.cs
+++ b/MainWindow.xaml.cs
@@ -288,6 +288,15 @@ namespace CtrEditor
new Tuple(new Point(rectBox.Right, rectBox.Top + rectBox.Height / 2), "CenterRight")
};
+ // Add validation before setting Canvas position
+ void SetCanvasPosition(UIElement element, double left, double top)
+ {
+ if (!double.IsInfinity(left) && !double.IsNaN(left))
+ Canvas.SetLeft(element, left);
+ if (!double.IsInfinity(top) && !double.IsNaN(top))
+ Canvas.SetTop(element, top);
+ }
+
foreach (var position in positions)
{
Rectangle rect = new Rectangle
@@ -337,8 +346,8 @@ namespace CtrEditor
break;
}
- Canvas.SetLeft(rect, position.Item1.X - rectSize / 2);
- Canvas.SetTop(rect, position.Item1.Y - rectSize / 2);
+ // Replace direct Canvas.Set calls with the validation method
+ SetCanvasPosition(rect, position.Item1.X - rectSize / 2, position.Item1.Y - rectSize / 2);
rect.MouseLeftButtonDown += UserControl_MouseLeftButtonDown;
rect.MouseMove += UserControl_MouseMove;
diff --git a/ObjetosSim/Estaticos/ucVMmotorSim.xaml.cs b/ObjetosSim/Estaticos/ucVMmotorSim.xaml.cs
index 7521d7c..6616a41 100644
--- a/ObjetosSim/Estaticos/ucVMmotorSim.xaml.cs
+++ b/ObjetosSim/Estaticos/ucVMmotorSim.xaml.cs
@@ -123,8 +123,6 @@ namespace CtrEditor.ObjetosSim
public override void UpdatePLC(PLCViewModel plc, int TotalMilliseconds)
{
- Stopwatch stopwatch = new Stopwatch();
- stopwatch.Start();
elapsedTimeAccumulator += TotalMilliseconds;
float randomFactor = (float)(new Random().NextDouble() * 0.1); // 10% random factor
@@ -135,12 +133,9 @@ namespace CtrEditor.ObjetosSim
motState.UpdatePLC(plc, this, TotalMilliseconds);
elapsedTimeAccumulator = 0;
}
-
Velocidad = (Proporcional_Speed / 100) * (motState.STATUS_VFD_ACT_Speed_Hz / 10);
Sentido_contrario = motState.OUT_Reversal;
- stopwatch.Stop();
- Debug.WriteLine($" osVMmotorSim : {Nombre} : {stopwatch.Elapsed.TotalMilliseconds} ms");
}
public override void UpdateControl(int TotalMilliseconds)
diff --git a/ObjetosSim/Extraccion Datos/ucBuscarCoincidencias.xaml.cs b/ObjetosSim/Extraccion Datos/ucBuscarCoincidencias.xaml.cs
index 590b00f..d1424ae 100644
--- a/ObjetosSim/Extraccion Datos/ucBuscarCoincidencias.xaml.cs
+++ b/ObjetosSim/Extraccion Datos/ucBuscarCoincidencias.xaml.cs
@@ -27,9 +27,40 @@ using Emgu.CV.Structure;
namespace CtrEditor.ObjetosSim.Extraccion_Datos
{
///
- /// Interaction logic for ucBuscarCoincidencias.xaml
+ /// Represents a template search control that identifies similar patterns in images and creates tag extraction clones.
+ /// This class is designed to work with OCR extraction by finding visual patterns and creating copies of extraction tags
+ /// at each matching location.
///
+ ///
+ /// Key functionalities:
+ /// - Template matching using OpenCV
+ /// - Automatic tag cloning at found locations
+ /// - OCR text extraction from matched regions
+ /// - Export capabilities to Excel
///
+ /// Workflow:
+ /// 1. User creates a search template by positioning and sizing the control over a pattern
+ /// 2. Links extraction tags to this template using Id_Search_Templates
+ /// 3. Activates search_templates to find similar patterns
+ /// 4. The system automatically:
+ /// - Searches for visual matches in the image
+ /// - Creates clones of linked extraction tags at each match
+ /// - Assigns incremental copy_Number to organize rows in exports
+ /// - Performs OCR on each cloned tag location
+ ///
+ /// Properties:
+ /// - search_templates: Triggers the pattern search process
+ /// - threshold: Minimum similarity threshold for pattern matching
+ /// - coincidencias: Number of matches found (readonly)
+ /// - show_debug_ocr: Shows debug windows during OCR process
+ /// - export_ocr: Triggers OCR text export for all matches
+ ///
+ /// Usage example:
+ /// 1. Position the search template over a repeating pattern
+ /// 2. Create extraction tags and link them to this template
+ /// 3. Set threshold value (default usually works well)
+ /// 4. Activate search_templates to find matches and create clones
+ ///
public partial class osBuscarCoincidencias : osBase, IosBase
{
@@ -362,9 +393,6 @@ namespace CtrEditor.ObjetosSim.Extraccion_Datos
}
}
Row++;
- if (newObj != null)
- newObj.New_Row = true;
-
}
}
diff --git a/ObjetosSim/Extraccion Datos/ucExtraccionTag.xaml.cs b/ObjetosSim/Extraccion Datos/ucExtraccionTag.xaml.cs
index 2631580..13654cc 100644
--- a/ObjetosSim/Extraccion Datos/ucExtraccionTag.xaml.cs
+++ b/ObjetosSim/Extraccion Datos/ucExtraccionTag.xaml.cs
@@ -36,7 +36,18 @@ namespace CtrEditor.ObjetosSim.Extraccion_Datos
bool extraer;
[ObservableProperty]
- bool new_Row;
+ [property: Category("Tag Extraction:")]
+ bool eliminar_enters;
+
+ [ObservableProperty]
+ [property: Category("Tag Extraction:")]
+ [property: ItemsSource(typeof(IdiomasItemsSource))]
+ string idioma_Extraccion;
+
+ [ObservableProperty]
+ [property: Category("Tag Extraction:")]
+ [property: ItemsSource(typeof(TagPatternItemsSource))]
+ string pattern_Type;
public override void TopChanged(float value)
{
@@ -147,11 +158,39 @@ namespace CtrEditor.ObjetosSim.Extraccion_Datos
Alto = 1;
Angulo = 0;
Opacity_oculto = 0.1f;
+ Idioma_Extraccion = Idiomas.DEFAULT_LANGUAGE;
+ Pattern_Type = TagPattern.DEFAULT_PATTERN;
}
public void CaptureImageAreaAndDoOCR()
{
- Tag_extract = CaptureImageAreaAndDoOCR(Left, Top, Ancho, Alto, Angulo, Show_Debug_Window);
+ string extractedText = CaptureImageAreaAndDoOCR(Left, Top, Ancho, Alto, Angulo, Show_Debug_Window);
+
+ // Clean up the extracted text if eliminar_enters is true
+ if (Eliminar_enters && !string.IsNullOrEmpty(extractedText))
+ {
+ // Replace all types of line endings with spaces
+ extractedText = extractedText.Replace("\r\n", " ")
+ .Replace("\n", " ")
+ .Replace("\r", " ");
+
+ // Replace multiple consecutive spaces with a single space
+ extractedText = System.Text.RegularExpressions.Regex.Replace(extractedText, @"\s+", " ");
+
+ // Trim spaces at the beginning and end
+ extractedText = extractedText.Trim();
+ }
+
+ // Apply the selected pattern
+ extractedText = TagPattern.ApplyPattern(extractedText, Pattern_Type);
+
+ Tag_extract = extractedText;
+
+ // Set default language to English if not set
+ if (string.IsNullOrEmpty(Idioma_Extraccion))
+ {
+ Idioma_Extraccion = Idiomas.DEFAULT_LANGUAGE;
+ }
}
public int ExportToExcel(IXLWorksheet worksheet, int row, int colBase)
diff --git a/ObjetosSim/osBase.cs b/ObjetosSim/osBase.cs
index e26f732..df4cdce 100644
--- a/ObjetosSim/osBase.cs
+++ b/ObjetosSim/osBase.cs
@@ -312,7 +312,7 @@ namespace CtrEditor.ObjetosSim
if (e.PropertyName == nameof(osFramePlate.Left))
{
Left += ((osFramePlate)sender).offsetX;
- OnMoveResizeRotate();
+ OnMoveResizeRotate();
}
if (e.PropertyName == nameof(osFramePlate.Zindex_FramePlate))
diff --git a/PopUps/AssignImagesWindow.xaml.cs b/PopUps/AssignImagesWindow.xaml.cs
index aec6359..a2e485f 100644
--- a/PopUps/AssignImagesWindow.xaml.cs
+++ b/PopUps/AssignImagesWindow.xaml.cs
@@ -12,7 +12,7 @@ namespace CtrEditor.PopUps
/// Interaction logic for AssignImagesWindow.xaml
///
public partial class AssignImagesWindow : Window
- {
+ {
public AssignImagesWindow()
{
InitializeComponent();
diff --git a/PopUps/ColumnSelectionDialog.xaml.cs b/PopUps/ColumnSelectionDialog.xaml.cs
index a7cc3d0..a34de8d 100644
--- a/PopUps/ColumnSelectionDialog.xaml.cs
+++ b/PopUps/ColumnSelectionDialog.xaml.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Windows;
+using CtrEditor.FuncionesBase;
namespace CtrEditor.PopUps
{
@@ -12,15 +13,7 @@ namespace CtrEditor.PopUps
public string SelectedSourceLanguage { get; private set; }
public string SelectedTargetLanguage { get; private set; }
- private static readonly List SupportedLanguages = new List
- {
- "English",
- "Spanish",
- "Italian",
- "French",
- "German",
- "Portuguese"
- };
+ private static readonly List SupportedLanguages = Idiomas.GetLanguageList();
private string _sourceColumnLabel = "Idioma 1";
private string _targetColumnLabel = "Idioma 2";
diff --git a/PopUps/MatrixPreviewWindow.xaml b/PopUps/MatrixPreviewWindow.xaml
index bf7a0be..c3db2b4 100644
--- a/PopUps/MatrixPreviewWindow.xaml
+++ b/PopUps/MatrixPreviewWindow.xaml
@@ -1,7 +1,7 @@
+ Title="Exportar Tags Extraidos" Height="600" Width="800">
@@ -70,23 +70,23 @@
HorizontalAlignment="Right"
Margin="5"
Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}">
-
-
-
-
-
diff --git a/Services/LLMService.cs b/Services/LLMService.cs
index 85ad5a8..17beae4 100644
--- a/Services/LLMService.cs
+++ b/Services/LLMService.cs
@@ -41,8 +41,8 @@ namespace CtrEditor.Services
public async Task> ProcessTextBatch(
List<(string Source, string Target)> textPairs,
- string sourceLanguage = "Unknown",
- string targetLanguage = "Unknown")
+ string sourceLanguage = "English",
+ string targetLanguage = "English")
{
try
{
diff --git a/ViewModels/MatrixPreviewViewModel.cs b/ViewModels/MatrixPreviewViewModel.cs
index e89138a..95eb5b6 100644
--- a/ViewModels/MatrixPreviewViewModel.cs
+++ b/ViewModels/MatrixPreviewViewModel.cs
@@ -213,31 +213,60 @@ namespace CtrEditor.PopUps
UpdateMatrixPreview();
}
+ ///
+ /// Analyzes the current page and collects all extraction tags data for matrix preview.
+ ///
+ ///
+ /// The method processes three types of extraction tags:
+ /// 1. Fixed Tags: Tags without Search Templates association
+ /// 2. Grouped Tags: Tags linked to Search Templates but not cloned
+ /// 3. Cloned Tags: Tags created by Search Templates pattern matching
+ ///
+ /// Workflow:
+ /// 1. Filters objects by visibility on current page
+ /// 2. Groups tags by their type (fixed, grouped, cloned)
+ /// 3. Performs OCR extraction on each tag
+ /// 4. Creates MatrixItems with extracted data
+ ///
+ /// Data Organization:
+ /// - Fixed tags maintain their original position and values
+ /// - Grouped tags are associated with their Search Templates
+ /// - Cloned tags inherit Copy_Number from their creation order in pattern matching
+ ///
+ /// Related Classes:
+ /// - osExtraccionTag: Contains OCR extraction logic and tag properties
+ /// - osBuscarCoincidencias: Handles pattern matching and tag cloning
+ ///
+ /// A list of MatrixItems containing all processed tag data for the current page
private List AnalyzePage()
{
var items = new List();
+ // Filter visible objects by type
var osBuscarCoincidencias_List = _mainViewModel.ObjetosSimulables
.OfType()
.Where(tag => tag.Show_On_This_Page)
.ToList();
+ // Get base tags that are linked to Search Templates
var osExtraccionTagBaseGrouped_List = _mainViewModel.ObjetosSimulables
.OfType()
.Where(tag => tag.Show_On_This_Page && !tag.Cloned && tag.Id_Search_Templates != null && tag.Id_Search_Templates != "")
.ToList();
+ // Get fixed tags (not linked to Search Templates)
var osExtraccionTagBaseFix_List = _mainViewModel.ObjetosSimulables
.OfType()
.Where(tag => tag.Show_On_This_Page && !tag.Cloned && (tag.Id_Search_Templates == null || tag.Id_Search_Templates == ""))
.ToList();
+ // Get cloned tags (created by Search Templates)
var osExtraccionTagCloned_List = _mainViewModel.ObjetosSimulables
.OfType()
.Where(tag => tag.Show_On_This_Page && tag.Cloned)
.ToList();
- // Add fixed tags
+ // Process fixed tags
foreach (var tag in osExtraccionTagBaseFix_List)
{
tag.CaptureImageAreaAndDoOCR();
@@ -253,7 +282,7 @@ namespace CtrEditor.PopUps
});
}
- // Add grouped tags
+ // Process grouped tags
foreach (var tag in osExtraccionTagBaseGrouped_List)
{
tag.CaptureImageAreaAndDoOCR();
@@ -269,7 +298,7 @@ namespace CtrEditor.PopUps
});
}
- // Add cloned tags
+ // Process cloned tags
foreach (var tag in osExtraccionTagCloned_List)
{
tag.CaptureImageAreaAndDoOCR();
@@ -282,7 +311,7 @@ namespace CtrEditor.PopUps
Type = "Cloned",
IsCloned = true,
Id = tag.Id,
- Copy_Number = tag.Copy_Number // Add this line
+ Copy_Number = tag.Copy_Number
});
}
diff --git a/XAMLhelpers.cs b/XAMLhelpers.cs
index db943e2..829e918 100644
--- a/XAMLhelpers.cs
+++ b/XAMLhelpers.cs
@@ -30,6 +30,33 @@ namespace CtrEditor
}
+ public class ConnectStateToBtnTextConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return (bool)value ? "Desconectar PLC" : "Conectar PLC";
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public class ConnectStateToImageConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return (bool)value ? "Icons/disconnect.png" : "Icons/connect.png";
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+
public class ListItemPropertyDescriptor : PropertyDescriptor
{
private readonly IList owner;