From 5910e3812b071f452b6530c3a476a11411e957f2 Mon Sep 17 00:00:00 2001 From: Michael Babienco Date: Mon, 16 Feb 2026 18:13:14 +0900 Subject: [PATCH] Save/load settings, Move about window to dialog --- App.axaml | 3 ++ App.axaml.cs | 4 +- Models/ReportFile.cs | 3 ++ Models/Settings.cs | 86 ++++++++++++++++++++++++++++++++++++ ViewModels/AboutViewModel.cs | 33 ++++++++++++++ ViewModels/MainViewModel.cs | 43 ++++++++++++------ Views/AboutView.axaml | 23 ++++++++++ Views/AboutView.axaml.cs | 15 +++++++ Views/AboutWindow.axaml | 16 ------- Views/AboutWindow.axaml.cs | 13 ------ 10 files changed, 195 insertions(+), 44 deletions(-) create mode 100644 Models/Settings.cs create mode 100644 ViewModels/AboutViewModel.cs create mode 100644 Views/AboutView.axaml create mode 100644 Views/AboutView.axaml.cs delete mode 100644 Views/AboutWindow.axaml delete mode 100644 Views/AboutWindow.axaml.cs diff --git a/App.axaml b/App.axaml index c800ae1..3f67de4 100644 --- a/App.axaml +++ b/App.axaml @@ -94,6 +94,9 @@ + + + diff --git a/App.axaml.cs b/App.axaml.cs index b27b8f8..90042c9 100644 --- a/App.axaml.cs +++ b/App.axaml.cs @@ -3,7 +3,9 @@ using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Interactivity; using Avalonia.Markup.Xaml; +using DialogHostAvalonia; using ReceiptPDFBuilder; +using ReceiptPDFBuilder.ViewModels; namespace ReceiptPDFBuilder; @@ -26,6 +28,6 @@ public partial class App : Application public void AboutOnClick(object? sender, EventArgs args) { - new AboutWindow().Show(); + DialogHost.Show(new AboutViewModel()); } } \ No newline at end of file diff --git a/Models/ReportFile.cs b/Models/ReportFile.cs index 051b38e..00fe9b3 100644 --- a/Models/ReportFile.cs +++ b/Models/ReportFile.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Text.Json.Serialization; using ReceiptPDFBuilder.Helpers; namespace ReceiptPDFBuilder.Models; @@ -44,6 +45,7 @@ class ReportFile : ChangeNotifier } } + [JsonIgnore] public DateOnly ReceiptDate { get => DateOnly.FromDateTime(_receiptDateTime); @@ -61,6 +63,7 @@ class ReportFile : ChangeNotifier set { _filePath = value; NotifyPropertyChanged(); } } + [JsonIgnore] public string FileName { get => Path.GetFileName(_filePath); diff --git a/Models/Settings.cs b/Models/Settings.cs new file mode 100644 index 0000000..09df28f --- /dev/null +++ b/Models/Settings.cs @@ -0,0 +1,86 @@ +using System; +using System.IO; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using ReceiptPDFBuilder.Helpers; + +namespace ReceiptPDFBuilder.Models; + +class Settings : ChangeNotifier +{ + private string _lastUsedPath; + + public Settings() + { + _lastUsedPath = ""; + } + + [JsonInclude] + public string LastUsedPath + { + get => _lastUsedPath; + set { _lastUsedPath = value; NotifyPropertyChanged(); } + } + + public static string GetSettingsFileName() + { + return "settings.json"; + } + + public static string GetSettingsPath() + { + var path = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "ReceiptPDFBuilder" + ); + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + return Path.Combine(path, GetSettingsFileName()); + } + + private static JsonSerializerOptions GetSerializerOptions() + { + var opts = new JsonSerializerOptions + { + WriteIndented = false, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + }; + return opts; + } + + public async Task SaveSettingsAsync() + { + var jsonContext = new SourceGenerationContext(GetSerializerOptions()); + using MemoryStream memoryStream = new MemoryStream(); + await JsonSerializer.SerializeAsync(memoryStream, this, jsonContext.Settings); + memoryStream.Position = 0; + using var reader = new StreamReader(memoryStream); + var json = await reader.ReadToEndAsync(); + await File.WriteAllTextAsync(GetSettingsPath(), json); + return json; + } + + public static Settings LoadSettings() + { + var path = GetSettingsPath(); + if (!File.Exists(path)) + { + return new Settings(); + } + var json = File.ReadAllText(GetSettingsPath()); + var jsonContext = new SourceGenerationContext(GetSerializerOptions()); + return JsonSerializer.Deserialize(json, jsonContext.Settings) ?? new Settings(); + } + + public static async Task LoadSettingsAsync() + { + using FileStream fileStream = File.OpenRead(GetSettingsPath()); + var jsonContext = new SourceGenerationContext(GetSerializerOptions()); + var output = await JsonSerializer.DeserializeAsync(fileStream, jsonContext.Settings) ?? new Settings(); + return output; + } +} \ No newline at end of file diff --git a/ViewModels/AboutViewModel.cs b/ViewModels/AboutViewModel.cs new file mode 100644 index 0000000..8f9404f --- /dev/null +++ b/ViewModels/AboutViewModel.cs @@ -0,0 +1,33 @@ +#nullable enable + +using System; +using System.Collections.ObjectModel; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Avalonia.Platform.Storage; +using Avalonia.Themes.Fluent; +using DialogHostAvalonia; +using ImageMagick; +using MigraDoc.DocumentObjectModel; +using MigraDoc.Rendering; +using PdfSharp.Fonts; +using PdfSharp.Pdf.IO; +using PdfSharp.Snippets.Font; +using ReceiptPDFBuilder.Interfaces; +using ReceiptPDFBuilder.Models; + +namespace ReceiptPDFBuilder.ViewModels; + +class AboutViewModel +{ + public AboutViewModel() + { + } + + public void Close() + { + DialogHost.Close("DialogHost", null); + } +} \ No newline at end of file diff --git a/ViewModels/MainViewModel.cs b/ViewModels/MainViewModel.cs index c6f2119..e95676f 100644 --- a/ViewModels/MainViewModel.cs +++ b/ViewModels/MainViewModel.cs @@ -30,6 +30,8 @@ class MainViewModel : BaseViewModel, IFontResolver private string _reportTitle; private ObservableCollection _reportFiles; + private Settings _settings; + public MainViewModel(IChangeViewModel viewModelChanger) : base(viewModelChanger) { _baseDir = Path.GetDirectoryName(Environment.ProcessPath) ?? ""; @@ -39,6 +41,12 @@ class MainViewModel : BaseViewModel, IFontResolver _reportFiles = new ObservableCollection(); _reportFiles.CollectionChanged += ( sender, e ) => { NotifyPropertyChanged(nameof(IsCreatePDFButtonEnabled)); }; _reportTitle = ""; + _settings = Settings.LoadSettings(); + if (!string.IsNullOrWhiteSpace(_settings.LastUsedPath)) + { + LogInfo("Loading data at last used path of {0}", _settings.LastUsedPath); + ScanFolder(_settings.LastUsedPath); + } } public string ReportTitle @@ -95,20 +103,27 @@ class MainViewModel : BaseViewModel, IFontResolver if (folders.Count == 1) { var folder = folders[0]; - LogInfo("Chosen folder: " + folder.Path.LocalPath); - if (Directory.Exists(folder.Path.LocalPath)) - { - _workingFolder = folder.Path.LocalPath; - NotifyPropertyChanged(nameof(IsTitleBoxVisible)); - // TODO: Scan folder for saved info from previous reports and reload that first - // Scan folder for files and display in DataGrid - var filePaths = Directory.GetFiles(_workingFolder); - filePaths.Sort(); - foreach (var filePath in filePaths) - { - AddFileBasedOnPath(filePath); - } - } + LogInfo("Loading items in folder: " + folder.Path.LocalPath); + ScanFolder(folder.Path.LocalPath); + _settings.LastUsedPath = folder.Path.LocalPath; + await _settings.SaveSettingsAsync(); + } + } + } + + private void ScanFolder(string path) + { + if (Directory.Exists(path)) + { + _workingFolder = path; + NotifyPropertyChanged(nameof(IsTitleBoxVisible)); + // TODO: Scan folder for saved info from previous reports and reload that first + // Scan folder for files and display in DataGrid + var filePaths = Directory.GetFiles(_workingFolder); + filePaths.Sort(); + foreach (var filePath in filePaths) + { + AddFileBasedOnPath(filePath); } } } diff --git a/Views/AboutView.axaml b/Views/AboutView.axaml new file mode 100644 index 0000000..90d13af --- /dev/null +++ b/Views/AboutView.axaml @@ -0,0 +1,23 @@ + + + +