Se añadió un estilo global para TreeViewItem en App.xaml para evitar errores de binding. Se actualizaron las referencias de paquetes en CtrEditor.csproj, cambiando la versión de LiveChartsCore.SkiaSharpView.WPF y añadiendo SkiaSharp.Views.WPF. Se mejoró la gestión de carga de imágenes en osBase.cs y ucCustomImage.xaml.cs, implementando un manejo de errores más robusto y estableciendo imágenes por defecto en caso de fallos. Se ajustó el XAML de ucBoolTag para mejorar la conversión de color. Se implementó un convertidor seguro para ImageSource en StateSerializer.cs, mejorando la deserialización de imágenes.

This commit is contained in:
Miguel 2025-06-24 10:59:58 +02:00
parent 3af9ad99d8
commit 5c2daaeb98
6 changed files with 197 additions and 21 deletions

View File

@ -22,6 +22,12 @@
<local:SubclassFilterConverter x:Key="SubclassFilterConverterosVMMotor" TargetType="{x:Type os:osVMmotorSim}" />
<local:UnsavedChangesConverter x:Key="UnsavedChangesConverter"/>
<local:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<!-- Estilo global para TreeViewItem para evitar errores de binding -->
<Style TargetType="TreeViewItem">
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
</Style>
</Application.Resources>
</Application>

View File

@ -85,8 +85,9 @@
<PackageReference Include="Emgu.CV.UI" Version="4.9.0.5494" />
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.7.25104.5739" />
<PackageReference Include="LanguageDetection" Version="1.2.0" />
<PackageReference Include="LiveChartsCore.SkiaSharpView.WPF" Version="2.0.0-rc4.5" />
<PackageReference Include="LiveChartsCore.SkiaSharpView.WPF" Version="2.0.0-rc3.3" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.135" />
<PackageReference Include="SkiaSharp.Views.WPF" Version="2.88.8" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Ookii.Dialogs.Wpf" Version="5.0.1" />
<PackageReference Include="PaddleOCRSharp" Version="4.5.0.1" />

View File

@ -1,7 +1,7 @@
using CommunityToolkit.Mvvm.ComponentModel;
using LibS7Adv;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls;
using System.Windows.Media;
using CtrEditor.FuncionesBase;
using System.ComponentModel;
@ -27,11 +27,42 @@ namespace CtrEditor.ObjetosSim
}
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(ImageSource_oculta))]
[property: Description("Path to the image file")]
[property: Category("Image:")]
private string imagePath;
private string _imagePath;
[Description("Path to the image file")]
[Category("Image:")]
public string ImagePath
{
get => _imagePath;
set
{
try
{
if (SetProperty(ref _imagePath, value))
{
OnImagePathChanged(value);
OnPropertyChanged(nameof(ImageSource_oculta));
}
}
catch
{
// Si hay error al establecer la propiedad, simplemente ignorarlo
// y establecer la imagen por defecto
try
{
SetProperty(ref _imagePath, value);
ImageSource_oculta = ImageFromPath("/Icons/unselect.png");
OnPropertyChanged(nameof(ImageSource_oculta));
}
catch
{
// En caso de error total, establecer valores seguros
_imagePath = value;
ImageSource_oculta = null;
}
}
}
}
[ObservableProperty]
[property: Description("Flip the image horizontally")]
@ -48,16 +79,32 @@ namespace CtrEditor.ObjetosSim
[ObservableProperty]
public ImageSource imageSource_oculta;
partial void OnImagePathChanged(string value)
private void OnImagePathChanged(string value)
{
if (!string.IsNullOrEmpty(value))
try
{
ImageSource_oculta = ImageFromPath(value);
if (!string.IsNullOrEmpty(value))
{
ImageSource_oculta = ImageFromPath(value);
}
else
{
// Si no hay path, usar la imagen por defecto
ImageSource_oculta = ImageFromPath("/Icons/unselect.png");
}
}
else
catch
{
// Si no hay path, usar la imagen por defecto
ImageSource_oculta = ImageFromPath("/Icons/unselect.png");
// Si hay cualquier error, usar la imagen por defecto
try
{
ImageSource_oculta = ImageFromPath("/Icons/unselect.png");
}
catch
{
// Si incluso la imagen por defecto falla, establecer como null
ImageSource_oculta = null;
}
}
}
@ -82,14 +129,30 @@ namespace CtrEditor.ObjetosSim
public override void ucLoaded()
{
base.ucLoaded();
if (!string.IsNullOrEmpty(ImagePath))
try
{
ImageSource_oculta = ImageFromPath(ImagePath);
if (!string.IsNullOrEmpty(ImagePath))
{
ImageSource_oculta = ImageFromPath(ImagePath);
}
else
{
// Si no hay path al cargar, usar la imagen por defecto
ImageSource_oculta = ImageFromPath("/Icons/unselect.png");
}
}
else
catch
{
// Si no hay path al cargar, usar la imagen por defecto
ImageSource_oculta = ImageFromPath("/Icons/unselect.png");
// Si hay cualquier error, usar la imagen por defecto
try
{
ImageSource_oculta = ImageFromPath("/Icons/unselect.png");
}
catch
{
// Si incluso la imagen por defecto falla, establecer como null
ImageSource_oculta = null;
}
}
}
}

