diff --git a/Source/LibationWinForms/AvaloniaUI/Controls/DataGridCheckBoxColumnExt.axaml.cs b/Source/LibationWinForms/AvaloniaUI/Controls/DataGridCheckBoxColumnExt.axaml.cs
index 9e82e369..b481dda4 100644
--- a/Source/LibationWinForms/AvaloniaUI/Controls/DataGridCheckBoxColumnExt.axaml.cs
+++ b/Source/LibationWinForms/AvaloniaUI/Controls/DataGridCheckBoxColumnExt.axaml.cs
@@ -1,15 +1,79 @@
using Avalonia.Controls;
-using Avalonia.Controls.Utils;
using Avalonia.Interactivity;
using LibationWinForms.AvaloniaUI.ViewModels;
using System;
-using System.Linq;
+using System.Reflection;
namespace LibationWinForms.AvaloniaUI.Controls
{
- /// The purpose of this extension WAS to immediately commit any check state changes to the viewmodel, but for the life of me I cannot get it to work!
+ /// The purpose of this extension it to immediately commit any check
+ /// state changes to the viewmodel. There must be a better way to do this, but
+ /// I sure as shit can't find it.
public partial class DataGridCheckBoxColumnExt : DataGridCheckBoxColumn
{
-
+ Func _owningGrid_get;
+ Func _endCellEdit;
+ Func _waitForLostFocus;
+ public DataGrid OwningGrid
+ {
+ get
+ {
+ if (_owningGrid_get == null)
+ {
+ var pi = typeof(DataGridColumn).GetProperty(nameof(OwningGrid), BindingFlags.NonPublic | BindingFlags.Instance);
+ var mi = pi.GetGetMethod(true);
+ _owningGrid_get = mi.CreateDelegate>(this);
+ }
+ return _owningGrid_get();
+ }
+ }
+
+ public Func WaitForLostFocus
+ {
+ get
+ {
+ if (_endCellEdit == null)
+ {
+ var mi = typeof(DataGrid).GetMethod(nameof(WaitForLostFocus), BindingFlags.NonPublic | BindingFlags.Instance);
+ _waitForLostFocus = mi.CreateDelegate>(OwningGrid);
+ }
+ return _waitForLostFocus;
+ }
+ }
+
+ public Func EndCellEdit
+ {
+ get
+ {
+ if (_endCellEdit == null)
+ {
+ var mi = typeof(DataGrid).GetMethod(nameof(EndCellEdit), BindingFlags.NonPublic | BindingFlags.Instance);
+ _endCellEdit = mi.CreateDelegate>(OwningGrid);
+ }
+ return _endCellEdit;
+ }
+ }
+
+ 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)
+ {
+ if (sender is CheckBox cbox && cbox.DataContext is GridEntry2 gentry)
+ {
+ var check = cbox.IsChecked;
+ WaitForLostFocus(() =>
+ {
+ EndCellEdit(DataGridEditAction.Cancel, true, true, false);
+ gentry.Remove = check;
+ });
+ }
+ }
}
}
diff --git a/Source/LibationWinForms/AvaloniaUI/ViewModels/GridEntry2.cs b/Source/LibationWinForms/AvaloniaUI/ViewModels/GridEntry2.cs
index 21e684b1..3f866bf3 100644
--- a/Source/LibationWinForms/AvaloniaUI/ViewModels/GridEntry2.cs
+++ b/Source/LibationWinForms/AvaloniaUI/ViewModels/GridEntry2.cs
@@ -57,7 +57,6 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
public string ProductRating { get => _productRating; protected set { this.RaiseAndSetIfChanged(ref _productRating, value); } }
public string MyRating { get => _myRating; protected set { this.RaiseAndSetIfChanged(ref _myRating, value); } }
-
protected bool? _remove = false;
public abstract bool? Remove { get; set; }
public abstract LiberateButtonStatus2 Liberate { get; }
diff --git a/Source/LibationWinForms/AvaloniaUI/ViewModels/GridEntryBindingList2.cs b/Source/LibationWinForms/AvaloniaUI/ViewModels/GridEntryBindingList2.cs
index b2c42b57..9a881515 100644
--- a/Source/LibationWinForms/AvaloniaUI/ViewModels/GridEntryBindingList2.cs
+++ b/Source/LibationWinForms/AvaloniaUI/ViewModels/GridEntryBindingList2.cs
@@ -128,13 +128,11 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
public void ExpandItem(SeriesEntrys2 sEntry)
{
- var sindex = Items.IndexOf(sEntry);
-
foreach (var episode in FilterRemoved.BookEntries().Where(b => b.Parent == sEntry).ToList())
{
if (SearchResults is null || SearchResults.Docs.Any(d => d.ProductId == episode.AudibleProductId))
{
- InsertItem(++sindex, episode);
+ Add(episode);
}
}
sEntry.Liberate.Expanded = true;
diff --git a/Source/LibationWinForms/AvaloniaUI/ViewModels/RowComparer.cs b/Source/LibationWinForms/AvaloniaUI/ViewModels/RowComparer.cs
new file mode 100644
index 00000000..8a3585bc
--- /dev/null
+++ b/Source/LibationWinForms/AvaloniaUI/ViewModels/RowComparer.cs
@@ -0,0 +1,99 @@
+using Avalonia.Controls;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace LibationWinForms.AvaloniaUI.ViewModels
+{
+ //TODO keep episodes beneath their parents when other entries compare equal (because as it stands, children always compare > parents.
+ ///
+ /// This compare class 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.
+ ///
+ internal 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; private set; }
+ public ListSortDirection? SortDirection { get; set; }
+
+ public RowComparer(DataGridColumn column)
+ {
+ Column = column;
+ PropertyName = Column.SortMemberPath;
+ }
+
+
+ public int Compare(object x, object y)
+ {
+ if (x is null && y is not null) return -1;
+ if (x is not null && y is null) return 1;
+ if (x is null && y is null) return 0;
+
+ var geA = (GridEntry2)x;
+ var geB = (GridEntry2)y;
+
+ SortDirection ??= GetSortOrder();
+
+ 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);
+ }
+
+ //Avalonia doesn't expose the column's CurrentSortingState, so we must get it through reflection
+ private ListSortDirection? GetSortOrder()
+ => 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);
+ }
+ }
+}
diff --git a/Source/LibationWinForms/AvaloniaUI/Views/ProductsDisplay2.axaml b/Source/LibationWinForms/AvaloniaUI/Views/ProductsDisplay2.axaml
index 50361033..55a4323f 100644
--- a/Source/LibationWinForms/AvaloniaUI/Views/ProductsDisplay2.axaml
+++ b/Source/LibationWinForms/AvaloniaUI/Views/ProductsDisplay2.axaml
@@ -11,7 +11,7 @@
-
+
diff --git a/Source/LibationWinForms/AvaloniaUI/Views/ProductsDisplay2.axaml.cs b/Source/LibationWinForms/AvaloniaUI/Views/ProductsDisplay2.axaml.cs
index 5c40f5b1..4c8b6bf3 100644
--- a/Source/LibationWinForms/AvaloniaUI/Views/ProductsDisplay2.axaml.cs
+++ b/Source/LibationWinForms/AvaloniaUI/Views/ProductsDisplay2.axaml.cs
@@ -124,7 +124,27 @@ namespace LibationWinForms.AvaloniaUI.Views
};
}
- #endregion
+ #endregion
+
+ #region Filter
+
+ public void Filter(string searchString)
+ {
+ int visibleCount = bindingList.Count;
+
+ if (string.IsNullOrEmpty(searchString))
+ bindingList.RemoveFilter();
+ else
+ bindingList.Filter = searchString;
+
+ if (visibleCount != bindingList.Count)
+ VisibleCountChanged?.Invoke(this, bindingList.BookEntries().Count());
+
+ //Re-sort after filtering
+ ReSort();
+ }
+
+ #endregion
#region Sorting
@@ -150,7 +170,10 @@ namespace LibationWinForms.AvaloniaUI.Views
private void ReSort()
{
if (CurrentSortColumn is null)
+ {
bindingList.InternalList.Sort((i1, i2) => i2.DateAdded.CompareTo(i1.DateAdded));
+ bindingList.ResetCollection();
+ }
else
CurrentSortColumn.Sort(((RowComparer)CurrentSortColumn.CustomSortComparer).SortDirection ?? ListSortDirection.Ascending);
}
@@ -168,88 +191,6 @@ namespace LibationWinForms.AvaloniaUI.Views
CurrentSortColumn = e.Column;
}
- 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; }
-
- public RowComparer(DataGridColumn column)
- {
- Column = column;
- PropertyName = column.SortMemberPath;
- }
-
- ///
- /// 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();
-
- 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 ListSortDirection? GetSortOrder()
- => 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);
- }
- }
#endregion
@@ -571,29 +512,7 @@ namespace LibationWinForms.AvaloniaUI.Views
existingEpisodeEntry.UpdateLibraryBook(episodeBook);
}
- #endregion
-
- #region Filter
-
- public void Filter(string searchString)
- {
- int visibleCount = bindingList.Count;
-
- if (string.IsNullOrEmpty(searchString))
- bindingList.RemoveFilter();
- else
- bindingList.Filter = searchString;
-
- if (visibleCount != bindingList.Count)
- VisibleCountChanged?.Invoke(this, bindingList.BookEntries().Count());
-
- //Re-sort after filtering
-
- bindingList.ResetCollection();
- ReSort();
- }
-
- #endregion
+ #endregion
#region Column Customizations