Se implementó un sistema de filtrado por etiquetas en la interfaz de usuario, permitiendo a los usuarios buscar y seleccionar objetos basados en etiquetas personalizadas. Se añadieron nuevas propiedades y métodos en la clase osBase para gestionar etiquetas, así como mejoras en la lógica de actualización de filtros en función de los objetos disponibles. Además, se realizaron ajustes en la visualización y manejo de los filtros en el control osVisFilter.

This commit is contained in:
Miguel 2025-06-17 18:38:00 +02:00
parent 99248e9112
commit 67fa5eef3d
7 changed files with 718 additions and 91 deletions

View File

@ -46,6 +46,24 @@
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>
</ItemsControl> </ItemsControl>
<!-- Separator between types and tags -->
<Separator Style="{StaticResource FilterSeparatorStyle}" />
<!-- Tag Filter Options -->
<TextBlock Text="Tags" Style="{StaticResource FilterHeaderStyle}" />
<TextBox Margin="0,2,0,5"
Text="{Binding SearchTags, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ToolTip="Buscar etiquetas (use # antes de cada etiqueta, ej: #motor #bomba)"
FontSize="11" />
<ItemsControl ItemsSource="{Binding TagFilters}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding DisplayName}" IsChecked="{Binding IsSelected, Mode=TwoWay}"
Style="{StaticResource FilterCheckBoxStyle}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel> </StackPanel>
</ScrollViewer> </ScrollViewer>
</Border> </Border>

View File

@ -46,6 +46,14 @@ namespace CtrEditor.Controls
{ {
FilterViewModel.UpdateTypeFilters(types); FilterViewModel.UpdateTypeFilters(types);
} }
/// <summary>
/// Updates the tag filters based on the provided objects
/// </summary>
public void UpdateAvailableTags(IEnumerable<osBase> objects)
{
FilterViewModel.UpdateTagFilters(objects);
}
} }
/// <summary> /// <summary>
@ -71,6 +79,12 @@ namespace CtrEditor.Controls
[ObservableProperty] [ObservableProperty]
private ObservableCollection<TypeFilterItem> typeFilters = new ObservableCollection<TypeFilterItem>(); private ObservableCollection<TypeFilterItem> typeFilters = new ObservableCollection<TypeFilterItem>();
[ObservableProperty]
private ObservableCollection<TagFilterItem> tagFilters = new ObservableCollection<TagFilterItem>();
[ObservableProperty]
private string searchTags = "";
partial void OnShowAllChanged(bool value) partial void OnShowAllChanged(bool value)
{ {
NotifyFilterChanged(); NotifyFilterChanged();
@ -113,6 +127,28 @@ namespace CtrEditor.Controls
} }
} }
partial void OnTagFiltersChanged(ObservableCollection<TagFilterItem> value)
{
if (value != null)
{
foreach (var filter in value)
{
filter.PropertyChanged += (s, e) =>
{
if (e.PropertyName == nameof(TagFilterItem.IsSelected))
{
NotifyFilterChanged();
}
};
}
}
}
partial void OnSearchTagsChanged(string value)
{
NotifyFilterChanged();
}
private void NotifyFilterChanged() private void NotifyFilterChanged()
{ {
if (this.Parent is osVisFilter filter) if (this.Parent is osVisFilter filter)
@ -165,6 +201,44 @@ namespace CtrEditor.Controls
} }
} }
/// <summary>
/// Updates the tag filters based on the provided objects
/// </summary>
public void UpdateTagFilters(IEnumerable<osBase> objects)
{
// Get all unique tags from all objects
var allTags = objects
.SelectMany(obj => obj.ListaEtiquetas)
.Distinct()
.OrderBy(tag => tag)
.ToList();
// Remove tags that are no longer present
var tagsToRemove = TagFilters
.Where(tf => !allTags.Contains(tf.TagName))
.ToList();
foreach (var item in tagsToRemove)
{
UnsubscribeFromTagFilter(item);
TagFilters.Remove(item);
}
// Add new tags that aren't already in the list
foreach (var tag in allTags)
{
if (!TagFilters.Any(tf => tf.TagName == tag))
{
var newFilter = new TagFilterItem(tag)
{
IsSelected = true
};
SubscribeToTagFilter(newFilter);
TagFilters.Add(newFilter);
}
}
}
private void SubscribeToTypeFilter(TypeFilterItem filter) private void SubscribeToTypeFilter(TypeFilterItem filter)
{ {
filter.PropertyChanged += TypeFilter_PropertyChanged; filter.PropertyChanged += TypeFilter_PropertyChanged;
@ -175,6 +249,16 @@ namespace CtrEditor.Controls
filter.PropertyChanged -= TypeFilter_PropertyChanged; filter.PropertyChanged -= TypeFilter_PropertyChanged;
} }
private void SubscribeToTagFilter(TagFilterItem filter)
{
filter.PropertyChanged += TagFilter_PropertyChanged;
}
private void UnsubscribeFromTagFilter(TagFilterItem filter)
{
filter.PropertyChanged -= TagFilter_PropertyChanged;
}
private void TypeFilter_PropertyChanged(object sender, PropertyChangedEventArgs e) private void TypeFilter_PropertyChanged(object sender, PropertyChangedEventArgs e)
{ {
if (e.PropertyName == nameof(TypeFilterItem.IsSelected)) if (e.PropertyName == nameof(TypeFilterItem.IsSelected))
@ -183,6 +267,14 @@ namespace CtrEditor.Controls
} }
} }
private void TagFilter_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(TagFilterItem.IsSelected))
{
NotifyFilterChanged();
}
}
/// <summary> /// <summary>
/// Gets the display name for a type, using the NombreClase static method if available /// Gets the display name for a type, using the NombreClase static method if available
/// </summary> /// </summary>
@ -220,4 +312,26 @@ namespace CtrEditor.Controls
OnPropertyChanged(nameof(IsSelected)); OnPropertyChanged(nameof(IsSelected));
} }
} }
/// <summary>
/// Represents a tag filter item with selection state
/// </summary>
public partial class TagFilterItem : ObservableObject
{
[ObservableProperty]
private bool isSelected;
public string TagName { get; }
public string DisplayName => "#" + TagName;
public TagFilterItem(string tagName)
{
TagName = tagName;
}
partial void OnIsSelectedChanged(bool value)
{
// Could add logic here if needed when selection changes
}
}
} }