View File

@ -27,7 +27,7 @@
Width="16" Height="25" VerticalAlignment="Center" HorizontalAlignment="Center"
Background="{Binding Color, Converter={StaticResource ColorToBrushConverter}}" />
<Label Content="{Binding Descripcion}" Grid.Column="1" VerticalAlignment="Center"
Background="{Binding Color}"
Background="{Binding Color, Converter={StaticResource ColorToBrushConverter}}"
Visibility="{Binding Show_Description, Converter={StaticResource BooleanToVisibilityConverter}}" />
<CheckBox Grid.Column="2" VerticalAlignment="Center" IsChecked="{Binding Estado}"
Background="{Binding Color_oculto}" />

View File

@ -1642,7 +1642,31 @@ namespace CtrEditor.ObjetosSim
{
if (value is string stringValue)
{
return new BitmapImage(new Uri(stringValue, UriKind.RelativeOrAbsolute));
try
{
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.UriSource = new Uri(stringValue, UriKind.RelativeOrAbsolute);
bitmapImage.EndInit();
return bitmapImage;
}
catch
{
// Si falla la carga de la imagen, usar imagen por defecto como fallback
try
{
var defaultImage = new BitmapImage();
defaultImage.BeginInit();
defaultImage.UriSource = new Uri("/Icons/unselect.png", UriKind.RelativeOrAbsolute);
defaultImage.EndInit();
return defaultImage;
}
catch
{
// Si incluso la imagen por defecto falla, devolver null
return null;
}
}
}
return null;
}

View File

@ -6,6 +6,7 @@ using System.Windows;
using CtrEditor.Simulacion;
using System.IO;
using System.Linq;
using System.Windows.Media;
namespace CtrEditor.Serialization
{
@ -254,8 +255,89 @@ namespace CtrEditor.Serialization
NullValueHandling = NullValueHandling.Ignore,
TypeNameHandling = TypeNameHandling.Auto,
ObjectCreationHandling = ObjectCreationHandling.Replace,
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
Error = HandleDeserializationError,
Converters = { new SafeImageSourceConverter() }
};
}
private void HandleDeserializationError(object sender, Newtonsoft.Json.Serialization.ErrorEventArgs e)
{
// Si es un error relacionado con ImagePath, ImageSource o osCustomImage, simplemente ignorarlo y continuar
if (e.ErrorContext.Error.Message.Contains("ImagePath") ||
e.ErrorContext.Error.Message.Contains("osCustomImage") ||
e.ErrorContext.Error.Message.Contains("ImageSource") ||
e.ErrorContext.Error.Message.Contains("lmageSource") || // posible typo en el mensaje de error
(e.ErrorContext.Path != null && e.ErrorContext.Path.Contains("ImageSource")) ||
(e.ErrorContext.Path != null && e.ErrorContext.Path.Contains("imagePath")) ||
(e.ErrorContext.Member != null && e.ErrorContext.Member.ToString().Contains("Image")))
{
e.ErrorContext.Handled = true;
// Si el contexto actual es un objeto osCustomImage y hay un error,
// intentar establecer valores por defecto
if (e.ErrorContext.OriginalObject is osCustomImage customImage)
{
try
{
// Establecer valores seguros por defecto
if (e.ErrorContext.Member?.ToString() == "ImagePath" ||
e.ErrorContext.Member?.ToString() == "imagePath")
{
customImage.ImagePath = ""; // Esto activará el fallback a imagen por defecto
}
}
catch
{
// Si incluso esto falla, simplemente ignorar
}
}
}
}
}
/// <summary>
/// Convertidor JSON seguro para ImageSource que nunca lanza excepciones
/// </summary>
public class SafeImageSourceConverter : JsonConverter<ImageSource>
{
public override ImageSource ReadJson(JsonReader reader, Type objectType, ImageSource existingValue, bool hasExistingValue, JsonSerializer serializer)
{
try
{
// Si el valor es null o string vacío, devolver null
if (reader.Value == null || reader.Value.ToString() == "")
return null;
// Intentar crear ImageSource desde el valor
string value = reader.Value.ToString();
// Limpiar caracteres problemáticos
value = value.Trim();
if (value.Contains("•"))
value = value.Replace("•", "");
if (value.StartsWith("filet///"))
value = value.Replace("filet///", "file:///");
// Si el valor limpio está vacío, devolver null
if (string.IsNullOrWhiteSpace(value))
return null;
// No intentar crear ImageSource aquí, simplemente devolver null
// La lógica de carga de imagen se manejará en el código de la aplicación
return null;
}
catch
{
// En caso de cualquier error, devolver null silenciosamente
return null;
}
}
public override void WriteJson(JsonWriter writer, ImageSource value, JsonSerializer serializer)
{
// Nunca serializar ImageSource, siempre escribir null
writer.WriteNull();
}
}
}