Refactoring

This commit is contained in:
Michael Bucari-Tovo 2022-07-15 16:36:58 -06:00
parent 1ce5fedc8c
commit 0189a197a8
9 changed files with 85 additions and 65 deletions

View File

@ -21,7 +21,7 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
private int _visibleNotLiberated = 1; private int _visibleNotLiberated = 1;
/// <summary> The Process Queue's viewmodel </summary> /// <summary> The Process Queue's viewmodel </summary>
public ProcessQueueViewModel ProcessQueueViewModel { get; } = new ProcessQueueViewModel(); public ProcessQueueViewModel ProcessQueue { get; } = new ProcessQueueViewModel();
public ProductsDisplayViewModel ProductsDisplay { get; } = new ProductsDisplayViewModel(); public ProductsDisplayViewModel ProductsDisplay { get; } = new ProductsDisplayViewModel();

View File

@ -1,12 +1,9 @@
using Avalonia.Collections;
using Avalonia.Controls; using Avalonia.Controls;
using DataLayer; using DataLayer;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using ReactiveUI; using ReactiveUI;
using System.Reflection; using System.Reflection;
@ -14,25 +11,25 @@ using System.Collections;
using Avalonia.Threading; using Avalonia.Threading;
using ApplicationServices; using ApplicationServices;
using AudibleUtilities; using AudibleUtilities;
using LibationWinForms.AvaloniaUI.Views;
namespace LibationWinForms.AvaloniaUI.ViewModels namespace LibationWinForms.AvaloniaUI.ViewModels
{ {
public class ProductsDisplayViewModel : ViewModelBase public class ProductsDisplayViewModel : ViewModelBase
{ {
/// <summary>Number of visible rows has changed</summary> /// <summary>Number of visible rows has changed</summary>
public event EventHandler<int> VisibleCountChanged; public event EventHandler<int> VisibleCountChanged;
public event EventHandler<int> RemovableCountChanged; public event EventHandler<int> RemovableCountChanged;
public event EventHandler InitialLoaded; public event EventHandler InitialLoaded;
private DataGridColumn _currentSortColumn; private DataGridColumn _currentSortColumn;
private DataGrid productsDataGrid;
private GridEntryBindingList2 _gridEntries; private GridEntryBindingList2 _gridEntries;
private bool _removeColumnVisivle; private bool _removeColumnVisivle;
public GridEntryBindingList2 GridEntries { get => _gridEntries; private set => this.RaiseAndSetIfChanged(ref _gridEntries, value); } public GridEntryBindingList2 GridEntries { get => _gridEntries; private set => this.RaiseAndSetIfChanged(ref _gridEntries, value); }
public bool RemoveColumnVisivle { get => _removeColumnVisivle; private set => this.RaiseAndSetIfChanged(ref _removeColumnVisivle, value); } public bool RemoveColumnVisivle { get => _removeColumnVisivle; private set => this.RaiseAndSetIfChanged(ref _removeColumnVisivle, value); }
public List<LibraryBook> GetVisibleBookEntries() public List<LibraryBook> GetVisibleBookEntries()
=> GridEntries => GridEntries
.BookEntries() .BookEntries()
@ -49,14 +46,47 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
{ {
using var context = DbContexts.GetContext(); using var context = DbContexts.GetContext();
var book = context.GetLibraryBook_Flat_NoTracking("B017V4IM1G"); var book = context.GetLibraryBook_Flat_NoTracking("B017V4IM1G");
_gridEntries = new GridEntryBindingList2(CreateGridEntries(new List<LibraryBook> { book })); GridEntries = new GridEntryBindingList2(CreateGridEntries(new List<LibraryBook> { book }));
return; return;
} }
} }
public void InitialDisplay(List<LibraryBook> dbBooks, Views.ProductsDisplay2 productsGrid) #region Display Functions
{
/// <summary>
/// Call once on load so we can modify access a private member with reflection
/// </summary>
public void RegisterCollectionChanged(ProductsDisplay2 productsDisplay = null)
{
productsDataGrid ??= productsDisplay?.productsGrid;
if (GridEntries is null)
return;
//Avalonia displays items in the DataConncetion from an internal copy of
//the bound list, not the actual bound list. So we need to reflect to get
//the current display order and set each GridEntry.ListIndex correctly.
var DataConnection_PI = typeof(DataGrid).GetProperty("DataConnection", BindingFlags.NonPublic | BindingFlags.Instance);
var DataSource_PI = DataConnection_PI.PropertyType.GetProperty("DataSource", BindingFlags.Public | BindingFlags.Instance);
GridEntries.CollectionChanged += (s, e) =>
{
if (s != GridEntries) return;
var displayListGE = ((IEnumerable)DataSource_PI.GetValue(DataConnection_PI.GetValue(productsDataGrid))).Cast<GridEntry2>();
int index = 0;
foreach (var di in displayListGE)
{
di.ListIndex = index++;
}
};
}
/// <summary>
/// Only call once per lifetime
/// </summary>
public void InitialDisplay(List<LibraryBook> dbBooks)
{
try try
{ {
GridEntries = new GridEntryBindingList2(CreateGridEntries(dbBooks)); GridEntries = new GridEntryBindingList2(CreateGridEntries(dbBooks));
@ -67,21 +97,7 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
InitialLoaded?.Invoke(this, EventArgs.Empty); InitialLoaded?.Invoke(this, EventArgs.Empty);
VisibleCountChanged?.Invoke(this, bookEntryCount); VisibleCountChanged?.Invoke(this, bookEntryCount);
//Avalonia displays items in the DataConncetion from an internal copy of RegisterCollectionChanged();
//the bound list, not the actual bound list. So we need to reflect to get
//the current display order and set each GridEntry.ListIndex correctly.
var DataConnection_PI = typeof(DataGrid).GetProperty("DataConnection", BindingFlags.NonPublic | BindingFlags.Instance);
var DataSource_PI = DataConnection_PI.PropertyType.GetProperty("DataSource", BindingFlags.Public | BindingFlags.Instance);
GridEntries.CollectionChanged += (s, e) =>
{
var displayListGE = ((IEnumerable)DataSource_PI.GetValue(DataConnection_PI.GetValue(productsGrid.productsGrid))).Cast<GridEntry2>();
int index = 0;
foreach (var di in displayListGE)
{
di.ListIndex = index++;
}
};
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -89,9 +105,11 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
} }
} }
/// <summary>
/// Call when there's been a change to the library
/// </summary>
public async Task DisplayBooks(List<LibraryBook> dbBooks) public async Task DisplayBooks(List<LibraryBook> dbBooks)
{ {
try try
{ {
//List is already displayed. Replace all items with new ones, refilter, and re-sort //List is already displayed. Replace all items with new ones, refilter, and re-sort
@ -148,7 +166,19 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
return geList.OrderByDescending(e => e.DateAdded); return geList.OrderByDescending(e => e.DateAdded);
} }
public void ToggleSeriesExpanded(SeriesEntrys2 seriesEntry)
{
if (seriesEntry.Liberate.Expanded)
GridEntries.CollapseItem(seriesEntry);
else
GridEntries.ExpandItem(seriesEntry);
VisibleCountChanged?.Invoke(this, GridEntries.BookEntries().Count());
}
#endregion
#region Filtering
public async Task Filter(string searchString) public async Task Filter(string searchString)
{ {
await Dispatcher.UIThread.InvokeAsync(() => await Dispatcher.UIThread.InvokeAsync(() =>
@ -168,16 +198,9 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
}); });
} }
public void ToggleSeriesExpanded(SeriesEntrys2 seriesEntry) #endregion
{
if (seriesEntry.Liberate.Expanded)
GridEntries.CollapseItem(seriesEntry);
else
GridEntries.ExpandItem(seriesEntry);
VisibleCountChanged?.Invoke(this, GridEntries.BookEntries().Count());
}
#region Sorting
public void Sort(DataGridColumn sortColumn) public void Sort(DataGridColumn sortColumn)
{ {
@ -207,6 +230,10 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
} }
} }
#endregion
#region Scan and Remove Books
public void DoneRemovingBooks() public void DoneRemovingBooks()
{ {
foreach (var item in GridEntries.AllItems()) foreach (var item in GridEntries.AllItems())
@ -311,5 +338,7 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
RemovableCountChanged?.Invoke(this, removeCount); RemovableCountChanged?.Invoke(this, removeCount);
} }
} }
#endregion
} }
} }

