From bff9b67b7270d037b3c00452221749bbe28c5337 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Mon, 21 Jul 2025 10:47:10 -0600 Subject: [PATCH] Remove GridEntry derrived types and interfaces Use existing BaseUtil.LoadImage delegate, obviating need for derrived classes to load images Since GridEntry types are no longer generic, interfaces are unnecessary and deleted. --- .../Controls/DataGridCheckBoxColumnExt.cs | 2 +- .../Controls/DataGridContextMenus.cs | 8 ++-- .../ViewModels/AvaloniaEntryStatus.cs | 20 ---------- .../ViewModels/MainVM.ProcessQueue.cs | 2 +- .../ViewModels/MainVM._NoUI.cs | 15 +++++++ .../ViewModels/ProductsDisplayViewModel.cs | 40 +++++++++---------- .../ViewModels/RowComparer.cs | 2 +- .../Views/LiberateStatusButton.axaml.cs | 4 +- .../Views/MainWindow.axaml.cs | 2 +- .../Views/ProductsDisplay.axaml | 34 ++++++++-------- .../Views/ProductsDisplay.axaml.cs | 34 ++++++++-------- Source/LibationUiBase/BaseUtil.cs | 28 +++++++++++-- Source/LibationUiBase/GridView/EntryStatus.cs | 16 ++------ .../GridView/GridContextMenu.cs | 12 +++--- .../GridView/GridEntry[TStatus].cs | 13 +++--- Source/LibationUiBase/GridView/IGridEntry.cs | 34 ---------------- .../GridView/ILibraryBookEntry.cs | 7 ---- .../LibationUiBase/GridView/ISeriesEntry.cs | 11 ----- ...kEntry[TStatus].cs => LibraryBookEntry.cs} | 10 ++--- .../GridView/QueryExtensions.cs | 20 +++++----- .../GridView/RowComparerBase.cs | 18 ++++----- ...SeriesEntry[TStatus].cs => SeriesEntry.cs} | 18 ++++----- Source/LibationWinForms/Form1.Designer.cs | 2 +- Source/LibationWinForms/Form1.ProcessQueue.cs | 2 +- Source/LibationWinForms/Form1._NonUI.cs | 1 + .../EditTagsDataGridViewImageButtonColumn.cs | 2 +- .../GridView/GridEntryBindingList.cs | 30 +++++++------- .../LiberateDataGridViewImageButtonColumn.cs | 12 ++++-- .../GridView/ProductsDisplay.cs | 24 +++++------ .../GridView/ProductsGrid.Designer.cs | 2 +- .../LibationWinForms/GridView/ProductsGrid.cs | 40 +++++++++---------- .../LibationWinForms/GridView/RowComparer.cs | 4 +- .../GridView/WinFormsEntryStatus.cs | 24 ----------- 33 files changed, 214 insertions(+), 279 deletions(-) delete mode 100644 Source/LibationAvalonia/ViewModels/AvaloniaEntryStatus.cs delete mode 100644 Source/LibationUiBase/GridView/IGridEntry.cs delete mode 100644 Source/LibationUiBase/GridView/ILibraryBookEntry.cs delete mode 100644 Source/LibationUiBase/GridView/ISeriesEntry.cs rename Source/LibationUiBase/GridView/{LibraryBookEntry[TStatus].cs => LibraryBookEntry.cs} (72%) rename Source/LibationUiBase/GridView/{SeriesEntry[TStatus].cs => SeriesEntry.cs} (81%) delete mode 100644 Source/LibationWinForms/GridView/WinFormsEntryStatus.cs diff --git a/Source/LibationAvalonia/Controls/DataGridCheckBoxColumnExt.cs b/Source/LibationAvalonia/Controls/DataGridCheckBoxColumnExt.cs index f99d9bda..d4caadfc 100644 --- a/Source/LibationAvalonia/Controls/DataGridCheckBoxColumnExt.cs +++ b/Source/LibationAvalonia/Controls/DataGridCheckBoxColumnExt.cs @@ -9,7 +9,7 @@ namespace LibationAvalonia.Controls { //Only SeriesEntry types have three-state checks, individual LibraryEntry books are binary. var ele = base.GenerateEditingElementDirect(cell, dataItem) as CheckBox; - ele.IsThreeState = dataItem is ISeriesEntry; + ele.IsThreeState = dataItem is SeriesEntry; return ele; } } diff --git a/Source/LibationAvalonia/Controls/DataGridContextMenus.cs b/Source/LibationAvalonia/Controls/DataGridContextMenus.cs index d4db742b..10b6c099 100644 --- a/Source/LibationAvalonia/Controls/DataGridContextMenus.cs +++ b/Source/LibationAvalonia/Controls/DataGridContextMenus.cs @@ -34,11 +34,11 @@ namespace LibationAvalonia.Controls private static void Cell_ContextRequested(object sender, ContextRequestedEventArgs e) { if (sender is DataGridCell cell && - cell.DataContext is IGridEntry clickedEntry && + cell.DataContext is GridEntry clickedEntry && OwningColumnProperty.GetValue(cell) is DataGridColumn column && OwningGridProperty.GetValue(column) is DataGrid grid) { - var allSelected = grid.SelectedItems.OfType().ToArray(); + var allSelected = grid.SelectedItems.OfType().ToArray(); var clickedIndex = Array.IndexOf(allSelected, clickedEntry); if (clickedIndex == -1) { @@ -101,7 +101,7 @@ namespace LibationAvalonia.Controls private static string RemoveLineBreaks(string text) => text.Replace("\r\n", "").Replace('\r', ' ').Replace('\n', ' '); - private string GetRowClipboardContents(IGridEntry gridEntry) + private string GetRowClipboardContents(GridEntry gridEntry) { var contents = Grid.Columns.Where(c => c.IsVisible).OrderBy(c => c.DisplayIndex).Select(c => RemoveLineBreaks(GetCellValue(c, gridEntry))).ToArray(); return string.Join("\t", contents); @@ -109,7 +109,7 @@ namespace LibationAvalonia.Controls public required DataGrid Grid { get; init; } public required DataGridColumn Column { get; init; } - public required IGridEntry[] GridEntries { get; init; } + public required GridEntry[] GridEntries { get; init; } public required ContextMenu ContextMenu { get; init; } public AvaloniaList ContextMenuItems => ContextMenu.ItemsSource as AvaloniaList; diff --git a/Source/LibationAvalonia/ViewModels/AvaloniaEntryStatus.cs b/Source/LibationAvalonia/ViewModels/AvaloniaEntryStatus.cs deleted file mode 100644 index 605f2e17..00000000 --- a/Source/LibationAvalonia/ViewModels/AvaloniaEntryStatus.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Avalonia.Media.Imaging; -using DataLayer; -using LibationUiBase.GridView; -using System; - -#nullable enable -namespace LibationAvalonia.ViewModels -{ - public class AvaloniaEntryStatus : EntryStatus, IEntryStatus, IComparable - { - private AvaloniaEntryStatus(LibraryBook libraryBook) : base(libraryBook) { } - public static EntryStatus Create(LibraryBook libraryBook) => new AvaloniaEntryStatus(libraryBook); - - protected override Bitmap LoadImage(byte[] picture) - => AvaloniaUtils.TryLoadImageOrDefault(picture, LibationFileManager.PictureSize._80x80); - - //Button icons are handled by LiberateStatusButton - protected override Bitmap? GetResourceImage(string rescName) => null; - } -} diff --git a/Source/LibationAvalonia/ViewModels/MainVM.ProcessQueue.cs b/Source/LibationAvalonia/ViewModels/MainVM.ProcessQueue.cs index a35f1036..21d1b509 100644 --- a/Source/LibationAvalonia/ViewModels/MainVM.ProcessQueue.cs +++ b/Source/LibationAvalonia/ViewModels/MainVM.ProcessQueue.cs @@ -57,7 +57,7 @@ namespace LibationAvalonia.ViewModels } } - public void LiberateSeriesClicked(ISeriesEntry series) + public void LiberateSeriesClicked(SeriesEntry series) { try { diff --git a/Source/LibationAvalonia/ViewModels/MainVM._NoUI.cs b/Source/LibationAvalonia/ViewModels/MainVM._NoUI.cs index d57594e3..0d361117 100644 --- a/Source/LibationAvalonia/ViewModels/MainVM._NoUI.cs +++ b/Source/LibationAvalonia/ViewModels/MainVM._NoUI.cs @@ -1,5 +1,6 @@ using LibationFileManager; using LibationUiBase; +using System; using System.IO; #nullable enable @@ -23,6 +24,20 @@ namespace LibationAvalonia.ViewModels PictureStorage.SetDefaultImage(PictureSize.Native, ms3.ToArray()); BaseUtil.SetLoadImageDelegate(AvaloniaUtils.TryLoadImageOrDefault); + BaseUtil.SetLoadResourceImageDelegate(LoadResourceImage); + } + private static Avalonia.Media.Imaging.Bitmap? LoadResourceImage(string resourceName) + { + try + { + using var stream = App.OpenAsset(resourceName); + return new Avalonia.Media.Imaging.Bitmap(stream); + } + catch (Exception ex) + { + Serilog.Log.Error(ex, "Failed to load resource image: {ResourceName}", resourceName); + return null; + } } } } diff --git a/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs b/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs index e7416e14..304d8d21 100644 --- a/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs +++ b/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs @@ -26,9 +26,9 @@ namespace LibationAvalonia.ViewModels public event EventHandler? RemovableCountChanged; /// Backing list of all grid entries - private readonly AvaloniaList SOURCE = new(); + private readonly AvaloniaList SOURCE = new(); /// Grid entries included in the filter set. If null, all grid entries are shown - private HashSet? FilteredInGridEntries; + private HashSet? FilteredInGridEntries; public string? FilterString { get; private set; } private DataGridCollectionView? _gridEntries; @@ -43,15 +43,15 @@ namespace LibationAvalonia.ViewModels public List GetVisibleBookEntries() => FilteredInGridEntries? - .OfType() + .OfType() .Select(lbe => lbe.LibraryBook) .ToList() ?? SOURCE - .OfType() + .OfType() .Select(lbe => lbe.LibraryBook) .ToList(); - private IEnumerable GetAllBookEntries() + private IEnumerable GetAllBookEntries() => SOURCE .BookEntries(); @@ -112,8 +112,8 @@ namespace LibationAvalonia.ViewModels var sc = await Dispatcher.UIThread.InvokeAsync(() => AvaloniaSynchronizationContext.Current); AvaloniaSynchronizationContext.SetSynchronizationContext(sc); - var geList = await LibraryBookEntry.GetAllProductsAsync(dbBooks); - var seriesEntries = await SeriesEntry.GetAllSeriesEntriesAsync(dbBooks); + var geList = await LibraryBookEntry.GetAllProductsAsync(dbBooks); + var seriesEntries = await SeriesEntry.GetAllSeriesEntriesAsync(dbBooks); //Add all IGridEntries to the SOURCE list. Note that SOURCE has not yet been linked to the UI via //the GridEntries property, so adding items to SOURCE will not trigger any refreshes or UI action. @@ -147,8 +147,8 @@ namespace LibationAvalonia.ViewModels private void GridEntries_CollectionChanged(object? sender = null, EventArgs? e = null) { var count - = FilteredInGridEntries?.OfType().Count() - ?? SOURCE.OfType().Count(); + = FilteredInGridEntries?.OfType().Count() + ?? SOURCE.OfType().Count(); VisibleCountChanged?.Invoke(this, count); } @@ -223,9 +223,9 @@ namespace LibationAvalonia.ViewModels GridEntries_CollectionChanged(); } - private void RemoveBooks(IEnumerable removedBooks, IEnumerable removedSeries) + private void RemoveBooks(IEnumerable removedBooks, IEnumerable removedSeries) { - foreach (var removed in removedBooks.Cast().Concat(removedSeries).Where(b => b is not null).ToList()) + foreach (var removed in removedBooks.Cast().Concat(removedSeries).Where(b => b is not null).ToList()) { if (GridEntries?.PassesFilter(removed) ?? false) GridEntries.Remove(removed); @@ -238,21 +238,21 @@ namespace LibationAvalonia.ViewModels } } - private void UpsertBook(LibraryBook book, ILibraryBookEntry? existingBookEntry) + private void UpsertBook(LibraryBook book, LibraryBookEntry? existingBookEntry) { if (existingBookEntry is null) // Add the new product to top - SOURCE.Insert(0, new LibraryBookEntry(book)); + SOURCE.Insert(0, new LibraryBookEntry(book)); else // update existing existingBookEntry.UpdateLibraryBook(book); } - private void UpsertEpisode(LibraryBook episodeBook, ILibraryBookEntry? existingEpisodeEntry, List seriesEntries, IEnumerable dbBooks) + private void UpsertEpisode(LibraryBook episodeBook, LibraryBookEntry? existingEpisodeEntry, List seriesEntries, IEnumerable dbBooks) { if (existingEpisodeEntry is null) { - ILibraryBookEntry episodeEntry; + LibraryBookEntry episodeEntry; var seriesEntry = seriesEntries.FindSeriesParent(episodeBook); @@ -270,7 +270,7 @@ namespace LibationAvalonia.ViewModels return; } - seriesEntry = new SeriesEntry(seriesBook, episodeBook); + seriesEntry = new SeriesEntry(seriesBook, episodeBook); seriesEntries.Add(seriesEntry); episodeEntry = seriesEntry.Children[0]; @@ -280,7 +280,7 @@ namespace LibationAvalonia.ViewModels else { //Series exists. Create and add episode child then update the SeriesEntry - episodeEntry = new LibraryBookEntry(episodeBook, seriesEntry); + episodeEntry = new LibraryBookEntry(episodeBook, seriesEntry); seriesEntry.Children.Add(episodeEntry); seriesEntry.Children.Sort((c1, c2) => c1.SeriesIndex.CompareTo(c2.SeriesIndex)); var seriesBook = dbBooks.Single(lb => lb.Book.AudibleProductId == seriesEntry.LibraryBook.Book.AudibleProductId); @@ -307,7 +307,7 @@ namespace LibationAvalonia.ViewModels } } - public async Task ToggleSeriesExpanded(ISeriesEntry seriesEntry) + public async Task ToggleSeriesExpanded(SeriesEntry seriesEntry) { seriesEntry.Liberate.Expanded = !seriesEntry.Liberate.Expanded; @@ -332,7 +332,7 @@ namespace LibationAvalonia.ViewModels private bool CollectionFilter(object item) { - if (item is ILibraryBookEntry lbe + if (item is LibraryBookEntry lbe && lbe.Liberate.IsEpisode && lbe.Parent?.Liberate?.Expanded != true) return false; @@ -454,7 +454,7 @@ namespace LibationAvalonia.ViewModels private void GridEntry_PropertyChanged(object? sender, PropertyChangedEventArgs? e) { - if (e?.PropertyName == nameof(IGridEntry.Remove) && sender is ILibraryBookEntry) + if (e?.PropertyName == nameof(GridEntry.Remove) && sender is LibraryBookEntry) { int removeCount = GetAllBookEntries().Count(lbe => lbe.Remove is true); RemovableCountChanged?.Invoke(this, removeCount); diff --git a/Source/LibationAvalonia/ViewModels/RowComparer.cs b/Source/LibationAvalonia/ViewModels/RowComparer.cs index 071bedc6..bcdf50b0 100644 --- a/Source/LibationAvalonia/ViewModels/RowComparer.cs +++ b/Source/LibationAvalonia/ViewModels/RowComparer.cs @@ -17,7 +17,7 @@ namespace LibationAvalonia.ViewModels public RowComparer(DataGridColumn? column) { Column = column; - PropertyName = Column?.SortMemberPath ?? nameof(IGridEntry.DateAdded); + PropertyName = Column?.SortMemberPath ?? nameof(GridEntry.DateAdded); } //Avalonia doesn't expose the column's CurrentSortingState, so we must get it through reflection diff --git a/Source/LibationAvalonia/Views/LiberateStatusButton.axaml.cs b/Source/LibationAvalonia/Views/LiberateStatusButton.axaml.cs index f1217ca2..072137ce 100644 --- a/Source/LibationAvalonia/Views/LiberateStatusButton.axaml.cs +++ b/Source/LibationAvalonia/Views/LiberateStatusButton.axaml.cs @@ -53,8 +53,8 @@ namespace LibationAvalonia.Views private void LiberateStatusButton_DataContextChanged(object sender, EventArgs e) { //Force book status recheck when an entry is scrolled into view. - //This will force a recheck for a paprtially downloaded file. - var status = DataContext as ILibraryBookEntry; + //This will force a recheck for a partially downloaded file. + var status = DataContext as LibraryBookEntry; status?.Liberate.Invalidate(nameof(status.Liberate.BookStatus)); } diff --git a/Source/LibationAvalonia/Views/MainWindow.axaml.cs b/Source/LibationAvalonia/Views/MainWindow.axaml.cs index 783e69e1..e98d71db 100644 --- a/Source/LibationAvalonia/Views/MainWindow.axaml.cs +++ b/Source/LibationAvalonia/Views/MainWindow.axaml.cs @@ -137,7 +137,7 @@ namespace LibationAvalonia.Views } public void ProductsDisplay_LiberateClicked(object _, LibraryBook[] libraryBook) => ViewModel.LiberateClicked(libraryBook); - public void ProductsDisplay_LiberateSeriesClicked(object _, ISeriesEntry series) => ViewModel.LiberateSeriesClicked(series); + public void ProductsDisplay_LiberateSeriesClicked(object _, SeriesEntry series) => ViewModel.LiberateSeriesClicked(series); public void ProductsDisplay_ConvertToMp3Clicked(object _, LibraryBook[] libraryBook) => ViewModel.ConvertToMp3Clicked(libraryBook); BookDetailsDialog bookDetailsForm; diff --git a/Source/LibationAvalonia/Views/ProductsDisplay.axaml b/Source/LibationAvalonia/Views/ProductsDisplay.axaml index 90517420..739c15d7 100644 --- a/Source/LibationAvalonia/Views/ProductsDisplay.axaml +++ b/Source/LibationAvalonia/Views/ProductsDisplay.axaml @@ -59,7 +59,7 @@ Width="75"> - + - + - + @@ -93,7 +93,7 @@ - + @@ -103,7 +103,7 @@ - + @@ -113,7 +113,7 @@ - + @@ -123,7 +123,7 @@ - + @@ -133,7 +133,7 @@ - + @@ -143,7 +143,7 @@ - + @@ -153,7 +153,7 @@ - + @@ -163,7 +163,7 @@ - + @@ -172,7 +172,7 @@ - + @@ -192,7 +192,7 @@ - + @@ -213,7 +213,7 @@ - + @@ -223,7 +223,7 @@ - +