Save/load settings, Move about window to dialog

This commit is contained in:
2026-02-16 18:13:14 +09:00
parent 016ecf0a30
commit 5910e3812b
10 changed files with 195 additions and 44 deletions
+3
View File
@@ -94,6 +94,9 @@
<DataTemplate DataType="{x:Type viewModels:EditFileViewModel}"> <DataTemplate DataType="{x:Type viewModels:EditFileViewModel}">
<views:EditFile/> <views:EditFile/>
</DataTemplate> </DataTemplate>
<DataTemplate DataType="{x:Type viewModels:AboutViewModel}">
<views:AboutView/>
</DataTemplate>
</Application.DataTemplates> </Application.DataTemplates>
<Application.Resources> <Application.Resources>
<ResourceDictionary> <ResourceDictionary>
+3 -1
View File
@@ -3,7 +3,9 @@ using Avalonia;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using DialogHostAvalonia;
using ReceiptPDFBuilder; using ReceiptPDFBuilder;
using ReceiptPDFBuilder.ViewModels;
namespace ReceiptPDFBuilder; namespace ReceiptPDFBuilder;
@@ -26,6 +28,6 @@ public partial class App : Application
public void AboutOnClick(object? sender, EventArgs args) public void AboutOnClick(object? sender, EventArgs args)
{ {
new AboutWindow().Show(); DialogHost.Show(new AboutViewModel());
} }
} }
+3
View File
@@ -1,5 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using System.Text.Json.Serialization;
using ReceiptPDFBuilder.Helpers; using ReceiptPDFBuilder.Helpers;
namespace ReceiptPDFBuilder.Models; namespace ReceiptPDFBuilder.Models;
@@ -44,6 +45,7 @@ class ReportFile : ChangeNotifier
} }
} }
[JsonIgnore]
public DateOnly ReceiptDate public DateOnly ReceiptDate
{ {
get => DateOnly.FromDateTime(_receiptDateTime); get => DateOnly.FromDateTime(_receiptDateTime);
@@ -61,6 +63,7 @@ class ReportFile : ChangeNotifier
set { _filePath = value; NotifyPropertyChanged(); } set { _filePath = value; NotifyPropertyChanged(); }
} }
[JsonIgnore]
public string FileName public string FileName
{ {
get => Path.GetFileName(_filePath); get => Path.GetFileName(_filePath);
+86
View File
@@ -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<string> 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<Settings>(json, jsonContext.Settings) ?? new Settings();
}
public static async Task<Settings> LoadSettingsAsync()
{
using FileStream fileStream = File.OpenRead(GetSettingsPath());
var jsonContext = new SourceGenerationContext(GetSerializerOptions());
var output = await JsonSerializer.DeserializeAsync<Settings>(fileStream, jsonContext.Settings) ?? new Settings();
return output;
}
}
+33
View File
@@ -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);
}
}
+29 -14
View File
@@ -30,6 +30,8 @@ class MainViewModel : BaseViewModel, IFontResolver
private string _reportTitle; private string _reportTitle;
private ObservableCollection<ReportFile> _reportFiles; private ObservableCollection<ReportFile> _reportFiles;
private Settings _settings;
public MainViewModel(IChangeViewModel viewModelChanger) : base(viewModelChanger) public MainViewModel(IChangeViewModel viewModelChanger) : base(viewModelChanger)
{ {
_baseDir = Path.GetDirectoryName(Environment.ProcessPath) ?? ""; _baseDir = Path.GetDirectoryName(Environment.ProcessPath) ?? "";
@@ -39,6 +41,12 @@ class MainViewModel : BaseViewModel, IFontResolver
_reportFiles = new ObservableCollection<ReportFile>(); _reportFiles = new ObservableCollection<ReportFile>();
_reportFiles.CollectionChanged += ( sender, e ) => { NotifyPropertyChanged(nameof(IsCreatePDFButtonEnabled)); }; _reportFiles.CollectionChanged += ( sender, e ) => { NotifyPropertyChanged(nameof(IsCreatePDFButtonEnabled)); };
_reportTitle = ""; _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 public string ReportTitle
@@ -95,20 +103,27 @@ class MainViewModel : BaseViewModel, IFontResolver
if (folders.Count == 1) if (folders.Count == 1)
{ {
var folder = folders[0]; var folder = folders[0];
LogInfo("Chosen folder: " + folder.Path.LocalPath); LogInfo("Loading items in folder: " + folder.Path.LocalPath);
if (Directory.Exists(folder.Path.LocalPath)) ScanFolder(folder.Path.LocalPath);
{ _settings.LastUsedPath = folder.Path.LocalPath;
_workingFolder = folder.Path.LocalPath; await _settings.SaveSettingsAsync();
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(); private void ScanFolder(string path)
foreach (var filePath in filePaths) {
{ if (Directory.Exists(path))
AddFileBasedOnPath(filePath); {
} _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);
} }
} }
} }
+23
View File
@@ -0,0 +1,23 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="ReceiptPDFBuilder.Views.AboutView"
xmlns:models="clr-namespace:ReceiptPDFBuilder.Models"
xmlns:vm="clr-namespace:ReceiptPDFBuilder.ViewModels"
xmlns:dialogHost="clr-namespace:DialogHostAvalonia;assembly=DialogHost.Avalonia"
x:DataType="vm:AboutViewModel">
<StackPanel Orientation="Vertical"
Spacing="4">
<TextBlock Text="Receipt PDF Builder was built by MB for A in 2026. May the quacking of ducks always be in your favor. Thanks for using!"
MaxWidth="300"
TextWrapping="Wrap"
FontSize="14"/>
<Button Command="{Binding Close}"
Classes="accent"
Content="Close"
HorizontalAlignment="Right"
Margin="0,0,4,4"/>
</StackPanel>
</UserControl>
+15
View File
@@ -0,0 +1,15 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ReceiptPDFBuilder.Views
{
public partial class AboutView : UserControl
{
public AboutView()
{
this.InitializeComponent();
}
}
}
-16
View File
@@ -1,16 +0,0 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="ReceiptPDFBuilder.AboutWindow"
Title="About Receipt PDF Builder"
Width="300"
Height="300"
MaxHeight="400"
MaxWidth="400"
MinWidth="300"
MinHeight="300">
<TextBlock Text="Receipt PDF Builder was built by MB for A in 2026. Thanks for using!"
TextWrapping="Wrap"/>
</Window>
-13
View File
@@ -1,13 +0,0 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ReceiptPDFBuilder;
public partial class AboutWindow : Window
{
public AboutWindow()
{
InitializeComponent();
}
}