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.
This commit is contained in:
MaddoScientisto 2026-02-14 20:38:51 +01:00
commit 5cb491f1b5
3 changed files with 113 additions and 19 deletions

View file

@ -48,6 +48,9 @@ public partial class MainForm
BindControls(); 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 // Wire up 'Open folder in Explorer' buttons
btnOpenSourceFolder.Click += BtnOpenSourceFolder_Click; btnOpenSourceFolder.Click += BtnOpenSourceFolder_Click;
btnOpenDestFolder.Click += BtnOpenDestFolder_Click; btnOpenDestFolder.Click += BtnOpenDestFolder_Click;
@ -194,10 +197,17 @@ public partial class MainForm
private void OnSelectSourceFolderRequested(object sender, EventArgs e) 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)) if (!string.IsNullOrWhiteSpace(dialogResult))
{ {
Model.SourcePath = 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) 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)) if (!string.IsNullOrWhiteSpace(dialogResult))
{ {
Model.DestinationPath = dialogResult; Model.DestinationPath = dialogResult;
_parametriSetup.AggiornaParametro("LastDestinationFolder", dialogResult);
_parametriSetup.SalvaParametriSetup();
} }
} }
@ -265,15 +281,36 @@ public partial class MainForm
{ {
var dialog = new OpenFileDialog(); var dialog = new OpenFileDialog();
dialog.Filter = "Image Files|*.jpg;*.jpeg;*.png;*.bmp;*.gif"; dialog.Filter = "Image Files|*.jpg;*.jpeg;*.png;*.bmp;*.gif";
if (Model.LogoFile.Length > 0) if (!string.IsNullOrWhiteSpace(Model.LogoFile))
{ {
dialog.FileName = 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) if (dialog.ShowDialog() == DialogResult.OK)
{ {
Model.LogoFile = dialog.FileName; Model.LogoFile = dialog.FileName;
UpdateLogoPictureBox(Model.LogoFile); 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, FilterIndex = 0,
RestoreDirectory = true RestoreDirectory = true
}; };
var lastSettings = _parametriSetup.LeggiParametroString("LastSettingsFolder");
if (!string.IsNullOrWhiteSpace(lastSettings) && Directory.Exists(lastSettings))
saveDialog.InitialDirectory = lastSettings;
if (saveDialog.ShowDialog() != DialogResult.OK) return; if (saveDialog.ShowDialog() != DialogResult.OK) return;
await Model.SaveSettingsToFileAsync(saveDialog.FileName); await Model.SaveSettingsToFileAsync(saveDialog.FileName);
Text = "Image Catalog - " + Path.GetFileName(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) private async void OnLoadSettingsRequested(object sender, string e)
@ -301,6 +355,10 @@ public partial class MainForm
RestoreDirectory = true RestoreDirectory = true
}; };
var lastSettings = _parametriSetup.LeggiParametroString("LastSettingsFolder");
if (!string.IsNullOrWhiteSpace(lastSettings) && Directory.Exists(lastSettings))
openDialog.InitialDirectory = lastSettings;
if (openDialog.ShowDialog() != DialogResult.OK) return; if (openDialog.ShowDialog() != DialogResult.OK) return;
try try
@ -318,6 +376,20 @@ public partial class MainForm
Text = "Image Catalog - " + Path.GetFileName(openDialog.FileName); 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}"); _logger.LogInformation($"Settings loaded successfully from {openDialog.FileName}");
} }
catch (Exception ex) 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) private void OnSelectColorRequested(object sender, EventArgs e)
{ {
var colorDialog = new ColorDialog var colorDialog = new ColorDialog

View file

@ -92,7 +92,10 @@ static class Program
services.AddTransient<ImageCreationStuff>(); services.AddTransient<ImageCreationStuff>();
services.AddTransient<ImageCreatorSharp>(); services.AddTransient<ImageCreatorSharp>();
services.AddSingleton<ParametriSetup>(); // 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<PicSettings>(); services.AddSingleton<PicSettings>();
// Register your forms // Register your forms

View file

@ -24,9 +24,11 @@ namespace ImageCatalog_2.Services
public Task SaveSettingsAsync(string filePath, object settings) 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(() => return Task.Run(() =>
{ {
_parametriSetup.NomeFileSetup = filePath; var fileParams = new ParametriSetup(filePath);
// Convert ViewModel to DTO // Convert ViewModel to DTO
var dto = ViewModelToDto(settings); var dto = ViewModelToDto(settings);
@ -36,28 +38,27 @@ namespace ImageCatalog_2.Services
foreach (var prop in properties) foreach (var prop in properties)
{ {
// Get the XmlElement attribute to determine the XML property name
var xmlAttr = prop.GetCustomAttribute<XmlElementAttribute>(); var xmlAttr = prop.GetCustomAttribute<XmlElementAttribute>();
if (xmlAttr == null) if (xmlAttr == null)
continue; // Skip properties without XmlElement attribute continue;
var xmlName = xmlAttr.ElementName; var xmlName = xmlAttr.ElementName;
var value = prop.GetValue(dto); 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) 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(() => var dto = await Task.Run(() =>
{ {
_parametriSetup.NomeFileSetup = filePath; var fileParams = new ParametriSetup(filePath);
_parametriSetup.CaricaParametriSetup();
var loadedDto = new SettingsDto(); var loadedDto = new SettingsDto();
var properties = typeof(SettingsDto).GetProperties(BindingFlags.Public | BindingFlags.Instance); var properties = typeof(SettingsDto).GetProperties(BindingFlags.Public | BindingFlags.Instance);
@ -76,11 +77,11 @@ namespace ImageCatalog_2.Services
object value; object value;
if (prop.PropertyType == typeof(string)) if (prop.PropertyType == typeof(string))
{ {
value = _parametriSetup.LeggiParametroString(xmlName); value = fileParams.LeggiParametroString(xmlName);
} }
else if (prop.PropertyType == typeof(bool)) else if (prop.PropertyType == typeof(bool))
{ {
value = _parametriSetup.LeggiParametroBoolean(xmlName); value = fileParams.LeggiParametroBoolean(xmlName);
} }
else if (prop.PropertyType == typeof(int)) else if (prop.PropertyType == typeof(int))
{ {