From 5cb491f1b58696f39bd9bacbfd9b2eaef52dca73 Mon Sep 17 00:00:00 2001 From: MaddoScientisto Date: Sat, 14 Feb 2026 20:38:51 +0100 Subject: [PATCH] Persist last-used dialog folders in user preferences Dialogs now remember last-used folders for source, destination, logo, and settings files by storing these paths in a user preferences file under LocalApplicationData. Preferences are saved on form close, reducing unnecessary writes. SettingsService now uses a temporary ParametriSetup for settings files to avoid polluting user preferences. Error handling ensures preference save failures do not disrupt the user. This separation improves user experience and keeps user preferences distinct from project settings. --- imagecatalog/MainForm.cs | 98 +++++++++++++++++++++++- imagecatalog/Program.cs | 5 +- imagecatalog/Services/SettingsService.cs | 29 +++---- 3 files changed, 113 insertions(+), 19 deletions(-) diff --git a/imagecatalog/MainForm.cs b/imagecatalog/MainForm.cs index d31a157..43940cb 100644 --- a/imagecatalog/MainForm.cs +++ b/imagecatalog/MainForm.cs @@ -48,6 +48,9 @@ public partial class MainForm BindControls(); + // Save user preferences on form close instead of immediately when dialogs are used + this.FormClosing += MainForm_FormClosing; + // Wire up 'Open folder in Explorer' buttons btnOpenSourceFolder.Click += BtnOpenSourceFolder_Click; btnOpenDestFolder.Click += BtnOpenDestFolder_Click; @@ -194,10 +197,17 @@ public partial class MainForm private void OnSelectSourceFolderRequested(object sender, EventArgs e) { - var dialogResult = SelectFolder(Model.SourcePath); + // Prefer model value; if empty fall back to last-used value stored in user prefs + var starting = !string.IsNullOrWhiteSpace(Model.SourcePath) + ? Model.SourcePath + : _parametriSetup.LeggiParametroString("LastSourceFolder"); + + var dialogResult = SelectFolder(starting); if (!string.IsNullOrWhiteSpace(dialogResult)) { Model.SourcePath = dialogResult; + _parametriSetup.AggiornaParametro("LastSourceFolder", dialogResult); + _parametriSetup.SalvaParametriSetup(); } } @@ -254,10 +264,16 @@ public partial class MainForm private void OnSelectDestinationFolderRequested(object sender, EventArgs e) { - var dialogResult = SelectFolder(Model.DestinationPath); + var starting = !string.IsNullOrWhiteSpace(Model.DestinationPath) + ? Model.DestinationPath + : _parametriSetup.LeggiParametroString("LastDestinationFolder"); + + var dialogResult = SelectFolder(starting); if (!string.IsNullOrWhiteSpace(dialogResult)) { Model.DestinationPath = dialogResult; + _parametriSetup.AggiornaParametro("LastDestinationFolder", dialogResult); + _parametriSetup.SalvaParametriSetup(); } } @@ -265,15 +281,36 @@ public partial class MainForm { var dialog = new OpenFileDialog(); dialog.Filter = "Image Files|*.jpg;*.jpeg;*.png;*.bmp;*.gif"; - if (Model.LogoFile.Length > 0) + if (!string.IsNullOrWhiteSpace(Model.LogoFile)) { dialog.FileName = Model.LogoFile; } + else + { + var lastLogoFolder = _parametriSetup.LeggiParametroString("LastLogoFolder"); + if (!string.IsNullOrWhiteSpace(lastLogoFolder) && Directory.Exists(lastLogoFolder)) + { + dialog.InitialDirectory = lastLogoFolder; + } + } if (dialog.ShowDialog() == DialogResult.OK) { Model.LogoFile = dialog.FileName; UpdateLogoPictureBox(Model.LogoFile); + try + { + var folder = Path.GetDirectoryName(dialog.FileName) ?? string.Empty; + if (!string.IsNullOrWhiteSpace(folder)) + { + _parametriSetup.AggiornaParametro("LastLogoFolder", folder); + _parametriSetup.SalvaParametriSetup(); + } + } + catch + { + // ignore preferences save failures + } } } @@ -285,11 +322,28 @@ public partial class MainForm FilterIndex = 0, RestoreDirectory = true }; - + var lastSettings = _parametriSetup.LeggiParametroString("LastSettingsFolder"); + if (!string.IsNullOrWhiteSpace(lastSettings) && Directory.Exists(lastSettings)) + saveDialog.InitialDirectory = lastSettings; + if (saveDialog.ShowDialog() != DialogResult.OK) return; await Model.SaveSettingsToFileAsync(saveDialog.FileName); Text = "Image Catalog - " + Path.GetFileName(saveDialog.FileName); + + try + { + var folder = Path.GetDirectoryName(saveDialog.FileName) ?? string.Empty; + if (!string.IsNullOrWhiteSpace(folder)) + { + _parametriSetup.AggiornaParametro("LastSettingsFolder", folder); + _parametriSetup.SalvaParametriSetup(); + } + } + catch + { + // ignore + } } private async void OnLoadSettingsRequested(object sender, string e) @@ -301,6 +355,10 @@ public partial class MainForm RestoreDirectory = true }; + var lastSettings = _parametriSetup.LeggiParametroString("LastSettingsFolder"); + if (!string.IsNullOrWhiteSpace(lastSettings) && Directory.Exists(lastSettings)) + openDialog.InitialDirectory = lastSettings; + if (openDialog.ShowDialog() != DialogResult.OK) return; try @@ -318,6 +376,20 @@ public partial class MainForm Text = "Image Catalog - " + Path.GetFileName(openDialog.FileName); + try + { + var folder = Path.GetDirectoryName(openDialog.FileName) ?? string.Empty; + if (!string.IsNullOrWhiteSpace(folder)) + { + _parametriSetup.AggiornaParametro("LastSettingsFolder", folder); + _parametriSetup.SalvaParametriSetup(); + } + } + catch + { + // ignore preferences save failures + } + _logger.LogInformation($"Settings loaded successfully from {openDialog.FileName}"); } catch (Exception ex) @@ -327,6 +399,24 @@ public partial class MainForm } } + private void MainForm_FormClosing(object? sender, FormClosingEventArgs e) + { + try + { + // Persist last-used dialogs paths (user preferences) + // These keys are managed independently from settings files + // and must be saved when the form closes. + _parametriSetup.AggiornaParametro("LastSourceFolder", Model.SourcePath ?? string.Empty); + _parametriSetup.AggiornaParametro("LastDestinationFolder", Model.DestinationPath ?? string.Empty); + _parametriSetup.AggiornaParametro("LastLogoFolder", Path.GetDirectoryName(Model.LogoFile ?? string.Empty) ?? string.Empty); + _parametriSetup.SalvaParametriSetup(); + } + catch (Exception ex) + { + _logger?.LogWarning(ex, "Failed to save user preferences on exit"); + } + } + private void OnSelectColorRequested(object sender, EventArgs e) { var colorDialog = new ColorDialog diff --git a/imagecatalog/Program.cs b/imagecatalog/Program.cs index 2a5e2b2..ea8f2aa 100644 --- a/imagecatalog/Program.cs +++ b/imagecatalog/Program.cs @@ -92,7 +92,10 @@ static class Program services.AddTransient(); services.AddTransient(); - services.AddSingleton(); + // Register a ParametriSetup singleton that persists user preferences in LocalApplicationData + var userPrefsPath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "ImageCatalog", "userprefs.xml"); + services.AddSingleton(new ParametriSetup(userPrefsPath)); services.AddSingleton(); // Register your forms diff --git a/imagecatalog/Services/SettingsService.cs b/imagecatalog/Services/SettingsService.cs index 3b0a630..8a92333 100644 --- a/imagecatalog/Services/SettingsService.cs +++ b/imagecatalog/Services/SettingsService.cs @@ -24,40 +24,41 @@ namespace ImageCatalog_2.Services public Task SaveSettingsAsync(string filePath, object settings) { + // 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. return Task.Run(() => { - _parametriSetup.NomeFileSetup = filePath; - + var fileParams = new ParametriSetup(filePath); + // Convert ViewModel to DTO var dto = ViewModelToDto(settings); - + // Use reflection on DTO properties with XmlElement attribute var properties = typeof(SettingsDto).GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (var prop in properties) { - // Get the XmlElement attribute to determine the XML property name var xmlAttr = prop.GetCustomAttribute(); if (xmlAttr == null) - continue; // Skip properties without XmlElement attribute - + continue; + var xmlName = xmlAttr.ElementName; var value = prop.GetValue(dto); - - _parametriSetup.AggiornaParametro(xmlName, value); + + fileParams.AggiornaParametro(xmlName, value); } - _parametriSetup.SalvaParametriSetup(); + fileParams.SalvaParametriSetup(); }); } public async Task LoadSettingsAsync(string filePath, object settings) { - // Step 1: Load XML and read into DTO on background thread + // Step 1: Load XML into a temporary ParametriSetup so we don't mutate the injected + // user-preferences instance (singleton). var dto = await Task.Run(() => { - _parametriSetup.NomeFileSetup = filePath; - _parametriSetup.CaricaParametriSetup(); + var fileParams = new ParametriSetup(filePath); var loadedDto = new SettingsDto(); var properties = typeof(SettingsDto).GetProperties(BindingFlags.Public | BindingFlags.Instance); @@ -76,11 +77,11 @@ namespace ImageCatalog_2.Services object value; if (prop.PropertyType == typeof(string)) { - value = _parametriSetup.LeggiParametroString(xmlName); + value = fileParams.LeggiParametroString(xmlName); } else if (prop.PropertyType == typeof(bool)) { - value = _parametriSetup.LeggiParametroBoolean(xmlName); + value = fileParams.LeggiParametroBoolean(xmlName); } else if (prop.PropertyType == typeof(int)) {