View File

@ -19,7 +19,7 @@ namespace LibationWinForms.AvaloniaUI.Views
Serilog.Log.Logger.Information("Begin backing up all library books"); Serilog.Log.Logger.Information("Begin backing up all library books");
_viewModel.ProcessQueueViewModel.AddDownloadDecrypt( _viewModel.ProcessQueue.AddDownloadDecrypt(
ApplicationServices.DbContexts ApplicationServices.DbContexts
.GetLibrary_Flat_NoTracking() .GetLibrary_Flat_NoTracking()
.UnLiberated() .UnLiberated()
@ -34,7 +34,7 @@ namespace LibationWinForms.AvaloniaUI.Views
public async void beginPdfBackupsToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args) public async void beginPdfBackupsToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args)
{ {
SetQueueCollapseState(false); SetQueueCollapseState(false);
await Task.Run(() => _viewModel.ProcessQueueViewModel.AddDownloadPdf(ApplicationServices.DbContexts.GetLibrary_Flat_NoTracking() await Task.Run(() => _viewModel.ProcessQueue.AddDownloadPdf(ApplicationServices.DbContexts.GetLibrary_Flat_NoTracking()
.Where(lb => lb.Book.UserDefinedItem.PdfStatus is DataLayer.LiberatedStatus.NotLiberated))); .Where(lb => lb.Book.UserDefinedItem.PdfStatus is DataLayer.LiberatedStatus.NotLiberated)));
} }
@ -51,7 +51,7 @@ namespace LibationWinForms.AvaloniaUI.Views
if (result == DialogResult.Yes) if (result == DialogResult.Yes)
{ {
SetQueueCollapseState(false); SetQueueCollapseState(false);
await Task.Run(() => _viewModel.ProcessQueueViewModel.AddConvertMp3(ApplicationServices.DbContexts.GetLibrary_Flat_NoTracking() await Task.Run(() => _viewModel.ProcessQueue.AddConvertMp3(ApplicationServices.DbContexts.GetLibrary_Flat_NoTracking()
.Where(lb => lb.Book.UserDefinedItem.BookStatus is DataLayer.LiberatedStatus.Liberated && lb.Book.ContentType is DataLayer.ContentType.Product))); .Where(lb => lb.Book.UserDefinedItem.BookStatus is DataLayer.LiberatedStatus.Liberated && lb.Book.ContentType is DataLayer.ContentType.Product)));
} }
//Only Queue Liberated books for conversion. This isn't a perfect filter, but it's better than nothing. //Only Queue Liberated books for conversion. This isn't a perfect filter, but it's better than nothing.

