From 7ea649850e779d497137f86e8f16043022742d14 Mon Sep 17 00:00:00 2001 From: Michael Babienco Date: Mon, 16 Feb 2026 09:40:15 +0900 Subject: [PATCH] Start major rework for DataGrid display For editing of file properties, etc. --- App.axaml | 1 + Directory.Build.props | 5 ++ MainWindow.axaml | 8 +-- Models/ReportFile.cs | 78 ++++++++++++++++++++++++++ ReceiptPDFBuilder.csproj | 14 ++--- ViewModels/MainViewModel.cs | 109 ++++++++++++++++++++++++++++++------ Views/MainView.axaml | 84 ++++++++++++++++++++++++++- 7 files changed, 267 insertions(+), 32 deletions(-) create mode 100644 Directory.Build.props create mode 100644 Models/ReportFile.cs diff --git a/App.axaml b/App.axaml index 36da22d..c1073a1 100644 --- a/App.axaml +++ b/App.axaml @@ -8,6 +8,7 @@ + diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..c67685f --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,5 @@ + + + 11.3.12 + + diff --git a/MainWindow.axaml b/MainWindow.axaml index 0976566..8a74edb 100644 --- a/MainWindow.axaml +++ b/MainWindow.axaml @@ -7,9 +7,9 @@ Title="Receipt PDF Builder" xmlns:vm="clr-namespace:ReceiptPDFBuilder.ViewModels" x:DataType="vm:MainWindowViewModel" - Width="500" - MinWidth="300" - Height="350" - MinHeight="300"> + Width="800" + MinWidth="400" + Height="600" + MinHeight="400"> diff --git a/Models/ReportFile.cs b/Models/ReportFile.cs new file mode 100644 index 0000000..93b1932 --- /dev/null +++ b/Models/ReportFile.cs @@ -0,0 +1,78 @@ +using System; +using System.IO; +using ReceiptPDFBuilder.Helpers; + +namespace ReceiptPDFBuilder.Models; + +class ReportFile : ChangeNotifier +{ + private string _title; + private DateOnly _date; + private DateTime _dateTime; + private DateTimeOffset _dto; + private string _notes; + private string _filePath; + + public ReportFile() + { + _title = ""; + _date = DateOnly.FromDateTime(DateTime.Now); + _notes = ""; + _filePath = ""; + } + + public string Title + { + get => _title; + set { _title = value; NotifyPropertyChanged(); } + } + + public DateOnly Date + { + get => _date; + set + { + _date = value; + DateTime = value.ToDateTime(TimeOnly.MinValue); + DTO = new DateTimeOffset(value.ToDateTime(TimeOnly.MinValue)); + NotifyPropertyChanged(); + } + } + + public DateTime DateTime + { + get => _dateTime; + set + { + _dateTime = value; + NotifyPropertyChanged(); + } + } + + public DateTimeOffset DTO + { + get => _dto; + set + { + _dto = value; + NotifyPropertyChanged(); + } + } + + public string Notes + { + get => _notes; + set { _notes = value; NotifyPropertyChanged(); } + } + + public string FilePath + { + get => _filePath; + set { _filePath = value; NotifyPropertyChanged(); } + } + + public string FileName + { + get => Path.GetFileName(_filePath); + } +} \ No newline at end of file diff --git a/ReceiptPDFBuilder.csproj b/ReceiptPDFBuilder.csproj index 681c2b7..52414ce 100644 --- a/ReceiptPDFBuilder.csproj +++ b/ReceiptPDFBuilder.csproj @@ -34,17 +34,17 @@ - - - - - - + + + + + + None All - + diff --git a/ViewModels/MainViewModel.cs b/ViewModels/MainViewModel.cs index 83e2e44..f2fe4d0 100644 --- a/ViewModels/MainViewModel.cs +++ b/ViewModels/MainViewModel.cs @@ -1,4 +1,7 @@ +#nullable enable + using System; +using System.Collections.ObjectModel; using System.Globalization; using System.IO; using System.Threading.Tasks; @@ -10,6 +13,7 @@ using PdfSharp.Fonts; using PdfSharp.Pdf.IO; using PdfSharp.Snippets.Font; using ReceiptPDFBuilder.Interfaces; +using ReceiptPDFBuilder.Models; namespace ReceiptPDFBuilder.ViewModels; @@ -18,12 +22,17 @@ class MainViewModel : BaseViewModel, IFontResolver private string _baseDir; private bool _isCreatingPDF; private string _createPDFLog; + private string _workingFolder; + + private ObservableCollection _reportFiles; public MainViewModel(IChangeViewModel viewModelChanger) : base(viewModelChanger) { _baseDir = Path.GetDirectoryName(Environment.ProcessPath) ?? ""; _isCreatingPDF = false; - _createPDFLog = "Ready to create PDF!"; + _createPDFLog = "Ready to create PDF! Choose a folder to begin..."; + _reportFiles = new ObservableCollection(); + _workingFolder = ""; } public bool IsCreatingPDF @@ -38,10 +47,17 @@ class MainViewModel : BaseViewModel, IFontResolver set { _createPDFLog = value; NotifyPropertyChanged(); } } - private void LogInfo(string log) + public ObservableCollection ReportFiles { - Console.WriteLine(log); - CreatePDFLog += Environment.NewLine + log; + get => _reportFiles; + set { _reportFiles = value; NotifyPropertyChanged(); } + } + + private void LogInfo(string message, params object[]? arguments) + { + var timestamp = string.Format("[{0:s}]", DateTime.Now); + Console.WriteLine(timestamp + " " + message, arguments); + CreatePDFLog += Environment.NewLine + string.Format(message, arguments ?? []); } public async void ChooseFolder() @@ -60,23 +76,23 @@ class MainViewModel : BaseViewModel, IFontResolver LogInfo("Chosen folder: " + folder.Path.LocalPath); if (Directory.Exists(folder.Path.LocalPath)) { - try + _workingFolder = folder.Path.LocalPath; + // TODO: Scan folder for saved info from previous reports and reload if needed + // Scan folder for files and display in DataGrid + var filePaths = Directory.GetFiles(_workingFolder); + filePaths.Sort(); + foreach (var filePath in filePaths) { - await Task.Run(() => CreatePDF(folder.Path.LocalPath)); - } catch (Exception e) - { - LogInfo("PDF process failed! Reason: " + e.Message); - if (e.StackTrace != null) + if (!filePath.Contains(".DS_Store")) { - LogInfo(e.StackTrace); - } - if (e.InnerException != null) - { - LogInfo("Inner exception: " + e.InnerException.Message); - if (e.InnerException.StackTrace != null) + // TODO: if date in file name, pull out that date instead + ReportFiles.Add(new ReportFile() { - LogInfo(e.InnerException.StackTrace); - } + Title = Path.GetFileName(filePath), + Date = DateOnly.FromDateTime(File.GetCreationTime(filePath)), + Notes = "", + FilePath = filePath + }); } } } @@ -84,6 +100,63 @@ class MainViewModel : BaseViewModel, IFontResolver } } + public void MoveItemUp(ReportFile file) + { + var idx = ReportFiles.IndexOf(file); + Console.WriteLine("{0} at idx {1}", file.Title, idx); + if (idx != 0) + { + // .Move() is not observed -_- + // https://github.com/AvaloniaUI/Avalonia.Controls.DataGrid/issues/74 + // ReportFiles.Move(idx, idx - 1); + // So, remove and insert. + ReportFiles.RemoveAt(idx); + ReportFiles.Insert(idx - 1, file); + } + } + + public void MoveItemDown(ReportFile file) + { + var idx = ReportFiles.IndexOf(file); + if (idx != ReportFiles.Count - 1) + { + ReportFiles.RemoveAt(idx); + ReportFiles.Insert(idx + 1, file); + } + } + + public void RemoveFile(ReportFile file) + { + var idx = ReportFiles.IndexOf(file); + if (idx != -1) + { + ReportFiles.RemoveAt(idx); + } + } + + private async void BuildPDF() + { + try + { + await Task.Run(() => CreatePDF(_workingFolder)); + } catch (Exception e) + { + LogInfo("PDF process failed! Reason: " + e.Message); + if (e.StackTrace != null) + { + LogInfo(e.StackTrace); + } + if (e.InnerException != null) + { + LogInfo("Inner exception: " + e.InnerException.Message); + if (e.InnerException.StackTrace != null) + { + LogInfo(e.InnerException.StackTrace); + } + } + } + } + public byte[]? GetFont(string faceName) { LogInfo(string.Format("Loading font {0}", faceName)); diff --git a/Views/MainView.axaml b/Views/MainView.axaml index 8b36833..73842eb 100644 --- a/Views/MainView.axaml +++ b/Views/MainView.axaml @@ -4,11 +4,12 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="ReceiptPDFBuilder.Views.MainView" + xmlns:models="clr-namespace:ReceiptPDFBuilder.Models" xmlns:vm="clr-namespace:ReceiptPDFBuilder.ViewModels" xmlns:progRing="clr-namespace:AvaloniaProgressRing;assembly=AvaloniaProgressRing" x:DataType="vm:MainViewModel"> + RowDefinitions="Auto, Auto, *, *">