Start major rework for DataGrid display

For editing of file properties, etc.
This commit is contained in:
2026-02-16 09:40:15 +09:00
parent 7ad1038b37
commit 7ea649850e
7 changed files with 267 additions and 32 deletions
+1
View File
@@ -8,6 +8,7 @@
<Application.Styles>
<FluentTheme />
<StyleInclude Source="avares://AvaloniaProgressRing/Styles/ProgressRing.xaml"/>
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>
</Application.Styles>
<Application.DataTemplates>
<DataTemplate DataType="{x:Type viewModels:MainViewModel}">
+5
View File
@@ -0,0 +1,5 @@
<Project>
<PropertyGroup>
<AvaloniaVersion>11.3.12</AvaloniaVersion>
</PropertyGroup>
</Project>
+4 -4
View File
@@ -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">
<ContentControl Content="{Binding CurrentViewModel}"/>
</Window>
+78
View File
@@ -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);
}
}
+7 -7
View File
@@ -34,17 +34,17 @@
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.3.10" />
<PackageReference Include="Avalonia.Desktop" Version="11.3.10" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.10" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.10" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Include="Avalonia.Diagnostics" Version="11.3.10">
<PackageReference Include="Avalonia" Version="$(AvaloniaVersion)" />
<PackageReference Include="Avalonia.Desktop" Version="$(AvaloniaVersion)" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="$(AvaloniaVersion)" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="$(AvaloniaVersion)" />
<PackageReference Include="Avalonia.Controls.DataGrid" Version="$(AvaloniaVersion)" />
<PackageReference Include="Avalonia.Diagnostics" Version="$(AvaloniaVersion)">
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference>
<PackageReference Include="PDFsharp-MigraDoc" Version="6.2.3" />
<PackageReference Include="Magick.NET-Q16-AnyCPU" Version="14.10.1" />
<PackageReference Include="Magick.NET-Q16-AnyCPU" Version="14.10.2" />
<PackageReference Include="Deadpikle.AvaloniaProgressRing" Version="0.10.11-preview20251127001" />
</ItemGroup>
</Project>
+91 -18
View File
@@ -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<ReportFile> _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<ReportFile>();
_workingFolder = "";
}
public bool IsCreatingPDF
@@ -38,10 +47,17 @@ class MainViewModel : BaseViewModel, IFontResolver
set { _createPDFLog = value; NotifyPropertyChanged(); }
}
private void LogInfo(string log)
public ObservableCollection<ReportFile> 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));
+81 -3
View File
@@ -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">
<Grid ColumnDefinitions="Auto, *"
RowDefinitions="Auto, *">
RowDefinitions="Auto, Auto, *, *">
<Label Content="Easy Receipt Folder -> PDF Builder"
FontSize="16"
Grid.Row="0"
@@ -33,9 +34,86 @@
Foreground="LightBlue"
Margin="10,20,0,0"/>
</StackPanel>
<ScrollViewer Grid.Column="1"
<DataGrid x:Name="FilesGrid"
Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="2"
ItemsSource="{Binding ReportFiles, Mode=TwoWay}"
AutoGenerateColumns="False"
IsReadOnly="False"
GridLinesVisibility="All"
CanUserReorderColumns="False"
CanUserResizeColumns="True"
CanUserSortColumns="False"
BorderThickness="1"
VerticalScrollBarVisibility="Visible"
HorizontalScrollBarVisibility="Disabled"
ScrollViewer.AllowAutoHide="False"
BorderBrush="Gray">
<DataGrid.Styles>
<Style Selector="TextBlock">
<Setter Property="TextWrapping" Value="NoWrap" />
<Setter Property="TextTrimming" Value="CharacterEllipsis" />
</Style>
<Style Selector="TextBox">
<Setter Property="TextWrapping" Value="NoWrap" />
</Style>
</DataGrid.Styles>
<DataGrid.Columns>
<DataGridTextColumn Header="Title"
Binding="{Binding Title}"
Width="*"/>
<DataGridTemplateColumn Header="Receipt Date"
IsReadOnly="False"
Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Date}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate DataType="models:ReportFile">
<!-- <DatePicker DayFormat="ddd dd"
SelectedDate="{Binding DateTime}"/> -->
<Calendar SelectionMode="SingleDate"
SelectedDate="{Binding DateTime}"
DisplayDate="{Binding DateTime}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="File Name"
Binding="{Binding FileName}"
IsReadOnly="True"
Width="*" />
<DataGridTemplateColumn Header=""
IsReadOnly="True"
Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Content="Up"
Command="{Binding $parent[DataGrid].((vm:MainViewModel)DataContext).MoveItemUp}"
CommandParameter="{Binding}"
IsEnabled="True"/>
<Button Content="Down"
Command="{Binding $parent[DataGrid].((vm:MainViewModel)DataContext).MoveItemDown}"
CommandParameter="{Binding}"
IsEnabled="True"/>
<Button Content="Byebye"
Command="{Binding $parent[DataGrid].((vm:MainViewModel)DataContext).RemoveFile}"
CommandParameter="{Binding}"
IsEnabled="True"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<ScrollViewer Grid.Column="0"
Grid.ColumnSpan="2"
Margin="2"
Grid.Row="1"
Grid.Row="3"
x:Name="LogScrollView">
<SelectableTextBlock Text="{Binding CreatePDFLog}"
Margin="2"