View File

@ -23,13 +23,13 @@ namespace LibationWinForms.AvaloniaUI.Views
{ {
Serilog.Log.Logger.Information("Begin single book backup of {libraryBook}", libraryBook); Serilog.Log.Logger.Information("Begin single book backup of {libraryBook}", libraryBook);
SetQueueCollapseState(false); SetQueueCollapseState(false);
_viewModel.ProcessQueueViewModel.AddDownloadDecrypt(libraryBook); _viewModel.ProcessQueue.AddDownloadDecrypt(libraryBook);
} }
else if (libraryBook.Book.UserDefinedItem.PdfStatus is LiberatedStatus.NotLiberated) else if (libraryBook.Book.UserDefinedItem.PdfStatus is LiberatedStatus.NotLiberated)
{ {
Serilog.Log.Logger.Information("Begin single pdf backup of {libraryBook}", libraryBook); Serilog.Log.Logger.Information("Begin single pdf backup of {libraryBook}", libraryBook);
SetQueueCollapseState(false); SetQueueCollapseState(false);
_viewModel.ProcessQueueViewModel.AddDownloadPdf(libraryBook); _viewModel.ProcessQueue.AddDownloadPdf(libraryBook);
} }
else if (libraryBook.Book.Audio_Exists()) else if (libraryBook.Book.Audio_Exists())
{ {

View File

@ -27,7 +27,7 @@ namespace LibationWinForms.AvaloniaUI.Views
Serilog.Log.Logger.Information("Begin backing up visible library books"); Serilog.Log.Logger.Information("Begin backing up visible library books");
_viewModel.ProcessQueueViewModel.AddDownloadDecrypt( _viewModel.ProcessQueue.AddDownloadDecrypt(
_viewModel _viewModel
.ProductsDisplay .ProductsDisplay
.GetVisibleBookEntries() .GetVisibleBookEntries()

View File

@ -167,14 +167,14 @@
<!-- Process Queue --> <!-- Process Queue -->
<SplitView.Pane> <SplitView.Pane>
<views:ProcessQueueControl2 DataContext="{Binding ProcessQueueViewModel}"/> <views:ProcessQueueControl2 DataContext="{Binding ProcessQueue}"/>
</SplitView.Pane> </SplitView.Pane>
<!-- Product Display Grid --> <!-- Product Display Grid -->
<views:ProductsDisplay2 <views:ProductsDisplay2
Initialized="ProductsDisplay_Initialized1"
DataContext="{Binding ProductsDisplay}" DataContext="{Binding ProductsDisplay}"
LiberateClicked="ProductsDisplay_LiberateClicked" LiberateClicked="ProductsDisplay_LiberateClicked"/>
Name="productsDisplay" />
</SplitView> </SplitView>
</Border> </Border>

View File

@ -20,13 +20,14 @@ namespace LibationWinForms.AvaloniaUI.Views
public MainWindow() public MainWindow()
{ {
this.DataContext = _viewModel = new MainWindowViewModel();
InitializeComponent(); InitializeComponent();
#if DEBUG #if DEBUG
this.AttachDevTools(); this.AttachDevTools();
#endif #endif
this.FindAllControls(); this.FindAllControls();
this.DataContext = _viewModel = new MainWindowViewModel();
// eg: if one of these init'd productsGrid, then another can't reliably subscribe to it // eg: if one of these init'd productsGrid, then another can't reliably subscribe to it
Configure_BackupCounts(); Configure_BackupCounts();
@ -52,16 +53,19 @@ namespace LibationWinForms.AvaloniaUI.Views
this.LibraryLoaded += MainWindow_LibraryLoaded; this.LibraryLoaded += MainWindow_LibraryLoaded;
LibraryCommands.LibrarySizeChanged += async (_, _) => await _viewModel.ProductsDisplay.DisplayBooks(DbContexts.GetLibrary_Flat_NoTracking(includeParents: true)); LibraryCommands.LibrarySizeChanged += async (_, _) => await _viewModel.ProductsDisplay.DisplayBooks(DbContexts.GetLibrary_Flat_NoTracking(includeParents: true));
this.Closing += (_,_) => this.SaveSizeAndLocation(Configuration.Instance); Closing += (_,_) => this.SaveSizeAndLocation(Configuration.Instance);
} }
} }
public void ProductsDisplay_Initialized1(object sender, EventArgs e)
{
if (sender is ProductsDisplay2 products)
_viewModel.ProductsDisplay.RegisterCollectionChanged(products);
}
private void MainWindow_LibraryLoaded(object sender, List<LibraryBook> dbBooks) private void MainWindow_LibraryLoaded(object sender, List<LibraryBook> dbBooks)
{ {
if (Design.IsDesignMode) _viewModel.ProductsDisplay.InitialDisplay(dbBooks);
return;
_viewModel.ProductsDisplay.InitialDisplay(dbBooks, productsDisplay);
} }
private void InitializeComponent() private void InitializeComponent()
@ -75,7 +79,6 @@ namespace LibationWinForms.AvaloniaUI.Views
private void FindAllControls() private void FindAllControls()
{ {
quickFiltersToolStripMenuItem = this.FindControl<MenuItem>(nameof(quickFiltersToolStripMenuItem)); quickFiltersToolStripMenuItem = this.FindControl<MenuItem>(nameof(quickFiltersToolStripMenuItem));
productsDisplay = this.FindControl<ProductsDisplay2>(nameof(productsDisplay));
} }
protected override void OnDataContextChanged(EventArgs e) protected override void OnDataContextChanged(EventArgs e)

View File

@ -34,7 +34,7 @@ namespace LibationWinForms.AvaloniaUI.Views
_viewModel.Sort(e.Column); _viewModel.Sort(e.Column);
} }
private void RemoveColumn_PropertyChanged(object sender, Avalonia.AvaloniaPropertyChangedEventArgs e) private void RemoveColumn_PropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)
{ {
if (sender is DataGridColumn col && e.Property.Name == nameof(DataGridColumn.IsVisible)) if (sender is DataGridColumn col && e.Property.Name == nameof(DataGridColumn.IsVisible))
{ {
@ -86,7 +86,7 @@ namespace LibationWinForms.AvaloniaUI.Views
{ {
Header = ((string)column.Header).Replace((char)0xa, ' '), Header = ((string)column.Header).Replace((char)0xa, ' '),
Tag = column, Tag = column,
Margin = new Avalonia.Thickness(6, 0), Margin = new Thickness(6, 0),
Icon = new CheckBox Icon = new CheckBox
{ {
Width = 50, Width = 50,
@ -151,7 +151,7 @@ namespace LibationWinForms.AvaloniaUI.Views
config.GridColumnsVisibilities = dictionary; config.GridColumnsVisibilities = dictionary;
} }
private void ProductsGrid_ColumnDisplayIndexChanged(object sender, Avalonia.Controls.DataGridColumnEventArgs e) private void ProductsGrid_ColumnDisplayIndexChanged(object sender, DataGridColumnEventArgs e)
{ {
var config = Configuration.Instance; var config = Configuration.Instance;

View File

@ -109,18 +109,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Update="AvaloniaUI\Views\ProcessQueueControl2.axaml.cs">
<DependentUpon>ProcessQueueControl2.axaml</DependentUpon>
</Compile>
<Compile Update="AvaloniaUI\Views\MainWindow\MainWindow.axaml.cs">
<DependentUpon>ProcessQueueControl2.axaml</DependentUpon>
</Compile>
<Compile Update="AvaloniaUI\Views\MainWindow\MainWindow.*.axaml.cs">
<DependentUpon>ProcessQueueControl2.axaml.cs</DependentUpon>
</Compile>
<Compile Update="AvaloniaUI\Views\ProcessBookControl2.axaml.cs">
<DependentUpon>ProcessBookControl2.axaml</DependentUpon>
</Compile>
<Compile Update="Properties\Resources.Designer.cs"> <Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>