View File

@ -317,6 +317,7 @@ namespace CtrEditor
private void UpdateVisFilterTypes() private void UpdateVisFilterTypes()
{ {
MainWindow?.VisFilter?.UpdateAvailableTypes(ObjetosSimulables.Select(o => o.GetType()).Distinct()); MainWindow?.VisFilter?.UpdateAvailableTypes(ObjetosSimulables.Select(o => o.GetType()).Distinct());
MainWindow?.VisFilter?.UpdateAvailableTags(ObjetosSimulables);
} }
// //
@ -412,6 +413,36 @@ namespace CtrEditor
isVisible = false; isVisible = false;
} }
// Check tag filters
if (filter.TagFilters.Any() && filter.TagFilters.Any(tf => tf.IsSelected))
{
var selectedTags = filter.TagFilters.Where(tf => tf.IsSelected).Select(tf => tf.TagName).ToList();
bool hasMatchingTag = obj.ListaEtiquetas.Any(tag => selectedTags.Contains(tag));
if (!hasMatchingTag)
{
isVisible = false;
}
}
// Check search tags
if (!string.IsNullOrWhiteSpace(filter.SearchTags))
{
var searchTags = filter.SearchTags
.Split(' ', StringSplitOptions.RemoveEmptyEntries)
.Where(tag => tag.StartsWith("#") && tag.Length > 1)
.Select(tag => tag.Substring(1).ToLowerInvariant())
.ToList();
if (searchTags.Any())
{
bool hasMatchingSearchTag = searchTags.Any(searchTag => obj.ListaEtiquetas.Contains(searchTag));
if (!hasMatchingSearchTag)
{
isVisible = false;
}
}
}
// Check other filters // Check other filters
if (filter.ShowCloned && !obj.Cloned) if (filter.ShowCloned && !obj.Cloned)
isVisible = false; isVisible = false;
@ -665,7 +696,7 @@ namespace CtrEditor
.Where(o => o.Show_On_This_Page && o.AutoCreated) .Where(o => o.Show_On_This_Page && o.AutoCreated)
.ToList(); .ToList();
foreach (var obj in osAutoCreated_List) foreach (var obj in osAutoCreated_List)
RemoverObjetoSimulable(obj); RemoverObjetoSimulable(obj);
} }
private void EliminarClonedCommand() private void EliminarClonedCommand()
@ -674,7 +705,7 @@ namespace CtrEditor
.Where(o => o.Show_On_This_Page && o.Cloned) .Where(o => o.Show_On_This_Page && o.Cloned)
.ToList(); .ToList();
foreach (var obj in osCloned_List) foreach (var obj in osCloned_List)
RemoverObjetoSimulable(obj); RemoverObjetoSimulable(obj);
} }
private void AssingPagesCommand() private void AssingPagesCommand()
@ -765,10 +796,10 @@ namespace CtrEditor
private void StartSimulation() private void StartSimulation()
{ {
// Detener simulación de fluidos si está ejecutándose // Detener simulación de fluidos si está ejecutándose
StopFluidSimulation(); StopFluidSimulation();
IsSimulationRunning = true; IsSimulationRunning = true;
// Ocultar rectángulos de selección antes de iniciar la simulación // Ocultar rectángulos de selección antes de iniciar la simulación
_objectManager.UpdateSelectionVisuals(); _objectManager.UpdateSelectionVisuals();
@ -795,36 +826,36 @@ namespace CtrEditor
{ {
simulationManager.Debug_ClearSimulationShapes(); simulationManager.Debug_ClearSimulationShapes();
Debug_SimulacionCreado = false; Debug_SimulacionCreado = false;
} }
_timerSimulacion.Stop(); _timerSimulacion.Stop();
// Restaurar los rectángulos de selección si hay objetos seleccionados // Restaurar los rectángulos de selección si hay objetos seleccionados
_objectManager.UpdateSelectionVisuals(); _objectManager.UpdateSelectionVisuals();
// Limpiar historial de undo al detener la simulación // Limpiar historial de undo al detener la simulación
MainWindow?.ClearUndoHistory(); MainWindow?.ClearUndoHistory();
} }
/// <summary> /// <summary>
/// Inicia la simulación de fluidos independiente /// Inicia la simulación de fluidos independiente
/// </summary> /// </summary>
public void StartFluidSimulation() public void StartFluidSimulation()
{ {
// Detener simulación física si está ejecutándose // Detener simulación física si está ejecutándose
StopSimulation(); StopSimulation();
FluidSimulation.StartFluidSimulation(); FluidSimulation.StartFluidSimulation();
CommandManager.InvalidateRequerySuggested(); CommandManager.InvalidateRequerySuggested();
} }
/// <summary> /// <summary>
/// Detiene la simulación de fluidos independiente /// Detiene la simulación de fluidos independiente
/// </summary> /// </summary>
public void StopFluidSimulation() public void StopFluidSimulation()
{ {
FluidSimulation.StopFluidSimulation(); FluidSimulation.StopFluidSimulation();
CommandManager.InvalidateRequerySuggested(); CommandManager.InvalidateRequerySuggested();
} }
private void OnTickSimulacion(object sender, EventArgs e) private void OnTickSimulacion(object sender, EventArgs e)
{ {
@ -920,7 +951,7 @@ namespace CtrEditor
} }
stopwatch.Stop(); // Stop measuring time stopwatch.Stop(); // Stop measuring time
// Debug.WriteLine($"OnRefreshEvent: {stopwatch.Elapsed.TotalMilliseconds} ms"); // Debug.WriteLine($"OnRefreshEvent: {stopwatch.Elapsed.TotalMilliseconds} ms");
} }
private void OpenWorkDirectory() private void OpenWorkDirectory()

