diff --git a/Source/LibationWinForms/AvaloniaUI/App.axaml b/Source/LibationWinForms/AvaloniaUI/App.axaml
index dadc8c76..3526c826 100644
--- a/Source/LibationWinForms/AvaloniaUI/App.axaml
+++ b/Source/LibationWinForms/AvaloniaUI/App.axaml
@@ -11,6 +11,6 @@
-
+
\ No newline at end of file
diff --git a/Source/LibationWinForms/AvaloniaUI/App.axaml.cs b/Source/LibationWinForms/AvaloniaUI/App.axaml.cs
index 44aab666..2351216d 100644
--- a/Source/LibationWinForms/AvaloniaUI/App.axaml.cs
+++ b/Source/LibationWinForms/AvaloniaUI/App.axaml.cs
@@ -16,10 +16,9 @@ namespace LibationWinForms.AvaloniaUI
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
- desktop.MainWindow = new MainWindow
- {
-
- };
+ var mainWindow = new MainWindow();
+ desktop.MainWindow = mainWindow;
+ mainWindow.OnLoad();
}
base.OnFrameworkInitializationCompleted();
diff --git a/Source/LibationWinForms/AvaloniaUI/Assets/DataGridTheme.xaml b/Source/LibationWinForms/AvaloniaUI/Assets/DataGridTheme.xaml
new file mode 100644
index 00000000..c10afcf4
--- /dev/null
+++ b/Source/LibationWinForms/AvaloniaUI/Assets/DataGridTheme.xaml
@@ -0,0 +1,658 @@
+
+
+ 0.6
+ 0.8
+ 12,0,12,0
+
+ M1875 1011l-787 787v-1798h-128v1798l-787 -787l-90 90l941 941l941 -941z
+ M515 93l930 931l-930 931l90 90l1022 -1021l-1022 -1021z
+ M1939 1581l90 -90l-1005 -1005l-1005 1005l90 90l915 -915z
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Source/LibationWinForms/AvaloniaUI/Controls/DataGridCheckBoxColumnExt.axaml.cs b/Source/LibationWinForms/AvaloniaUI/Controls/DataGridCheckBoxColumnExt.axaml.cs
index f70a91c0..be8157a0 100644
--- a/Source/LibationWinForms/AvaloniaUI/Controls/DataGridCheckBoxColumnExt.axaml.cs
+++ b/Source/LibationWinForms/AvaloniaUI/Controls/DataGridCheckBoxColumnExt.axaml.cs
@@ -1,32 +1,35 @@
-
-using Avalonia;
using Avalonia.Controls;
-using Avalonia.Data;
using Avalonia.Interactivity;
-using Avalonia.Styling;
using LibationWinForms.AvaloniaUI.ViewModels;
-using System;
namespace LibationWinForms.AvaloniaUI.Controls
{
+ /// The purpose of this extension is to immediately commit any check state changes to the viewmodel
public partial class DataGridCheckBoxColumnExt : DataGridCheckBoxColumn
{
- protected override object PrepareCellForEdit(IControl editingElement, RoutedEventArgs editingEventArgs)
- {
- return base.PrepareCellForEdit(editingElement, editingEventArgs);
- }
protected override IControl GenerateEditingElementDirect(DataGridCell cell, object dataItem)
{
var ele = base.GenerateEditingElementDirect(cell, dataItem) as CheckBox;
ele.Checked += EditingElement_Checked;
+ ele.Unchecked += EditingElement_Checked;
+ ele.Indeterminate += EditingElement_Checked;
return ele;
}
private void EditingElement_Checked(object sender, RoutedEventArgs e)
{
- var cbox = sender as CheckBox;
- var gEntry = cbox.DataContext as GridEntry2;
- gEntry.Remove = cbox.IsChecked;
+ if (sender is CheckBox cbox && cbox.DataContext is GridEntry2 gentry)
+ {
+ gentry.Remove = cbox.IsChecked;
+ FindDataGridParent(cbox)?.CommitEdit(DataGridEditingUnit.Cell, false);
+ }
+ }
+
+ DataGrid? FindDataGridParent(IControl? control)
+ {
+ if (control?.Parent is null) return null;
+ else if (control?.Parent is DataGrid dg) return dg;
+ else return FindDataGridParent(control?.Parent);
}
}
}
diff --git a/Source/LibationWinForms/AvaloniaUI/ViewModels/GridEntryBindingList2.cs b/Source/LibationWinForms/AvaloniaUI/ViewModels/GridEntryBindingList2.cs
index 1de83350..8e7570b4 100644
--- a/Source/LibationWinForms/AvaloniaUI/ViewModels/GridEntryBindingList2.cs
+++ b/Source/LibationWinForms/AvaloniaUI/ViewModels/GridEntryBindingList2.cs
@@ -25,71 +25,45 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
*/
public class GridEntryBindingList2 : ObservableCollection
{
- public GridEntryBindingList2(IEnumerable enumeration) : base(new List(enumeration))
- {
- foreach (var item in enumeration)
- item.PropertyChanged += Item_PropertyChanged;
- }
+ public GridEntryBindingList2(IEnumerable enumeration)
+ : base(new List(enumeration)) { }
+ public GridEntryBindingList2(List list)
+ : base(list) { }
+
+ public List InternalList => Items as List;
/// All items in the list, including those filtered out.
public List AllItems() => Items.Concat(FilterRemoved).ToList();
/// When true, itms will not be checked filtered by search criteria on item changed
public bool SuspendFilteringOnUpdate { get; set; }
public string Filter { get => FilterString; set => ApplyFilter(value); }
- protected MemberComparer Comparer { get; } = new();
/// Items that were removed from the base list due to filtering
private readonly List FilterRemoved = new();
private string FilterString;
private SearchResultSet SearchResults;
- private bool isSorted;
#region Items Management
public new void Remove(GridEntry2 entry)
{
- entry.PropertyChanged -= Item_PropertyChanged;
FilterRemoved.Add(entry);
base.Remove(entry);
}
- protected override void RemoveItem(int index)
- {
- var item = Items[index];
- item.PropertyChanged -= Item_PropertyChanged;
- base.RemoveItem(index);
- }
-
- protected override void ClearItems()
- {
- foreach (var item in Items)
- item.PropertyChanged -= Item_PropertyChanged;
- base.ClearItems();
- }
-
protected override void InsertItem(int index, GridEntry2 item)
{
- item.PropertyChanged += Item_PropertyChanged;
FilterRemoved.Remove(item);
base.InsertItem(index, item);
}
- private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e)
- {
- //Don't audo-sort Remove column or else Avalonia will crash.
- if (isSorted && e.PropertyName == Comparer.PropertyName && e.PropertyName != nameof(GridEntry.Remove))
- {
- Sort();
-
- OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
- return;
- }
- }
-
#endregion
#region Filtering
+ public void ResetCollection()
+ => OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+
private void ApplyFilter(string filterString)
{
if (filterString != FilterString)
@@ -125,18 +99,6 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
}
}
- if (isSorted)
- Sort();
- else
- {
- //No user sort is applied, so do default sorting by DateAdded, descending
- Comparer.PropertyName = nameof(GridEntry.DateAdded);
- Comparer.Direction = ListSortDirection.Descending;
- Sort();
- }
-
- OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
-
FilterString = null;
SearchResults = null;
}
@@ -182,52 +144,5 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
}
#endregion
-
- #region Sorting
-
- public void DoSortCore(string propertyName)
- {
- if (isSorted && Comparer.PropertyName == propertyName)
- {
- Comparer.Direction = ~Comparer.Direction & ListSortDirection.Descending;
- }
- else
- {
- Comparer.PropertyName = propertyName;
- Comparer.Direction = ListSortDirection.Descending;
- }
-
- Sort();
-
- isSorted = true;
-
- OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
- }
-
- protected void Sort()
- {
- var itemsList = (List)Items;
-
- var children = itemsList.BookEntries().Where(i => i.Parent is not null).ToList();
-
- var sortedItems = itemsList.Except(children).OrderBy(ge => ge, Comparer).ToList();
-
- itemsList.Clear();
-
- //Only add parentless items at this stage. After these items are added in the
- //correct sorting order, go back and add the children beneath their parents.
- itemsList.AddRange(sortedItems);
-
- foreach (var parent in children.Select(c => c.Parent).Distinct())
- {
- var pIndex = itemsList.IndexOf(parent);
-
- //children should always be sorted by series index.
- foreach (var c in children.Where(c => c.Parent == parent).OrderBy(c => c.SeriesIndex))
- itemsList.Insert(++pIndex, c);
- }
- }
-
- #endregion
}
}
diff --git a/Source/LibationWinForms/AvaloniaUI/ViewModels/ItemsRepeaterPageViewModel.cs b/Source/LibationWinForms/AvaloniaUI/ViewModels/ItemsRepeaterPageViewModel.cs
deleted file mode 100644
index 22f14569..00000000
--- a/Source/LibationWinForms/AvaloniaUI/ViewModels/ItemsRepeaterPageViewModel.cs
+++ /dev/null
@@ -1,160 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Collections.ObjectModel;
-using Avalonia.Media;
-using ReactiveUI;
-
-namespace LibationWinForms.AvaloniaUI.ViewModels
-{
- public class ProcessQueueItems : ObservableCollection
- {
- public ProcessQueueItems(IEnumerable items) :base(items) { }
-
- public void MoveFirst(ItemsRepeaterPageViewModel.Item item)
- {
- var index = Items.IndexOf(item);
- if (index < 1) return;
-
- Move(index, 0);
- }
- public void MoveUp(ItemsRepeaterPageViewModel.Item item)
- {
- var index = Items.IndexOf(item);
- if (index < 1) return;
-
- Move(index, index - 1);
- }
- public void MoveDown(ItemsRepeaterPageViewModel.Item item)
- {
- var index = Items.IndexOf(item);
- if (index < 0 || index > Items.Count - 2) return;
-
- Move(index, index + 1);
- }
-
- public void MoveLast(ItemsRepeaterPageViewModel.Item item)
- {
- var index = Items.IndexOf(item);
- if (index < 0 || index > Items.Count - 2) return;
-
- Move(index, Items.Count - 1);
- }
- }
-
-
- public class ItemsRepeaterPageViewModel : ViewModelBase
- {
- private int _newItemIndex = 1;
- private int _newGenerationIndex = 0;
- private ProcessQueueItems _items;
-
- public ItemsRepeaterPageViewModel()
- {
- _items = CreateItems();
- }
-
- public ProcessQueueItems Items
- {
- get => _items;
- set => this.RaiseAndSetIfChanged(ref _items, value);
- }
-
- public Item? SelectedItem { get; set; }
-
- public void AddItem()
- {
- var index = SelectedItem != null ? Items.IndexOf(SelectedItem) : -1;
- Items.Insert(index + 1, new Item(index + 1, $"New Item {_newItemIndex++}"));
- }
-
- public void RemoveItem()
- {
- if (SelectedItem is not null)
- {
- Items.Remove(SelectedItem);
- SelectedItem = null;
- }
- else if (Items.Count > 0)
- {
- Items.RemoveAt(Items.Count - 1);
- }
- }
-
- public void RandomizeHeights()
- {
- var random = new Random();
-
- foreach (var i in Items)
- {
- i.Height = random.Next(240) + 10;
- }
- }
-
- public void ResetItems()
- {
- Items = CreateItems();
- }
-
- private ProcessQueueItems CreateItems()
- {
- var suffix = _newGenerationIndex == 0 ? string.Empty : $"[{_newGenerationIndex.ToString()}]";
-
- _newGenerationIndex++;
-
- return new ProcessQueueItems(
- Enumerable.Range(1, 100).Select(i => new Item(i, $"Item {i.ToString()} {suffix}")));
- }
-
- public class Item : ViewModelBase
- {
- private double _height = double.NaN;
- static Random rnd = new Random();
-
- public Item(int index, string text)
- {
- Index = index;
- Text = text;
- Narrator = "Narrator " + index;
- Author = "Author " + index;
- Title = "Book " + index + ": This is a book title.\r\nThis is line 2 of the book title";
-
- Progress = rnd.Next(0, 101);
- ETA = "ETA: 01:14";
-
- IsDownloading = rnd.Next(0, 2) == 0;
-
- if (!IsDownloading)
- IsFinished = rnd.Next(0, 2) == 0;
-
- if (IsDownloading)
- Title += "\r\nDOWNLOADING";
- else if (IsFinished)
- Title += "\r\nFINISHED";
- else
- Title += "\r\nQUEUED";
- }
-
- public bool IsFinished { get; }
- public bool IsDownloading { get; }
- public bool Queued => !IsFinished && !IsDownloading;
-
-
- public int Index { get; }
- public string Text { get; }
- public string ETA { get; }
- public string Narrator { get; }
- public string Author { get; }
- public string Title { get; }
- public int Progress { get; }
-
- public double Height
- {
- get => _height;
- set => this.RaiseAndSetIfChanged(ref _height, value);
- }
- }
- }
-}
diff --git a/Source/LibationWinForms/AvaloniaUI/ViewModels/MainWindowViewModel.cs b/Source/LibationWinForms/AvaloniaUI/ViewModels/MainWindowViewModel.cs
deleted file mode 100644
index 8f281313..00000000
--- a/Source/LibationWinForms/AvaloniaUI/ViewModels/MainWindowViewModel.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using ApplicationServices;
-using Avalonia.Collections;
-using DataLayer;
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Globalization;
-using System.Linq;
-using System.Text;
-
-namespace LibationWinForms.AvaloniaUI.ViewModels
-{
- public class MainWindowViewModel : ViewModelBase
- {
- public string Greeting => "Welcome to Avalonia!";
- public GridEntryBindingList2 People { get; set; }
- public MainWindowViewModel(IEnumerable dbBooks)
- {
- var geList = dbBooks
- .Where(lb => lb.Book.IsProduct())
- .Select(b => new LibraryBookEntry2(b))
- .Cast()
- .ToList();
-
- var episodes = dbBooks.Where(lb => lb.Book.IsEpisodeChild());
-
- var seriesBooks = dbBooks.Where(lb => lb.Book.IsEpisodeParent()).ToList();
-
- foreach (var parent in seriesBooks)
- {
- var seriesEpisodes = episodes.FindChildren(parent);
-
- if (!seriesEpisodes.Any()) continue;
-
- var seriesEntry = new SeriesEntrys2(parent, seriesEpisodes);
-
- geList.Add(seriesEntry);
- geList.AddRange(seriesEntry.Children);
- }
-
- People = new GridEntryBindingList2(geList.OrderByDescending(e => e.DateAdded));
- People.CollapseAll();
-
- }
- }
-
-}
diff --git a/Source/LibationWinForms/AvaloniaUI/ViewModels/ProductsDisplayViewModel.cs b/Source/LibationWinForms/AvaloniaUI/ViewModels/ProductsDisplayViewModel.cs
index a804b9db..d0cd021f 100644
--- a/Source/LibationWinForms/AvaloniaUI/ViewModels/ProductsDisplayViewModel.cs
+++ b/Source/LibationWinForms/AvaloniaUI/ViewModels/ProductsDisplayViewModel.cs
@@ -12,8 +12,7 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
{
public class ProductsDisplayViewModel : ViewModelBase
{
- public string Greeting => "Welcome to Avalonia!";
- public GridEntryBindingList2 People { get; set; }
+ public GridEntryBindingList2 GridEntries { get; set; }
public ProductsDisplayViewModel(IEnumerable dbBooks)
{
var geList = dbBooks
@@ -38,9 +37,8 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
geList.AddRange(seriesEntry.Children);
}
- People = new GridEntryBindingList2(geList.OrderByDescending(e => e.DateAdded));
- People.CollapseAll();
+ GridEntries = new GridEntryBindingList2(geList.OrderByDescending(e => e.DateAdded));
+ GridEntries.CollapseAll();
}
}
-
}
diff --git a/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.BackupCounts.axaml.cs b/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.BackupCounts.axaml.cs
index 0cfbad94..ace398d6 100644
--- a/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.BackupCounts.axaml.cs
+++ b/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.BackupCounts.axaml.cs
@@ -20,7 +20,7 @@ namespace LibationWinForms.AvaloniaUI.Views
beginPdfBackupsToolStripMenuItem.Format(0);
pdfsCountsLbl.Text = "| [Calculating backed up PDFs]";
- Opened += setBackupCounts;
+ Load += setBackupCounts;
LibraryCommands.LibrarySizeChanged += setBackupCounts;
LibraryCommands.BookUserDefinedItemCommitted += setBackupCounts;
diff --git a/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.QuickFilters.axaml.cs b/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.QuickFilters.axaml.cs
index 061a9f58..58a725d0 100644
--- a/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.QuickFilters.axaml.cs
+++ b/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.QuickFilters.axaml.cs
@@ -14,8 +14,8 @@ namespace LibationWinForms.AvaloniaUI.Views
{
private void Configure_QuickFilters()
{
- Opened += updateFirstFilterIsDefaultToolStripMenuItem;
- Opened += updateFiltersMenu;
+ Load += updateFirstFilterIsDefaultToolStripMenuItem;
+ Load += updateFiltersMenu;
QuickFilters.UseDefaultChanged += updateFirstFilterIsDefaultToolStripMenuItem;
QuickFilters.Updated += updateFiltersMenu;
}
diff --git a/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.RemoveBooks.axaml.cs b/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.RemoveBooks.axaml.cs
index 1043eb25..e078ad25 100644
--- a/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.RemoveBooks.axaml.cs
+++ b/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.RemoveBooks.axaml.cs
@@ -1,11 +1,7 @@
using AudibleUtilities;
-using Avalonia.Controls;
using LibationWinForms.Dialogs;
using System;
-using System.Collections.Generic;
using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace LibationWinForms.AvaloniaUI.Views
{
@@ -16,6 +12,7 @@ namespace LibationWinForms.AvaloniaUI.Views
{
removeBooksBtn.IsVisible = false;
doneRemovingBtn.IsVisible = false;
+ removeLibraryBooksToolStripMenuItem.Click += removeLibraryBooksToolStripMenuItem_Click;
}
public async void removeBooksBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
diff --git a/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.ScanAuto.axaml.cs b/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.ScanAuto.axaml.cs
index 0f3a0f3a..d325a734 100644
--- a/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.ScanAuto.axaml.cs
+++ b/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.ScanAuto.axaml.cs
@@ -46,9 +46,9 @@ namespace LibationWinForms.AvaloniaUI.Views
};
// load init state to menu checkbox
- Opened += updateAutoScanLibraryToolStripMenuItem;
+ Load += updateAutoScanLibraryToolStripMenuItem;
// if enabled: begin on load
- Opened += startAutoScan;
+ Load += startAutoScan;
// if new 'default' account is added, run autoscan
AccountsSettingsPersister.Saving += accountsPreSave;
diff --git a/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.ScanManual.axaml.cs b/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.ScanManual.axaml.cs
index bf012a24..47efab3d 100644
--- a/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.ScanManual.axaml.cs
+++ b/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.ScanManual.axaml.cs
@@ -16,7 +16,7 @@ namespace LibationWinForms.AvaloniaUI.Views
{
private void Configure_ScanManual()
{
- Opened += refreshImportMenu;
+ Load += refreshImportMenu;
AccountsSettingsPersister.Saved += refreshImportMenu;
}
@@ -33,8 +33,21 @@ namespace LibationWinForms.AvaloniaUI.Views
scanLibraryOfSomeAccountsToolStripMenuItem.IsVisible = count > 1;
removeLibraryBooksToolStripMenuItem.IsVisible = count > 0;
- removeSomeAccountsToolStripMenuItem.IsVisible = count > 1;
- removeAllAccountsToolStripMenuItem.IsVisible = count > 1;
+
+ //Avalonia will not fire the Click event for a MenuItem with children,
+ //so if only 1 account, remove the children. Otherwise add children
+ //for multiple accounts.
+ removeLibraryBooksToolStripMenuItem.Items = null;
+
+ if (count > 1)
+ {
+ removeLibraryBooksToolStripMenuItem.Items =
+ new List
+ {
+ removeSomeAccountsToolStripMenuItem,
+ removeAllAccountsToolStripMenuItem
+ };
+ }
}
public void noAccountsYetAddAccountToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)
diff --git a/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.axaml b/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.axaml
index 8cfe327b..633f312b 100644
--- a/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.axaml
+++ b/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.axaml
@@ -90,11 +90,11 @@
+ Name="productsDisplay" />
diff --git a/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.axaml.cs b/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.axaml.cs
index d2254916..681fb3b7 100644
--- a/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.axaml.cs
+++ b/Source/LibationWinForms/AvaloniaUI/Views/MainWindow/MainWindow.axaml.cs
@@ -43,58 +43,13 @@ namespace LibationWinForms.AvaloniaUI.Views
Configure_NonUI();
{
+ this.Load += (_, _) => productsDisplay.Display();
LibraryCommands.LibrarySizeChanged += (_, __) => Dispatcher.UIThread.Post(() => productsDisplay.Display());
}
}
- /*
- MenuItem importToolStripMenuItem;
- MenuItem autoScanLibraryToolStripMenuItem;
- CheckBox autoScanLibraryToolStripMenuItemCheckbox;
- MenuItem noAccountsYetAddAccountToolStripMenuItem;
- MenuItem scanLibraryToolStripMenuItem;
- MenuItem scanLibraryOfAllAccountsToolStripMenuItem;
- MenuItem scanLibraryOfSomeAccountsToolStripMenuItem;
- MenuItem removeLibraryBooksToolStripMenuItem;
- MenuItem removeAllAccountsToolStripMenuItem;
- MenuItem removeSomeAccountsToolStripMenuItem;
- MenuItem liberateToolStripMenuItem;
- MenuItem beginBookBackupsToolStripMenuItem;
- MenuItem beginPdfBackupsToolStripMenuItem;
- MenuItem convertAllM4bToMp3ToolStripMenuItem;
- MenuItem liberateVisibleToolStripMenuItem_LiberateMenu;
- MenuItem exportToolStripMenuItem;
- MenuItem exportLibraryToolStripMenuItem;
- MenuItem quickFiltersToolStripMenuItem;
- MenuItem firstFilterIsDefaultToolStripMenuItem;
- CheckBox firstFilterIsDefaultToolStripMenuItem_Checkbox;
- MenuItem editQuickFiltersToolStripMenuItem;
- MenuItem visibleBooksToolStripMenuItem;
- MenuItem liberateVisibleToolStripMenuItem_VisibleBooksMenu;
- MenuItem replaceTagsToolStripMenuItem;
- MenuItem setDownloadedToolStripMenuItem;
- MenuItem removeToolStripMenuItem;
- MenuItem settingsToolStripMenuItem;
- MenuItem accountsToolStripMenuItem;
- MenuItem basicSettingsToolStripMenuItem;
- MenuItem aboutToolStripMenuItem;
+ public event EventHandler Load;
-
- StackPanel scanningToolStripMenuItem;
- TextBlock scanningToolStripMenuItem_Text;
-
- Button filterHelpBtn;
- Button addQuickFilterBtn;
- TextBox filterSearchTb;
- Button filterBtn;
- Button toggleQueueHideBtn;
-
- StackPanel removeBooksButtonsPanel;
- Button removeBooksBtn;
-
- SplitView splitContainer1;
- ProductsDisplay2 productsDisplay;
- ProcessQueueControl2 processBookQueue1;
- */
+ public void OnLoad() => Load?.Invoke(this, EventArgs.Empty);
private void FindAllControls()
{
diff --git a/Source/LibationWinForms/AvaloniaUI/Views/ProductsDisplay2.axaml b/Source/LibationWinForms/AvaloniaUI/Views/ProductsDisplay2.axaml
index 9065dd7f..ed3449e4 100644
--- a/Source/LibationWinForms/AvaloniaUI/Views/ProductsDisplay2.axaml
+++ b/Source/LibationWinForms/AvaloniaUI/Views/ProductsDisplay2.axaml
@@ -4,14 +4,16 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:LibationWinForms.AvaloniaUI.Views"
xmlns:controls="clr-namespace:LibationWinForms.AvaloniaUI.Controls"
- mc:Ignorable="d" d:DesignWidth="1560" d:DesignHeight="700"
+ mc:Ignorable="d" d:DesignWidth="1560" d:DesignHeight="400"
x:Class="LibationWinForms.AvaloniaUI.Views.ProductsDisplay2">
+
+
-
+
-
+
@@ -24,8 +26,9 @@
+
-
+
@@ -103,17 +106,17 @@
-
+
-
+
-
+
@@ -127,17 +130,17 @@
-
+
-
+
-
+
diff --git a/Source/LibationWinForms/AvaloniaUI/Views/ProductsDisplay2.axaml.cs b/Source/LibationWinForms/AvaloniaUI/Views/ProductsDisplay2.axaml.cs
index 865065eb..ae2c344c 100644
--- a/Source/LibationWinForms/AvaloniaUI/Views/ProductsDisplay2.axaml.cs
+++ b/Source/LibationWinForms/AvaloniaUI/Views/ProductsDisplay2.axaml.cs
@@ -4,11 +4,14 @@ using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using DataLayer;
+using Dinah.Core.DataBinding;
using FileLiberator;
using LibationFileManager;
using LibationWinForms.AvaloniaUI.ViewModels;
using System;
+using System.Collections;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
@@ -20,6 +23,7 @@ namespace LibationWinForms.AvaloniaUI.Views
public event EventHandler VisibleCountChanged;
public event EventHandler RemovableCountChanged;
public event EventHandler LiberateClicked;
+ public event EventHandler InitialLoaded;
private ProductsDisplayViewModel _viewModel;
private GridEntryBindingList2 bindingList => productsGrid.Items as GridEntryBindingList2;
@@ -42,11 +46,10 @@ namespace LibationWinForms.AvaloniaUI.Views
productsGrid.CanUserSortColumns = true;
removeGVColumn = productsGrid.Columns[0];
-
- var dbBooks = DbContexts.GetLibrary_Flat_NoTracking(includeParents: true);
- productsGrid.DataContext = _viewModel = new ProductsDisplayViewModel(dbBooks);
-
- this.AttachedToVisualTree +=(_, _) => VisibleCountChanged?.Invoke(this, bindingList.BookEntries().Count());
+ }
+ public override void EndInit()
+ {
+ base.EndInit();
}
private void InitializeComponent()
@@ -54,20 +57,106 @@ namespace LibationWinForms.AvaloniaUI.Views
AvaloniaXamlLoader.Load(this);
}
+ private class RowComparer : IComparer
+ {
+ private static readonly System.Reflection.PropertyInfo HeaderCellPi = typeof(DataGridColumn).GetProperty("HeaderCell", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ private static readonly System.Reflection.PropertyInfo CurrentSortingStatePi = typeof(DataGridColumnHeader).GetProperty("CurrentSortingState", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+
+ public DataGridColumn Column { get; init; }
+ public string PropertyName { get; init; }
+ public ListSortDirection? SortDirection { get; set; }
+
+ ///
+ /// This compare method ensures that all top-level grid entries (standalone books or series parents)
+ /// are sorted by PropertyName while all episodes remain immediately beneath their parents and remain
+ /// sorted by series index, ascending.
+ ///
+ public int Compare(object x, object y)
+ {
+ if (x is null) return -1;
+ if (y is null) return 1;
+ if (x is null && y is null) return 0;
+
+ var geA = (GridEntry2)x;
+ var geB = (GridEntry2)y;
+
+ SortDirection ??= GetSortOrder(Column);
+
+ SeriesEntrys2 parentA = null;
+ SeriesEntrys2 parentB = null;
+
+ if (geA is LibraryBookEntry2 lbA && lbA.Parent is SeriesEntrys2 seA)
+ parentA = seA;
+ if (geB is LibraryBookEntry2 lbB && lbB.Parent is SeriesEntrys2 seB)
+ parentB = seB;
+
+ //both a and b are standalone
+ if (parentA is null && parentB is null)
+ return Compare(geA, geB);
+
+ //a is a standalone, b is a child
+ if (parentA is null && parentB is not null)
+ {
+ // b is a child of a, parent is always first
+ if (parentB == geA)
+ return SortDirection is ListSortDirection.Ascending ? -1 : 1;
+ else
+ return Compare(geA, parentB);
+ }
+
+ //a is a child, b is a standalone
+ if (parentA is not null && parentB is null)
+ {
+ // a is a child of b, parent is always first
+ if (parentA == geB)
+ return SortDirection is ListSortDirection.Ascending ? 1 : -1;
+ else
+ return Compare(parentA, geB);
+ }
+
+ //both are children of the same series, always present in order of series index, ascending
+ if (parentA == parentB)
+ return geA.SeriesIndex.CompareTo(geB.SeriesIndex) * (SortDirection is ListSortDirection.Ascending ? 1 : -1);
+
+ //a and b are children of different series.
+ return Compare(parentA, parentB);
+ }
+
+ private static ListSortDirection? GetSortOrder(DataGridColumn column)
+ => CurrentSortingStatePi.GetValue(HeaderCellPi.GetValue(column)) as ListSortDirection?;
+
+ private int Compare(GridEntry2 x, GridEntry2 y)
+ {
+ var val1 = x.GetMemberValue(PropertyName);
+ var val2 = y.GetMemberValue(PropertyName);
+
+ return x.GetMemberComparer(val1.GetType()).Compare(val1, val2);
+ }
+ }
+
+ Dictionary ColumnComparers = new();
+ DataGridColumn CurrentSortColumn;
+
private void Dg1_Sorting(object sender, DataGridColumnEventArgs e)
{
- bindingList.DoSortCore(e.Column.SortMemberPath);
- e.Handled = true;
+ if (!ColumnComparers.ContainsKey(e.Column))
+ ColumnComparers[e.Column] = new RowComparer
+ {
+ Column = e.Column,
+ PropertyName = e.Column.SortMemberPath
+ };
+
+ //Force the comparer to get the current sort order. We can't
+ //retrieve it from inside this event handler because Avalonia
+ //doesn't set the property until after this event.
+ ColumnComparers[e.Column].SortDirection = null;
+
+ e.Column.CustomSortComparer = ColumnComparers[e.Column];
+ CurrentSortColumn = e.Column;
}
#region Button controls
- public void Remove_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args)
- {
- productsGrid.CommitEdit(DataGridEditingUnit.Cell, true);
- RemovableCountChanged?.Invoke(this, GetAllBookEntries().Count(lbe => lbe.Remove is true));
- }
-
public void LiberateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args)
{
var button = args.Source as Button;
@@ -228,6 +317,12 @@ namespace LibationWinForms.AvaloniaUI.Views
{
// don't return early if lib size == 0. this will not update correctly if all books are removed
var dbBooks = DbContexts.GetLibrary_Flat_NoTracking(includeParents: true);
+ if (productsGrid.DataContext is null)
+ {
+ productsGrid.DataContext = _viewModel = new ProductsDisplayViewModel(dbBooks);
+ InitialLoaded?.Invoke(this, EventArgs.Empty);
+ VisibleCountChanged?.Invoke(this, bindingList.BookEntries().Count());
+ }
UpdateGrid(dbBooks);
}
catch (Exception ex)
@@ -387,6 +482,14 @@ namespace LibationWinForms.AvaloniaUI.Views
if (visibleCount != bindingList.Count)
VisibleCountChanged?.Invoke(this, bindingList.BookEntries().Count());
+
+ //Re-sort after filtering
+ if (CurrentSortColumn is null)
+ bindingList.InternalList.Sort((i1, i2) => i2.DateAdded.CompareTo(i1.DateAdded));
+ else
+ CurrentSortColumn?.Sort(ColumnComparers[CurrentSortColumn].SortDirection.Value);
+
+ bindingList.ResetCollection();
}
#endregion
diff --git a/Source/LibationWinForms/LibationWinForms.csproj b/Source/LibationWinForms/LibationWinForms.csproj
index 4316a2c1..579d53ad 100644
--- a/Source/LibationWinForms/LibationWinForms.csproj
+++ b/Source/LibationWinForms/LibationWinForms.csproj
@@ -55,6 +55,7 @@
+
diff --git a/Source/LibationWinForms/Program.cs b/Source/LibationWinForms/Program.cs
index 31876365..e271d278 100644
--- a/Source/LibationWinForms/Program.cs
+++ b/Source/LibationWinForms/Program.cs
@@ -19,6 +19,8 @@ namespace LibationWinForms
[return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
static extern bool AllocConsole();
+ static bool UseAvaloniaUI = true;
+
[STAThread]
static void Main()
{
@@ -76,9 +78,10 @@ namespace LibationWinForms
// global exception handling (ShowAdminAlert) attempts to use logging. only call it after logging has been init'd
postLoggingGlobalExceptionHandling();
-
- BuildAvaloniaApp().StartWithClassicDesktopLifetime(null);
- //System.Windows.Forms.Application.Run(new Form1());
+ if (UseAvaloniaUI)
+ BuildAvaloniaApp().StartWithClassicDesktopLifetime(null);
+ else
+ System.Windows.Forms.Application.Run(new Form1());
}
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure()