Incremental prgress.
This commit is contained in:
parent
6182b2bcee
commit
eb49dcfc54
@ -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
|
||||
{
|
||||
/// <summary> 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! </summary>
|
||||
/// <summary> 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. </summary>
|
||||
public partial class DataGridCheckBoxColumnExt : DataGridCheckBoxColumn
|
||||
{
|
||||
Func<DataGrid> _owningGrid_get;
|
||||
Func<DataGridEditAction, bool, bool, bool, bool> _endCellEdit;
|
||||
Func<Action, bool> _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<Func<DataGrid>>(this);
|
||||
}
|
||||
return _owningGrid_get();
|
||||
}
|
||||
}
|
||||
|
||||
public Func<Action, bool> WaitForLostFocus
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_endCellEdit == null)
|
||||
{
|
||||
var mi = typeof(DataGrid).GetMethod(nameof(WaitForLostFocus), BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
_waitForLostFocus = mi.CreateDelegate<Func<Action, bool>>(OwningGrid);
|
||||
}
|
||||
return _waitForLostFocus;
|
||||
}
|
||||
}
|
||||
|
||||
public Func<DataGridEditAction, bool, bool, bool, bool> EndCellEdit
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_endCellEdit == null)
|
||||
{
|
||||
var mi = typeof(DataGrid).GetMethod(nameof(EndCellEdit), BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
_endCellEdit = mi.CreateDelegate<Func<DataGridEditAction, bool, bool, bool, bool>>(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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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; }
|
||||
|
||||
@ -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;
|
||||
|
||||
99
Source/LibationWinForms/AvaloniaUI/ViewModels/RowComparer.cs
Normal file
99
Source/LibationWinForms/AvaloniaUI/ViewModels/RowComparer.cs
Normal file
@ -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.
|
||||
/// <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>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -11,7 +11,7 @@
|
||||
<DataGrid Name="productsGrid" AutoGenerateColumns="False" Items="{Binding GridEntries}">
|
||||
<DataGrid.Columns>
|
||||
|
||||
<controls:DataGridCheckBoxColumnExt IsVisible="False" Header="Remove" IsThreeState="True" IsReadOnly="False" CanUserSort="True" Binding="{Binding Remove, Mode=TwoWay}" Width="70" SortMemberPath="Remove"/>
|
||||
<controls:DataGridCheckBoxColumnExt IsVisible="True" Header="Remove" IsThreeState="True" IsReadOnly="False" CanUserSort="True" Binding="{Binding Remove, Mode=TwoWay}" Width="70" SortMemberPath="Remove"/>
|
||||
|
||||
<DataGridTemplateColumn CanUserSort="True" Width="75" Header="Liberate" SortMemberPath="Liberate">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
|
||||
@ -126,6 +126,26 @@ namespace LibationWinForms.AvaloniaUI.Views
|
||||
|
||||
#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
|
||||
|
||||
private void RegisterCustomColumnComparers()
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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
|
||||
|
||||
@ -573,28 +514,6 @@ namespace LibationWinForms.AvaloniaUI.Views
|
||||
|
||||
#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
|
||||
|
||||
#region Column Customizations
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user