View File

@ -0,0 +1,91 @@
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using CtrEditor.PopUps;
using Xceed.Wpf.Toolkit.PropertyGrid;
using Xceed.Wpf.Toolkit.PropertyGrid.Editors;
namespace CtrEditor.ObjetosSim
{
/// <summary>
/// Atributo para marcar propiedades que deben usar el editor de etiquetas
/// </summary>
public class TagEditorAttribute : Attribute
{
public TagEditorAttribute() { }
}
/// <summary>
/// Editor personalizado para etiquetas en PropertyGrid
/// </summary>
public class TagPropertyEditor : ITypeEditor
{
public FrameworkElement ResolveEditor(PropertyItem propertyItem)
{
var grid = new Grid();
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
// TextBox para mostrar/editar etiquetas directamente
var textBox = new TextBox();
textBox.SetBinding(TextBox.TextProperty, new Binding("Value")
{
Source = propertyItem,
Mode = BindingMode.TwoWay,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
});
Grid.SetColumn(textBox, 0);
// Botón para abrir el editor modal
var button = new Button
{
Content = "...",
Width = 25,
Margin = new Thickness(2, 0, 0, 0),
ToolTip = "Abrir editor de etiquetas"
};
button.Click += (sender, e) =>
{
try
{
// Obtener el objeto que se está editando
if (propertyItem.Instance is osBase osObject)
{
// Obtener el MainWindow para acceder a todos los objetos
var mainWindow = Application.Current.Windows
.OfType<MainWindow>()
.FirstOrDefault();
if (mainWindow?.DataContext is MainViewModel mainViewModel)
{
// Abrir el editor de etiquetas
var tagEditor = new TagEditorWindow(osObject, mainViewModel.ObjetosSimulables);
tagEditor.Owner = mainWindow;
if (tagEditor.ShowDialog() == true)
{
// La ventana ya actualiza el objeto directamente
// Actualizar el textbox con el nuevo valor
textBox.GetBindingExpression(TextBox.TextProperty)?.UpdateTarget();
}
}
}
}
catch (Exception ex)
{
MessageBox.Show($"Error al abrir el editor de etiquetas: {ex.Message}",
"Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
};
Grid.SetColumn(button, 1);
grid.Children.Add(textBox);
grid.Children.Add(button);
return grid;
}
}
}

View File

@ -176,6 +176,52 @@ namespace CtrEditor.ObjetosSim
public virtual void AnguloChanged(float value) { } public virtual void AnguloChanged(float value) { }
public virtual void AnguloChanging(float oldValue, float newValue) { } public virtual void AnguloChanging(float oldValue, float newValue) { }
[ObservableProperty]
[property: Description("Etiquetas para clasificar el objeto. Use # antes de cada etiqueta (ej: #motor #bomba #critico)")]
[property: Category("General:")]
[property: Editor(typeof(TagPropertyEditor), typeof(TagPropertyEditor))]
[property: TagEditor]
private string etiquetas = "";
partial void OnEtiquetasChanged(string value)
{
EtiquetasChanged(value);
// Update the visibility filters when tags change
_mainViewModel?.MainWindow?.VisFilter?.UpdateAvailableTags(_mainViewModel.ObjetosSimulables);
}
public virtual void EtiquetasChanged(string value) { }
/// <summary>
/// Obtiene la lista de etiquetas parseadas desde el string
/// </summary>
[JsonIgnore]
public List<string> ListaEtiquetas
{
get
{
if (string.IsNullOrWhiteSpace(Etiquetas))
return new List<string>();
return Etiquetas
.Split(' ', StringSplitOptions.RemoveEmptyEntries)
.Where(tag => tag.StartsWith("#") && tag.Length > 1)
.Select(tag => tag.Substring(1).ToLowerInvariant())
.Distinct()
.ToList();
}
}
/// <summary>
/// Verifica si el objeto tiene una etiqueta específica
/// </summary>
/// <param name="etiqueta">La etiqueta a buscar (sin el #)</param>
/// <returns>True si el objeto tiene la etiqueta</returns>
public bool TieneEtiqueta(string etiqueta)
{
return ListaEtiquetas.Contains(etiqueta.ToLowerInvariant());
}
public void Resize(double width, double height) public void Resize(double width, double height)
{ {
@ -666,7 +712,8 @@ namespace CtrEditor.ObjetosSim
// Filtrar texto por lista de caracteres permitidos // Filtrar texto por lista de caracteres permitidos
var filteredBlocks = result.TextBlocks var filteredBlocks = result.TextBlocks
.Where(block => block.Score > 0.5) // Solo bloques con confianza > 50% .Where(block => block.Score > 0.5) // Solo bloques con confianza > 50%
.Select(block => new { .Select(block => new
{
Text = new string(block.Text.Where(c => allowedChars.Contains(c)).ToArray()), Text = new string(block.Text.Where(c => allowedChars.Contains(c)).ToArray()),
Score = block.Score Score = block.Score
}) })

111
PopUps/TagEditorWindow.xaml Normal file
View File

@ -0,0 +1,111 @@
<Window x:Class="CtrEditor.PopUps.TagEditorWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="Editor de Etiquetas" Height="400" Width="500"
WindowStartupLocation="CenterOwner" ResizeMode="CanResize">
<Window.Resources>
<Style x:Key="TagButtonStyle" TargetType="Button">
<Setter Property="Margin" Value="2"/>
<Setter Property="Padding" Value="8,4"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="LightGray"/>
<Setter Property="Background" Value="LightBlue"/>
<Setter Property="Foreground" Value="DarkBlue"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Cursor" Value="Hand"/>
</Style>
<Style x:Key="SelectedTagStyle" TargetType="Button" BasedOn="{StaticResource TagButtonStyle}">
<Setter Property="Background" Value="Navy"/>
<Setter Property="Foreground" Value="White"/>
</Style>
<Style x:Key="AvailableTagStyle" TargetType="Button" BasedOn="{StaticResource TagButtonStyle}">
<Setter Property="Background" Value="LightGray"/>
<Setter Property="Foreground" Value="Black"/>
</Style>
</Window.Resources>
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Header -->
<TextBlock Grid.Row="0" Text="Editor de Etiquetas" FontSize="16" FontWeight="Bold" Margin="0,0,0,10"/>
<!-- Nueva etiqueta -->
<Grid Grid.Row="1" Margin="0,0,0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Nueva etiqueta:" VerticalAlignment="Center" Margin="0,0,10,0"/>
<TextBox Grid.Column="1" x:Name="txtNuevaEtiqueta"
Text="{Binding NuevaEtiqueta, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
KeyDown="TxtNuevaEtiqueta_KeyDown"/>
<Button Grid.Column="2" Content="Agregar"
Command="{Binding AgregarEtiquetaCommand}"
Margin="10,0,0,0" MinWidth="70"/>
</Grid>
<!-- Etiquetas del objeto actual -->
<TextBlock Grid.Row="2" Text="Etiquetas del objeto:" FontWeight="Bold" Margin="0,0,0,5"/>
<ScrollViewer Grid.Row="3" VerticalScrollBarVisibility="Auto" MaxHeight="120">
<ItemsControl ItemsSource="{Binding EtiquetasObjeto}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding DisplayName}"
Style="{StaticResource SelectedTagStyle}"
Command="{Binding DataContext.RemoverEtiquetaCommand, RelativeSource={RelativeSource AncestorType=Window}}"
CommandParameter="{Binding TagName}"
ToolTip="Click para remover"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<!-- Etiquetas disponibles -->
<TextBlock Grid.Row="4" Text="Etiquetas disponibles:" FontWeight="Bold" Margin="0,10,0,5"/>
<ScrollViewer Grid.Row="5" VerticalScrollBarVisibility="Auto" MaxHeight="120">
<ItemsControl ItemsSource="{Binding EtiquetasDisponibles}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding DisplayName}"
Style="{StaticResource AvailableTagStyle}"
Command="{Binding DataContext.AgregarEtiquetaExistenteCommand, RelativeSource={RelativeSource AncestorType=Window}}"
CommandParameter="{Binding TagName}"
ToolTip="Click para agregar"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<!-- Botones -->
<StackPanel Grid.Row="6" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,20,0,0">
<Button Content="Aplicar" Command="{Binding AplicarCommand}" Margin="0,0,10,0" MinWidth="70"/>
<Button Content="Cancelar" Command="{Binding CancelarCommand}" MinWidth="70"/>
</StackPanel>
</Grid>
</Window>

View File

@ -0,0 +1,215 @@
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CtrEditor.ObjetosSim;
namespace CtrEditor.PopUps
{
/// <summary>
/// Interaction logic for TagEditorWindow.xaml
/// </summary>
public partial class TagEditorWindow : Window
{
public TagEditorWindow(osBase objeto, IEnumerable<osBase> todosLosObjetos)
{
InitializeComponent();
DataContext = new TagEditorViewModel(objeto, todosLosObjetos, this);
}
private void TxtNuevaEtiqueta_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
if (DataContext is TagEditorViewModel viewModel)
{
viewModel.AgregarEtiquetaCommand.Execute(null);
}
}
}
}
public class TagInfo
{
public string TagName { get; set; }
public string DisplayName => "#" + TagName;
public TagInfo(string tagName)
{
TagName = tagName;
}
}
public partial class TagEditorViewModel : ObservableObject
{
private readonly osBase _objeto;
private readonly IEnumerable<osBase> _todosLosObjetos;
private readonly TagEditorWindow _window;
private readonly List<string> _etiquetasOriginales;
[ObservableProperty]
private string nuevaEtiqueta = "";
[ObservableProperty]
private ObservableCollection<TagInfo> etiquetasObjeto = new();
[ObservableProperty]
private ObservableCollection<TagInfo> etiquetasDisponibles = new();
public ICommand AgregarEtiquetaCommand { get; }
public ICommand AgregarEtiquetaExistenteCommand { get; }
public ICommand RemoverEtiquetaCommand { get; }
public ICommand AplicarCommand { get; }
public ICommand CancelarCommand { get; }
public TagEditorViewModel(osBase objeto, IEnumerable<osBase> todosLosObjetos, TagEditorWindow window)
{
_objeto = objeto;
_todosLosObjetos = todosLosObjetos;
_window = window;
// Guardar las etiquetas originales para poder cancelar
_etiquetasOriginales = objeto.ListaEtiquetas.ToList();
// Inicializar comandos
AgregarEtiquetaCommand = new RelayCommand(AgregarEtiqueta, () => !string.IsNullOrWhiteSpace(NuevaEtiqueta));
AgregarEtiquetaExistenteCommand = new RelayCommand<string>(AgregarEtiquetaExistente);
RemoverEtiquetaCommand = new RelayCommand<string>(RemoverEtiqueta);
AplicarCommand = new RelayCommand(Aplicar);
CancelarCommand = new RelayCommand(Cancelar);
// Cargar datos iniciales
CargarEtiquetasObjeto();
CargarEtiquetasDisponibles();
// Suscribirse al cambio de NuevaEtiqueta para actualizar el comando
PropertyChanged += (s, e) =>
{
if (e.PropertyName == nameof(NuevaEtiqueta))
{
(AgregarEtiquetaCommand as IRelayCommand)?.NotifyCanExecuteChanged();
}
};
}
private void CargarEtiquetasObjeto()
{
EtiquetasObjeto.Clear();
foreach (var tag in _objeto.ListaEtiquetas.OrderBy(t => t))
{
EtiquetasObjeto.Add(new TagInfo(tag));
}
}
private void CargarEtiquetasDisponibles()
{
EtiquetasDisponibles.Clear();
// Obtener todas las etiquetas únicas de todos los objetos
var todasLasEtiquetas = _todosLosObjetos
.SelectMany(obj => obj.ListaEtiquetas)
.Distinct()
.OrderBy(tag => tag)
.ToList();
// Mostrar solo las que no están en el objeto actual
var etiquetasObjetoActual = _objeto.ListaEtiquetas;
foreach (var tag in todasLasEtiquetas.Except(etiquetasObjetoActual))
{
EtiquetasDisponibles.Add(new TagInfo(tag));
}
}
private void AgregarEtiqueta()
{
if (string.IsNullOrWhiteSpace(NuevaEtiqueta))
return;
// Limpiar la etiqueta (remover # si existe y convertir a minúsculas)
string tagLimpia = NuevaEtiqueta.Trim();
if (tagLimpia.StartsWith("#"))
tagLimpia = tagLimpia.Substring(1);
tagLimpia = tagLimpia.ToLowerInvariant();
// Validar que no esté vacía después de limpiar
if (string.IsNullOrWhiteSpace(tagLimpia))
return;
// Verificar que no existe ya
if (_objeto.ListaEtiquetas.Contains(tagLimpia))
return;
// Agregar la etiqueta al objeto
var etiquetasActuales = string.IsNullOrWhiteSpace(_objeto.Etiquetas) ?
new List<string>() :
_objeto.Etiquetas.Split(' ', StringSplitOptions.RemoveEmptyEntries).ToList();
etiquetasActuales.Add("#" + tagLimpia);
_objeto.Etiquetas = string.Join(" ", etiquetasActuales);
// Actualizar las listas
CargarEtiquetasObjeto();
CargarEtiquetasDisponibles();
// Limpiar el campo
NuevaEtiqueta = "";
}
private void AgregarEtiquetaExistente(string tagName)
{
if (string.IsNullOrWhiteSpace(tagName) || _objeto.ListaEtiquetas.Contains(tagName))
return;
// Agregar la etiqueta al objeto
var etiquetasActuales = string.IsNullOrWhiteSpace(_objeto.Etiquetas) ?
new List<string>() :
_objeto.Etiquetas.Split(' ', StringSplitOptions.RemoveEmptyEntries).ToList();
etiquetasActuales.Add("#" + tagName);
_objeto.Etiquetas = string.Join(" ", etiquetasActuales);
// Actualizar las listas
CargarEtiquetasObjeto();
CargarEtiquetasDisponibles();
}
private void RemoverEtiqueta(string tagName)
{
if (string.IsNullOrWhiteSpace(tagName))
return;
// Remover la etiqueta del objeto
var etiquetasActuales = _objeto.Etiquetas
.Split(' ', StringSplitOptions.RemoveEmptyEntries)
.Where(tag => !tag.Equals("#" + tagName, StringComparison.OrdinalIgnoreCase))
.ToList();
_objeto.Etiquetas = string.Join(" ", etiquetasActuales);
// Actualizar las listas
CargarEtiquetasObjeto();
CargarEtiquetasDisponibles();
}
private void Aplicar()
{
_window.DialogResult = true;
_window.Close();
}
private void Cancelar()
{
// Restaurar las etiquetas originales
var etiquetasOriginalesString = _etiquetasOriginales.Any() ?
string.Join(" ", _etiquetasOriginales.Select(tag => "#" + tag)) :
"";
_objeto.Etiquetas = etiquetasOriginalesString;
_window.DialogResult = false;
_window.Close();
}
}
}