Move filter query and RowComparer into UI base
This commit is contained in:
parent
e52772826a
commit
666b5d83df
@ -23,7 +23,7 @@ namespace LibationAvalonia.ViewModels
|
||||
/// <summary>Backing list of all grid entries</summary>
|
||||
private readonly AvaloniaList<IGridEntry> SOURCE = new();
|
||||
/// <summary>Grid entries included in the filter set. If null, all grid entries are shown</summary>
|
||||
private List<IGridEntry> FilteredInGridEntries;
|
||||
private HashSet<IGridEntry> FilteredInGridEntries;
|
||||
public string FilterString { get; private set; }
|
||||
public DataGridCollectionView GridEntries { get; private set; }
|
||||
|
||||
@ -117,7 +117,7 @@ namespace LibationAvalonia.ViewModels
|
||||
}
|
||||
|
||||
//Create the filtered-in list before adding entries to avoid a refresh
|
||||
FilteredInGridEntries = QueryResults(geList.Union(geList.OfType<ISeriesEntry>().SelectMany(s => s.Children)), FilterString);
|
||||
FilteredInGridEntries = geList.Union(geList.OfType<ISeriesEntry>().SelectMany(s => s.Children)).FilterEntries(FilterString);
|
||||
SOURCE.AddRange(geList.OrderByDescending(e => e.DateAdded));
|
||||
|
||||
//Add all children beneath their parent
|
||||
@ -301,7 +301,7 @@ namespace LibationAvalonia.ViewModels
|
||||
if (SOURCE.Count == 0)
|
||||
return;
|
||||
|
||||
FilteredInGridEntries = QueryResults(SOURCE, searchString);
|
||||
FilteredInGridEntries = SOURCE.FilterEntries(searchString);
|
||||
|
||||
await refreshGrid();
|
||||
}
|
||||
@ -318,23 +318,9 @@ namespace LibationAvalonia.ViewModels
|
||||
return FilteredInGridEntries.Contains(item);
|
||||
}
|
||||
|
||||
private static List<IGridEntry> QueryResults(IEnumerable<IGridEntry> entries, string searchString)
|
||||
{
|
||||
if (string.IsNullOrEmpty(searchString)) return null;
|
||||
|
||||
var searchResultSet = SearchEngineCommands.Search(searchString);
|
||||
|
||||
var booksFilteredIn = entries.BookEntries().Join(searchResultSet.Docs, lbe => lbe.AudibleProductId, d => d.ProductId, (lbe, d) => (IGridEntry)lbe);
|
||||
|
||||
//Find all series containing children that match the search criteria
|
||||
var seriesFilteredIn = entries.SeriesEntries().Where(s => s.Children.Join(searchResultSet.Docs, lbe => lbe.AudibleProductId, d => d.ProductId, (lbe, d) => lbe).Any());
|
||||
|
||||
return booksFilteredIn.Concat(seriesFilteredIn).ToList();
|
||||
}
|
||||
|
||||
private async void SearchEngineCommands_SearchEngineUpdated(object sender, EventArgs e)
|
||||
{
|
||||
var filterResults = QueryResults(SOURCE, FilterString);
|
||||
var filterResults = SOURCE.FilterEntries(FilterString);
|
||||
|
||||
if (filterResults is not null && FilteredInGridEntries.Intersect(filterResults).Count() != FilteredInGridEntries.Count)
|
||||
{
|
||||
|
||||
@ -1,25 +1,17 @@
|
||||
using Avalonia.Controls;
|
||||
using LibationUiBase.GridView;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
|
||||
namespace LibationAvalonia.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// 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. Stable sorting is achieved by comparing the GridEntry.ListIndex
|
||||
/// properties when 2 items compare equal.
|
||||
/// </summary>
|
||||
internal class RowComparer : IComparer, IComparer<IGridEntry>, IComparer<object>
|
||||
internal class RowComparer : RowComparerBase
|
||||
{
|
||||
private static readonly PropertyInfo HeaderCellPi = typeof(DataGridColumn).GetProperty("HeaderCell", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
private static readonly PropertyInfo CurrentSortingStatePi = typeof(DataGridColumnHeader).GetProperty("CurrentSortingState", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
public DataGridColumn Column { get; init; }
|
||||
public string PropertyName { get; private set; }
|
||||
private DataGridColumn Column { get; init; }
|
||||
public override string PropertyName { get; set; }
|
||||
|
||||
public RowComparer(DataGridColumn column)
|
||||
{
|
||||
@ -27,72 +19,8 @@ namespace LibationAvalonia.ViewModels
|
||||
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 = (IGridEntry)x;
|
||||
var geB = (IGridEntry)y;
|
||||
|
||||
var sortDirection = GetSortOrder();
|
||||
|
||||
ISeriesEntry parentA = null;
|
||||
ISeriesEntry parentB = null;
|
||||
|
||||
if (geA is ILibraryBookEntry lbA && lbA.Parent is ISeriesEntry seA)
|
||||
parentA = seA;
|
||||
if (geB is ILibraryBookEntry lbB && lbB.Parent is ISeriesEntry seB)
|
||||
parentB = seB;
|
||||
|
||||
//both a and b are top-level grid entries
|
||||
if (parentA is null && parentB is null)
|
||||
return InternalCompare(geA, geB);
|
||||
|
||||
//a is top-level, 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 InternalCompare(geA, parentB);
|
||||
}
|
||||
|
||||
//a is a child, b is a top-level
|
||||
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 InternalCompare(parentA, geB);
|
||||
}
|
||||
|
||||
//both are children of the same series
|
||||
if (parentA == parentB)
|
||||
return InternalCompare(geA, geB);
|
||||
|
||||
//a and b are children of different series.
|
||||
return InternalCompare(parentA, parentB);
|
||||
}
|
||||
|
||||
//Avalonia doesn't expose the column's CurrentSortingState, so we must get it through reflection
|
||||
private ListSortDirection? GetSortOrder()
|
||||
protected override ListSortDirection? GetSortOrder()
|
||||
=> CurrentSortingStatePi.GetValue(HeaderCellPi.GetValue(Column)) as ListSortDirection?;
|
||||
|
||||
private int InternalCompare(IGridEntry x, IGridEntry y)
|
||||
{
|
||||
var val1 = x.GetMemberValue(PropertyName);
|
||||
var val2 = y.GetMemberValue(PropertyName);
|
||||
|
||||
return x.GetMemberComparer(val1.GetType()).Compare(val1, val2); ;
|
||||
}
|
||||
|
||||
public int Compare(IGridEntry x, IGridEntry y)
|
||||
{
|
||||
return Compare((object)x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using DataLayer;
|
||||
using ApplicationServices;
|
||||
using DataLayer;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -39,6 +40,20 @@ namespace LibationUiBase.GridView
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static HashSet<IGridEntry>? FilterEntries(this IEnumerable<IGridEntry> entries, string searchString)
|
||||
{
|
||||
if (string.IsNullOrEmpty(searchString)) return null;
|
||||
|
||||
var searchResultSet = SearchEngineCommands.Search(searchString);
|
||||
|
||||
var booksFilteredIn = entries.BookEntries().Join(searchResultSet.Docs, lbe => lbe.AudibleProductId, d => d.ProductId, (lbe, d) => (IGridEntry)lbe);
|
||||
|
||||
//Find all series containing children that match the search criteria
|
||||
var seriesFilteredIn = entries.SeriesEntries().Where(s => s.Children.Join(searchResultSet.Docs, lbe => lbe.AudibleProductId, d => d.ProductId, (lbe, d) => lbe).Any());
|
||||
|
||||
return booksFilteredIn.Concat(seriesFilteredIn).ToHashSet();
|
||||
}
|
||||
}
|
||||
#nullable disable
|
||||
}
|
||||
|
||||
82
Source/LibationUiBase/GridView/RowComparerBase.cs
Normal file
82
Source/LibationUiBase/GridView/RowComparerBase.cs
Normal file
@ -0,0 +1,82 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace LibationUiBase.GridView
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public abstract class RowComparerBase : IComparer, IComparer<IGridEntry>, IComparer<object>
|
||||
{
|
||||
public abstract string PropertyName { get; set; }
|
||||
|
||||
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 = (IGridEntry)x;
|
||||
var geB = (IGridEntry)y;
|
||||
|
||||
var sortDirection = GetSortOrder();
|
||||
|
||||
ISeriesEntry parentA = null;
|
||||
ISeriesEntry parentB = null;
|
||||
|
||||
if (geA is ILibraryBookEntry lbA && lbA.Parent is ISeriesEntry seA)
|
||||
parentA = seA;
|
||||
if (geB is ILibraryBookEntry lbB && lbB.Parent is ISeriesEntry seB)
|
||||
parentB = seB;
|
||||
|
||||
//both a and b are top-level grid entries
|
||||
if (parentA is null && parentB is null)
|
||||
return InternalCompare(geA, geB);
|
||||
|
||||
//a is top-level, 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 InternalCompare(geA, parentB);
|
||||
}
|
||||
|
||||
//a is a child, b is a top-level
|
||||
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 InternalCompare(parentA, geB);
|
||||
}
|
||||
|
||||
//both are children of the same series
|
||||
if (parentA == parentB)
|
||||
return InternalCompare(geA, geB);
|
||||
|
||||
//a and b are children of different series.
|
||||
return InternalCompare(parentA, parentB);
|
||||
}
|
||||
|
||||
protected abstract ListSortDirection? GetSortOrder();
|
||||
|
||||
private int InternalCompare(IGridEntry x, IGridEntry y)
|
||||
{
|
||||
var val1 = x.GetMemberValue(PropertyName);
|
||||
var val2 = y.GetMemberValue(PropertyName);
|
||||
|
||||
return x.GetMemberComparer(val1.GetType()).Compare(val1, val2); ;
|
||||
}
|
||||
|
||||
public int Compare(IGridEntry x, IGridEntry y)
|
||||
{
|
||||
return Compare((object)x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user