Address comments

This commit is contained in:
Michael Bucari-Tovo 2022-05-24 11:15:41 -06:00
parent 43d6ea82cd
commit e26deb9092
6 changed files with 77 additions and 54 deletions

View File

@ -27,6 +27,7 @@ namespace LibationWinForms.GridView
} }
public new bool InvokeRequired => base.InvokeRequired; public new bool InvokeRequired => base.InvokeRequired;
public abstract DateTime DateAdded { get; } public abstract DateTime DateAdded { get; }
public abstract float SeriesIndex { get; }
public abstract string ProductRating { get; protected set; } public abstract string ProductRating { get; protected set; }
public abstract string PurchaseDate { get; protected set; } public abstract string PurchaseDate { get; protected set; }
public abstract string MyRating { get; protected set; } public abstract string MyRating { get; protected set; }
@ -40,10 +41,21 @@ namespace LibationWinForms.GridView
public abstract string Description { get; protected set; } public abstract string Description { get; protected set; }
public abstract string DisplayTags { get; } public abstract string DisplayTags { get; }
public abstract LiberateButtonStatus Liberate { get; } public abstract LiberateButtonStatus Liberate { get; }
public abstract object GetMemberValue(string memberName);
#endregion #endregion
#region Sorting
public GridEntry() => _memberValues = CreateMemberValueDictionary();
private Dictionary<string, Func<object>> _memberValues { get; set; }
protected abstract Dictionary<string, Func<object>> CreateMemberValueDictionary();
// These methods are implementation of Dinah.Core.DataBinding.IMemberComparable
// Used by GridEntryBindingList for all sorting
public virtual object GetMemberValue(string memberName) => _memberValues[memberName]();
public IComparer GetMemberComparer(Type memberType) => _memberTypeComparers[memberType]; public IComparer GetMemberComparer(Type memberType) => _memberTypeComparers[memberType];
#endregion
protected void LoadCover() protected void LoadCover()
{ {
// Get cover art. If it's default, subscribe to PictureCached // Get cover art. If it's default, subscribe to PictureCached

View File

@ -1,5 +1,6 @@
using ApplicationServices; using ApplicationServices;
using Dinah.Core.DataBinding; using Dinah.Core.DataBinding;
using LibationSearchEngine;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
@ -21,15 +22,13 @@ namespace LibationWinForms.GridView
*/ */
internal class GridEntryBindingList : BindingList<GridEntry>, IBindingListView internal class GridEntryBindingList : BindingList<GridEntry>, IBindingListView
{ {
private bool isSorted;
private ListSortDirection listSortDirection;
private PropertyDescriptor propertyDescriptor;
public GridEntryBindingList() : base(new List<GridEntry>()) { } public GridEntryBindingList() : base(new List<GridEntry>()) { }
public GridEntryBindingList(IEnumerable<GridEntry> enumeration) : base(new List<GridEntry>(enumeration)) { } public GridEntryBindingList(IEnumerable<GridEntry> enumeration) : base(new List<GridEntry>(enumeration)) { }
/// <returns>All items in the list, including those filtered out.</returns> /// <returns>All items in the list, including those filtered out.</returns>
public List<GridEntry> AllItems() => Items.Concat(FilterRemoved).ToList(); public List<GridEntry> AllItems() => Items.Concat(FilterRemoved).ToList();
public bool SupportsFiltering => true;
public string Filter { get => FilterString; set => ApplyFilter(value); }
protected MemberComparer<GridEntry> Comparer { get; } = new(); protected MemberComparer<GridEntry> Comparer { get; } = new();
protected override bool SupportsSortingCore => true; protected override bool SupportsSortingCore => true;
@ -37,15 +36,14 @@ namespace LibationWinForms.GridView
protected override bool IsSortedCore => isSorted; protected override bool IsSortedCore => isSorted;
protected override PropertyDescriptor SortPropertyCore => propertyDescriptor; protected override PropertyDescriptor SortPropertyCore => propertyDescriptor;
protected override ListSortDirection SortDirectionCore => listSortDirection; protected override ListSortDirection SortDirectionCore => listSortDirection;
public bool SupportsFiltering => true;
public string Filter { get => FilterString; set => ApplyFilter(value); }
/// <summary> /// <summary> Items that were removed from the base list due to filtering </summary>
/// Items that were removed from the base list due to filtering
/// </summary>
private readonly List<GridEntry> FilterRemoved = new(); private readonly List<GridEntry> FilterRemoved = new();
private string FilterString; private string FilterString;
private LibationSearchEngine.SearchResultSet SearchResults; private SearchResultSet SearchResults;
private bool isSorted;
private ListSortDirection listSortDirection;
private PropertyDescriptor propertyDescriptor;
#region Unused - Advanced Filtering #region Unused - Advanced Filtering
@ -118,11 +116,9 @@ namespace LibationWinForms.GridView
if (SearchResults is null || SearchResults.Docs.Any(d => d.ProductId == episode.AudibleProductId)) if (SearchResults is null || SearchResults.Docs.Any(d => d.ProductId == episode.AudibleProductId))
{ {
FilterRemoved.Remove(episode); FilterRemoved.Remove(episode);
Items.Insert(++sindex, episode); InsertItem(++sindex, episode);
} }
} }
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
sEntry.Liberate.Expanded = true; sEntry.Liberate.Expanded = true;
} }
@ -137,7 +133,7 @@ namespace LibationWinForms.GridView
if (item is SeriesEntry || (item is LibraryBookEntry lbe && (lbe.Parent is null || lbe.Parent.Liberate.Expanded))) if (item is SeriesEntry || (item is LibraryBookEntry lbe && (lbe.Parent is null || lbe.Parent.Liberate.Expanded)))
{ {
FilterRemoved.Remove(item); FilterRemoved.Remove(item);
base.InsertItem(visibleCount++, item); InsertItem(visibleCount++, item);
} }
} }
@ -175,36 +171,52 @@ namespace LibationWinForms.GridView
{ {
var itemsList = (List<GridEntry>)Items; var itemsList = (List<GridEntry>)Items;
var sortedItems = Items.OrderBy(ge => ge, Comparer).ToList(); var children = itemsList.LibraryBooks().Where(i => i.Parent is not null).ToList();
var children = sortedItems.LibraryBooks().Where(i => i.Parent is not null).ToList(); var sortedItems = itemsList.Except(children).OrderBy(ge => ge, Comparer).ToList();
itemsList.Clear(); itemsList.Clear();
//Only add parentless items at this stage. After these items are added in the //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. //correct sorting order, go back and add the children beneath their parents.
itemsList.AddRange(sortedItems.Except(children)); itemsList.AddRange(sortedItems);
foreach (var parent in children.Select(c => c.Parent).Distinct()) foreach (var parent in children.Select(c => c.Parent).Distinct())
{ {
var pIndex = itemsList.IndexOf(parent); var pIndex = itemsList.IndexOf(parent);
foreach (var c in children.Where(c => c.Parent == 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); itemsList.Insert(++pIndex, c);
} }
} }
protected override void OnListChanged(ListChangedEventArgs e) protected override void OnListChanged(ListChangedEventArgs e)
{ {
if (isSorted && e.ListChangedType == ListChangedType.ItemChanged && e.PropertyDescriptor == SortPropertyCore) if (e.ListChangedType == ListChangedType.ItemChanged)
{ {
var item = Items[e.NewIndex]; if (Items[e.NewIndex] is LibraryBookEntry lbItem)
Sort(); {
var newIndex = Items.IndexOf(item); SearchResults = SearchEngineCommands.Search(FilterString);
if (!SearchResults.Docs.Any(d => d.ProductId == lbItem.AudibleProductId))
{
FilterRemoved.Add(lbItem);
base.Remove(lbItem);
return;
}
}
base.OnListChanged(new ListChangedEventArgs(ListChangedType.ItemMoved, newIndex, e.NewIndex)); if (isSorted && e.PropertyDescriptor == SortPropertyCore)
{
var item = Items[e.NewIndex];
Sort();
var newIndex = Items.IndexOf(item);
base.OnListChanged(new ListChangedEventArgs(ListChangedType.ItemMoved, newIndex, e.NewIndex));
return;
}
} }
else base.OnListChanged(e);
base.OnListChanged(e);
} }
protected override void RemoveSortCore() protected override void RemoveSortCore()

View File

@ -7,8 +7,8 @@ namespace LibationWinForms.GridView
{ {
public LiberatedStatus BookStatus { get; set; } public LiberatedStatus BookStatus { get; set; }
public LiberatedStatus? PdfStatus { get; set; } public LiberatedStatus? PdfStatus { get; set; }
public bool IsSeries { get; init; }
public bool Expanded { get; set; } public bool Expanded { get; set; }
public bool IsSeries { get; init; }
/// <summary> /// <summary>
/// Defines the Liberate column's sorting behavior /// Defines the Liberate column's sorting behavior

View File

@ -1,4 +1,5 @@
using DataLayer; using DataLayer;
using Dinah.Core.Windows.Forms;
using System; using System;
using System.Drawing; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
@ -15,12 +16,18 @@ namespace LibationWinForms.GridView
internal class LiberateDataGridViewImageButtonCell : DataGridViewImageButtonCell internal class LiberateDataGridViewImageButtonCell : DataGridViewImageButtonCell
{ {
private static readonly Color SERIES_BG_COLOR = Color.LightSkyBlue;
protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts) protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
{ {
base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, null, null, null, cellStyle, advancedBorderStyle, paintParts); base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, null, null, null, cellStyle, advancedBorderStyle, paintParts);
if (value is LiberateButtonStatus status) if (value is LiberateButtonStatus status)
{ {
if (rowIndex >= 0 && DataGridView.GetBoundItem<GridEntry>(rowIndex) is LibraryBookEntry lbEntry && lbEntry.Parent is not null)
{
DataGridView.Rows[rowIndex].DefaultCellStyle.BackColor = SERIES_BG_COLOR;
}
if (status.IsSeries) if (status.IsSeries)
{ {
var imageName = status.Expanded ? "minus" : "plus"; var imageName = status.Expanded ? "minus" : "plus";

View File

@ -32,6 +32,7 @@ namespace LibationWinForms.GridView
private LiberatedStatus? _pdfStatus; private LiberatedStatus? _pdfStatus;
public override DateTime DateAdded => LibraryBook.DateAdded; public override DateTime DateAdded => LibraryBook.DateAdded;
public override float SeriesIndex => Book.SeriesLink.FirstOrDefault()?.Index ?? 0;
public override string ProductRating { get; protected set; } public override string ProductRating { get; protected set; }
public override string PurchaseDate { get; protected set; } public override string PurchaseDate { get; protected set; }
public override string MyRating { get; protected set; } public override string MyRating { get; protected set; }
@ -77,8 +78,7 @@ namespace LibationWinForms.GridView
private void setLibraryBook(LibraryBook libraryBook) private void setLibraryBook(LibraryBook libraryBook)
{ {
LibraryBook = libraryBook; LibraryBook = libraryBook;
_memberValues = CreateMemberValueDictionary();
LoadCover(); LoadCover();
@ -119,16 +119,19 @@ namespace LibationWinForms.GridView
{ {
case nameof(udi.Tags): case nameof(udi.Tags):
Book.UserDefinedItem.Tags = udi.Tags; Book.UserDefinedItem.Tags = udi.Tags;
SearchEngineCommands.UpdateBookTags(Book);
NotifyPropertyChanged(nameof(DisplayTags)); NotifyPropertyChanged(nameof(DisplayTags));
break; break;
case nameof(udi.BookStatus): case nameof(udi.BookStatus):
Book.UserDefinedItem.BookStatus = udi.BookStatus; Book.UserDefinedItem.BookStatus = udi.BookStatus;
_bookStatus = udi.BookStatus; _bookStatus = udi.BookStatus;
SearchEngineCommands.UpdateLiberatedStatus(Book);
NotifyPropertyChanged(nameof(Liberate)); NotifyPropertyChanged(nameof(Liberate));
break; break;
case nameof(udi.PdfStatus): case nameof(udi.PdfStatus):
Book.UserDefinedItem.PdfStatus = udi.PdfStatus; Book.UserDefinedItem.PdfStatus = udi.PdfStatus;
_pdfStatus = udi.PdfStatus; _pdfStatus = udi.PdfStatus;
SearchEngineCommands.UpdateLiberatedStatus(Book);
NotifyPropertyChanged(nameof(Liberate)); NotifyPropertyChanged(nameof(Liberate));
break; break;
} }
@ -157,16 +160,9 @@ namespace LibationWinForms.GridView
#endregion #endregion
#region Data Sorting #region Data Sorting
// These methods are implementation of Dinah.Core.DataBinding.IMemberComparable
// Used by Dinah.Core.DataBinding.SortableBindingList<T> for all sorting
public override object GetMemberValue(string memberName) => _memberValues[memberName]();
private Dictionary<string, Func<object>> _memberValues { get; set; } /// <summary>Create getters for all member object values by name </summary>
protected override Dictionary<string, Func<object>> CreateMemberValueDictionary() => new()
/// <summary>
/// Create getters for all member object values by name
/// </summary>
private Dictionary<string, Func<object>> CreateMemberValueDictionary() => new()
{ {
{ nameof(Title), () => Book.TitleSortable() }, { nameof(Title), () => Book.TitleSortable() },
{ nameof(Series), () => Book.SeriesSortable() }, { nameof(Series), () => Book.SeriesSortable() },

View File

@ -10,6 +10,7 @@ namespace LibationWinForms.GridView
{ {
public List<LibraryBookEntry> Children { get; init; } public List<LibraryBookEntry> Children { get; init; }
public override DateTime DateAdded => Children.Max(c => c.DateAdded); public override DateTime DateAdded => Children.Max(c => c.DateAdded);
public override float SeriesIndex { get; }
public override string ProductRating public override string ProductRating
{ {
get get
@ -47,20 +48,23 @@ namespace LibationWinForms.GridView
public override string Description { get; protected set; } = string.Empty; public override string Description { get; protected set; } = string.Empty;
public override string DisplayTags { get; } = string.Empty; public override string DisplayTags { get; } = string.Empty;
public override LiberateButtonStatus Liberate => _liberate; public override LiberateButtonStatus Liberate { get; }
protected override Book Book => SeriesBook.Book; protected override Book Book => SeriesBook.Book;
private SeriesBook SeriesBook { get; set; } private SeriesBook SeriesBook { get; set; }
private LiberateButtonStatus _liberate = new LiberateButtonStatus { IsSeries = true }; private SeriesEntry(SeriesBook seriesBook)
public SeriesEntry(SeriesBook seriesBook, IEnumerable<LibraryBook> children)
{ {
Children = children.Select(c => new LibraryBookEntry(c) { Parent = this }).ToList(); Liberate = new LiberateButtonStatus { IsSeries = true };
SeriesIndex = seriesBook.Index;
}
public SeriesEntry(SeriesBook seriesBook, IEnumerable<LibraryBook> children) : this(seriesBook)
{
Children = children.Select(c => new LibraryBookEntry(c) { Parent = this }).OrderBy(c => c.SeriesIndex).ToList();
SetSeriesBook(seriesBook); SetSeriesBook(seriesBook);
} }
public SeriesEntry(SeriesBook seriesBook, LibraryBook child) public SeriesEntry(SeriesBook seriesBook, LibraryBook child) : this(seriesBook)
{ {
Children = new() { new LibraryBookEntry(child) { Parent = this } }; Children = new() { new LibraryBookEntry(child) { Parent = this } };
SetSeriesBook(seriesBook); SetSeriesBook(seriesBook);
@ -69,7 +73,6 @@ namespace LibationWinForms.GridView
private void SetSeriesBook(SeriesBook seriesBook) private void SetSeriesBook(SeriesBook seriesBook)
{ {
SeriesBook = seriesBook; SeriesBook = seriesBook;
_memberValues = CreateMemberValueDictionary();
LoadCover(); LoadCover();
// Immutable properties // Immutable properties
@ -83,16 +86,9 @@ namespace LibationWinForms.GridView
} }
} }
// These methods are implementation of Dinah.Core.DataBinding.IMemberComparable
// Used by Dinah.Core.DataBinding.SortableBindingList<T> for all sorting
public override object GetMemberValue(string memberName) => _memberValues[memberName]();
private Dictionary<string, Func<object>> _memberValues { get; set; } /// <summary>Create getters for all member object values by name</summary>
protected override Dictionary<string, Func<object>> CreateMemberValueDictionary() => new()
/// <summary>
/// Create getters for all member object values by name
/// </summary>
private Dictionary<string, Func<object>> CreateMemberValueDictionary() => new()
{ {
{ nameof(Title), () => Book.SeriesSortable() }, { nameof(Title), () => Book.SeriesSortable() },
{ nameof(Series), () => Book.SeriesSortable() }, { nameof(Series), () => Book.SeriesSortable() },