2026-02-04 19:48:03 +01:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
|
using System.Threading.Tasks;
|
2026-02-04 22:10:16 +01:00
|
|
|
|
using System.Xml.Serialization;
|
2026-02-04 19:48:03 +01:00
|
|
|
|
using ImageCatalog;
|
2026-02-04 22:10:16 +01:00
|
|
|
|
using ImageCatalog_2.Models;
|
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
2026-02-04 19:48:03 +01:00
|
|
|
|
|
|
|
|
|
|
namespace ImageCatalog_2.Services
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
2026-02-04 22:10:16 +01:00
|
|
|
|
/// Modern settings service that uses DTO with attributes for serialization
|
2026-02-04 19:48:03 +01:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class SettingsService : ISettingsService
|
|
|
|
|
|
{
|
|
|
|
|
|
private readonly ParametriSetup _parametriSetup;
|
2026-02-04 22:10:16 +01:00
|
|
|
|
private readonly ILogger<SettingsService> _logger;
|
2026-02-04 19:48:03 +01:00
|
|
|
|
|
2026-02-04 22:10:16 +01:00
|
|
|
|
public SettingsService(ParametriSetup parametriSetup, ILogger<SettingsService> logger)
|
2026-02-04 19:48:03 +01:00
|
|
|
|
{
|
|
|
|
|
|
_parametriSetup = parametriSetup;
|
2026-02-04 22:10:16 +01:00
|
|
|
|
_logger = logger;
|
2026-02-04 19:48:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public Task SaveSettingsAsync(string filePath, object settings)
|
|
|
|
|
|
{
|
2026-02-14 20:38:51 +01:00
|
|
|
|
// Use a dedicated ParametriSetup instance for the target settings file so the injected
|
|
|
|
|
|
// user preferences store is not modified. This keeps user prefs (singleton) intact.
|
2026-02-04 19:48:03 +01:00
|
|
|
|
return Task.Run(() =>
|
|
|
|
|
|
{
|
2026-02-14 20:38:51 +01:00
|
|
|
|
var fileParams = new ParametriSetup(filePath);
|
|
|
|
|
|
|
2026-02-04 22:10:16 +01:00
|
|
|
|
// Convert ViewModel to DTO
|
|
|
|
|
|
var dto = ViewModelToDto(settings);
|
2026-02-14 20:38:51 +01:00
|
|
|
|
|
2026-02-04 22:10:16 +01:00
|
|
|
|
// Use reflection on DTO properties with XmlElement attribute
|
|
|
|
|
|
var properties = typeof(SettingsDto).GetProperties(BindingFlags.Public | BindingFlags.Instance);
|
2026-02-04 19:48:03 +01:00
|
|
|
|
|
|
|
|
|
|
foreach (var prop in properties)
|
|
|
|
|
|
{
|
2026-02-04 22:10:16 +01:00
|
|
|
|
var xmlAttr = prop.GetCustomAttribute<XmlElementAttribute>();
|
|
|
|
|
|
if (xmlAttr == null)
|
2026-02-14 20:38:51 +01:00
|
|
|
|
continue;
|
|
|
|
|
|
|
2026-02-04 22:10:16 +01:00
|
|
|
|
var xmlName = xmlAttr.ElementName;
|
|
|
|
|
|
var value = prop.GetValue(dto);
|
2026-02-14 20:38:51 +01:00
|
|
|
|
|
|
|
|
|
|
fileParams.AggiornaParametro(xmlName, value);
|
2026-02-04 19:48:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-14 20:38:51 +01:00
|
|
|
|
fileParams.SalvaParametriSetup();
|
2026-02-04 19:48:03 +01:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-04 22:10:16 +01:00
|
|
|
|
public async Task LoadSettingsAsync(string filePath, object settings)
|
2026-02-04 19:48:03 +01:00
|
|
|
|
{
|
2026-02-14 20:38:51 +01:00
|
|
|
|
// Step 1: Load XML into a temporary ParametriSetup so we don't mutate the injected
|
|
|
|
|
|
// user-preferences instance (singleton).
|
2026-02-04 22:10:16 +01:00
|
|
|
|
var dto = await Task.Run(() =>
|
2026-02-04 19:48:03 +01:00
|
|
|
|
{
|
2026-02-14 20:38:51 +01:00
|
|
|
|
var fileParams = new ParametriSetup(filePath);
|
2026-02-04 19:48:03 +01:00
|
|
|
|
|
2026-02-04 22:10:16 +01:00
|
|
|
|
var loadedDto = new SettingsDto();
|
|
|
|
|
|
var properties = typeof(SettingsDto).GetProperties(BindingFlags.Public | BindingFlags.Instance);
|
2026-02-04 19:48:03 +01:00
|
|
|
|
|
|
|
|
|
|
foreach (var prop in properties)
|
|
|
|
|
|
{
|
2026-02-04 22:10:16 +01:00
|
|
|
|
// Get the XmlElement attribute to determine the XML property name
|
|
|
|
|
|
var xmlAttr = prop.GetCustomAttribute<XmlElementAttribute>();
|
|
|
|
|
|
if (xmlAttr == null)
|
|
|
|
|
|
continue; // Skip properties without XmlElement attribute
|
|
|
|
|
|
|
|
|
|
|
|
var xmlName = xmlAttr.ElementName;
|
|
|
|
|
|
|
2026-02-04 19:48:03 +01:00
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
object value;
|
2026-02-16 19:55:37 +01:00
|
|
|
|
if (prop.PropertyType == typeof(string))
|
2026-02-04 19:48:03 +01:00
|
|
|
|
{
|
2026-02-14 20:38:51 +01:00
|
|
|
|
value = fileParams.LeggiParametroString(xmlName);
|
2026-02-04 19:48:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
else if (prop.PropertyType == typeof(bool))
|
|
|
|
|
|
{
|
2026-02-14 20:38:51 +01:00
|
|
|
|
value = fileParams.LeggiParametroBoolean(xmlName);
|
2026-02-04 19:48:03 +01:00
|
|
|
|
}
|
2026-02-16 19:55:37 +01:00
|
|
|
|
else if (prop.PropertyType.IsEnum)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Read enum as string from XML and try to parse to the enum type. Fall back to default.
|
|
|
|
|
|
var str = fileParams.LeggiParametroString(xmlName);
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var enumValue = Enum.Parse(prop.PropertyType, str ?? string.Empty, true);
|
|
|
|
|
|
value = enumValue;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
// try numeric
|
|
|
|
|
|
var intVal = fileParams.LeggiParametro<int>(xmlName, (int)(prop.GetValue(loadedDto) ?? 0));
|
|
|
|
|
|
value = Enum.ToObject(prop.PropertyType, intVal);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-02-04 19:48:03 +01:00
|
|
|
|
else if (prop.PropertyType == typeof(int))
|
|
|
|
|
|
{
|
2026-02-15 11:13:23 +01:00
|
|
|
|
value = fileParams.LeggiParametro<int>(xmlName, (int)(prop.GetValue(loadedDto) ?? 0));
|
2026-02-04 19:48:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
else if (prop.PropertyType == typeof(double))
|
|
|
|
|
|
{
|
2026-02-15 11:13:23 +01:00
|
|
|
|
value = fileParams.LeggiParametro<double>(xmlName, (double)(prop.GetValue(loadedDto) ?? 0.0));
|
2026-02-04 22:10:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
else if (prop.PropertyType == typeof(DateTime))
|
|
|
|
|
|
{
|
2026-02-15 11:13:23 +01:00
|
|
|
|
var strValue = fileParams.LeggiParametroString(xmlName);
|
2026-02-04 22:10:16 +01:00
|
|
|
|
if (DateTime.TryParse(strValue, out var dateValue))
|
|
|
|
|
|
{
|
|
|
|
|
|
value = dateValue;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
value = (DateTime)(prop.GetValue(loadedDto) ?? DateTime.Now);
|
|
|
|
|
|
}
|
2026-02-04 19:48:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-02-04 22:10:16 +01:00
|
|
|
|
continue;
|
2026-02-04 19:48:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-04 22:10:16 +01:00
|
|
|
|
prop.SetValue(loadedDto, value);
|
2026-02-04 19:48:03 +01:00
|
|
|
|
}
|
2026-02-04 21:12:27 +01:00
|
|
|
|
catch (Exception ex)
|
2026-02-04 19:48:03 +01:00
|
|
|
|
{
|
2026-02-04 22:10:16 +01:00
|
|
|
|
_logger.LogError(ex, "Failed to read property {XmlName} -> {PropertyName}", xmlName, prop.Name);
|
2026-02-04 19:48:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-02-04 22:10:16 +01:00
|
|
|
|
|
|
|
|
|
|
return loadedDto;
|
2026-02-16 19:55:37 +01:00
|
|
|
|
// End of LoadSettingsAsync method
|
2026-02-04 19:48:03 +01:00
|
|
|
|
});
|
2026-02-04 22:10:16 +01:00
|
|
|
|
|
|
|
|
|
|
// Step 2: Apply DTO values to ViewModel on UI thread
|
|
|
|
|
|
DtoToViewModel(dto, settings);
|
2026-02-04 19:48:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-04 22:10:16 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Converts DataModel (ViewModel) to SettingsDto
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private SettingsDto ViewModelToDto(object viewModel)
|
2026-02-04 19:48:03 +01:00
|
|
|
|
{
|
2026-02-04 22:10:16 +01:00
|
|
|
|
var vmType = viewModel.GetType();
|
|
|
|
|
|
var dto = new SettingsDto();
|
|
|
|
|
|
var dtoProps = typeof(SettingsDto).GetProperties(BindingFlags.Public | BindingFlags.Instance);
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var dtoProp in dtoProps)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Find matching property in ViewModel by name
|
|
|
|
|
|
var vmProp = vmType.GetProperty(dtoProp.Name, BindingFlags.Public | BindingFlags.Instance);
|
|
|
|
|
|
if (vmProp != null && vmProp.CanRead)
|
|
|
|
|
|
{
|
|
|
|
|
|
var value = vmProp.GetValue(viewModel);
|
|
|
|
|
|
dtoProp.SetValue(dto, value);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return dto;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Copies values from SettingsDto to DataModel (ViewModel)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void DtoToViewModel(SettingsDto dto, object viewModel)
|
|
|
|
|
|
{
|
|
|
|
|
|
var vmType = viewModel.GetType();
|
|
|
|
|
|
var dtoProps = typeof(SettingsDto).GetProperties(BindingFlags.Public | BindingFlags.Instance);
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var dtoProp in dtoProps)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Find matching property in ViewModel by name
|
|
|
|
|
|
var vmProp = vmType.GetProperty(dtoProp.Name, BindingFlags.Public | BindingFlags.Instance);
|
|
|
|
|
|
if (vmProp != null && vmProp.CanWrite)
|
|
|
|
|
|
{
|
|
|
|
|
|
var value = dtoProp.GetValue(dto);
|
|
|
|
|
|
|
|
|
|
|
|
// Don't update if empty string and current value is not empty
|
|
|
|
|
|
if (dtoProp.PropertyType == typeof(string) && string.IsNullOrEmpty((string)value))
|
|
|
|
|
|
{
|
|
|
|
|
|
var currentValue = vmProp.GetValue(viewModel) as string;
|
|
|
|
|
|
if (!string.IsNullOrEmpty(currentValue))
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
vmProp.SetValue(viewModel, value);
|
|
|
|
|
|
_logger.LogDebug("Set {PropertyName} = {Value}", vmProp.Name, value);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-02-04 19:48:03 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|