From dfedb23efd5a2602f070c547e218384ebaae6a9d Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Fri, 9 Dec 2022 12:27:52 -0700 Subject: [PATCH] Refactor ProductsDisplay --- .../Dialogs/MessageBoxWindow.axaml | 8 +- .../LibationAvalonia/LibationAvalonia.csproj | 3 +- .../ViewModels/GridEntryCollection.cs | 177 ------------ .../ViewModels/ProductsDisplayViewModel.cs | 258 +++++++----------- .../ViewModels/RowComparer.cs | 28 +- .../ViewModels/SeriesEntry.cs | 2 +- .../Views/MainWindow/MainWindow.axaml.cs | 7 +- .../Views/ProductsDisplay.axaml | 27 +- .../Views/ProductsDisplay.axaml.cs | 27 +- 9 files changed, 151 insertions(+), 386 deletions(-) delete mode 100644 Source/LibationAvalonia/ViewModels/GridEntryCollection.cs diff --git a/Source/LibationAvalonia/Dialogs/MessageBoxWindow.axaml b/Source/LibationAvalonia/Dialogs/MessageBoxWindow.axaml index 293a6218..a911a335 100644 --- a/Source/LibationAvalonia/Dialogs/MessageBoxWindow.axaml +++ b/Source/LibationAvalonia/Dialogs/MessageBoxWindow.axaml @@ -6,7 +6,7 @@ mc:Ignorable="d" d:DesignWidth="265" d:DesignHeight="110" MinWidth="265" MinHeight="110" x:Class="LibationAvalonia.Dialogs.MessageBoxWindow" - Title="{Binding Caption}" IsExtendedIntoWindowDecorations="True" ShowInTaskbar="True" + Title="{Binding Caption}" ShowInTaskbar="True" Icon="/Assets/1x1.png"> @@ -34,13 +34,13 @@ - - - diff --git a/Source/LibationAvalonia/LibationAvalonia.csproj b/Source/LibationAvalonia/LibationAvalonia.csproj index 9e38445c..d47d5916 100644 --- a/Source/LibationAvalonia/LibationAvalonia.csproj +++ b/Source/LibationAvalonia/LibationAvalonia.csproj @@ -136,7 +136,8 @@ - + + diff --git a/Source/LibationAvalonia/ViewModels/GridEntryCollection.cs b/Source/LibationAvalonia/ViewModels/GridEntryCollection.cs deleted file mode 100644 index 38427e3e..00000000 --- a/Source/LibationAvalonia/ViewModels/GridEntryCollection.cs +++ /dev/null @@ -1,177 +0,0 @@ -using ApplicationServices; -using LibationSearchEngine; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Collections.Specialized; -using System.Linq; - -namespace LibationAvalonia.ViewModels -{ - /* - * Allows filtering of the underlying ObservableCollection - * - * When filtering is applied, the filtered-out items are removed - * from the base list and added to the private FilterRemoved list. - * When filtering is removed, items in the FilterRemoved list are - * added back to the base list. - * - * Items are added and removed to/from the ObservableCollection's - * internal list instead of the ObservableCollection itself to - * avoid ObservableCollection firing CollectionChanged for every - * item. Editing the list this way improve's display performance, - * but requires ResetCollection() to be called after all changes - * have been made. - */ - public class GridEntryCollection : ObservableCollection - { - public GridEntryCollection(IEnumerable enumeration) - : base(new List(enumeration)) { } - public GridEntryCollection(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); } - - /// Items that were removed from the base list due to filtering - private readonly List FilterRemoved = new(); - private string FilterString; - private SearchResultSet SearchResults; - - #region Items Management - - /// - /// Removes all items from the collection, both visible and hidden, adds new items to the visible collection. - /// - public void ReplaceList(IEnumerable newItems) - { - Items.Clear(); - FilterRemoved.Clear(); - ((List)Items).AddRange(newItems); - ResetCollection(); - } - public void ResetCollection() - => OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - - #endregion - - #region Filtering - - - private void ApplyFilter(string filterString) - { - if (filterString != FilterString) - RemoveFilter(); - - FilterString = filterString; - SearchResults = SearchEngineCommands.Search(filterString); - - var booksFilteredIn = Items.BookEntries().Join(SearchResults.Docs, lbe => lbe.AudibleProductId, d => d.ProductId, (lbe, d) => (GridEntry)lbe); - - //Find all series containing children that match the search criteria - var seriesFilteredIn = Items.SeriesEntries().Where(s => s.Children.Join(SearchResults.Docs, lbe => lbe.AudibleProductId, d => d.ProductId, (lbe, d) => lbe).Any()); - - var filteredOut = Items.Except(booksFilteredIn.Concat(seriesFilteredIn)).ToList(); - - foreach (var item in filteredOut) - { - FilterRemoved.Add(item); - Items.Remove(item); - } - ResetCollection(); - } - - public void RemoveFilter() - { - if (FilterString is null) return; - - int visibleCount = Items.Count; - - foreach (var item in FilterRemoved.ToList()) - { - if (item is SeriesEntry || item is LibraryBookEntry lbe && (lbe.Parent is null || lbe.Parent.Liberate.Expanded)) - { - - FilterRemoved.Remove(item); - Items.Insert(visibleCount++, item); - } - } - - FilterString = null; - SearchResults = null; - ResetCollection(); - } - - #endregion - - #region Expand/Collapse - - public void CollapseAll() - { - foreach (var series in Items.SeriesEntries().ToList()) - CollapseItem(series); - } - - public void ExpandAll() - { - foreach (var series in Items.SeriesEntries().ToList()) - ExpandItem(series); - } - - public void CollapseItem(SeriesEntry sEntry) - { - foreach (var episode in Items.BookEntries().Where(b => b.Parent == sEntry).OrderByDescending(lbe => lbe.SeriesIndex).ToList()) - { - /* - * Bypass ObservationCollection's InsertItem method so that CollectionChanged isn't - * fired. When adding or removing many items at once, Avalonia's CollectionChanged - * event handler causes serious performance problems. And unfotrunately, Avalonia - * doesn't respect the NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, IList? changedItems) - * overload that would fire only once for all changed items. - * - * Doing this requires resetting the list so the view knows it needs to rebuild its display. - */ - - FilterRemoved.Add(episode); - Items.Remove(episode); - } - - sEntry.Liberate.Expanded = false; - ResetCollection(); - } - - public void ExpandItem(SeriesEntry sEntry) - { - var sindex = Items.IndexOf(sEntry); - - foreach (var episode in FilterRemoved.BookEntries().Where(b => b.Parent == sEntry).OrderByDescending(lbe => lbe.SeriesIndex).ToList()) - { - if (SearchResults is null || SearchResults.Docs.Any(d => d.ProductId == episode.AudibleProductId)) - { - /* - * Bypass ObservationCollection's InsertItem method so that CollectionChanged isn't - * fired. When adding or removing many items at once, Avalonia's CollectionChanged - * event handler causes serious performance problems. And unfotrunately, Avalonia - * doesn't respect the NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, IList? changedItems) - * overload that would fire only once for all changed items. - * - * Doing this requires resetting the list so the view knows it needs to rebuild its display. - */ - - FilterRemoved.Remove(episode); - Items.Insert(++sindex, episode); - } - } - - sEntry.Liberate.Expanded = true; - ResetCollection(); - } - - #endregion - } -} diff --git a/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs b/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs index af318103..f45b1f57 100644 --- a/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs +++ b/Source/LibationAvalonia/ViewModels/ProductsDisplayViewModel.cs @@ -1,4 +1,3 @@ -using Avalonia.Controls; using DataLayer; using System; using System.Collections.Generic; @@ -6,12 +5,9 @@ using System.ComponentModel; using System.Linq; using System.Threading.Tasks; using ReactiveUI; -using System.Reflection; -using System.Collections; using Avalonia.Threading; using ApplicationServices; using AudibleUtilities; -using LibationAvalonia.Views; using LibationAvalonia.Dialogs.Login; using Avalonia.Collections; @@ -24,81 +20,35 @@ namespace LibationAvalonia.ViewModels public event EventHandler RemovableCountChanged; public event EventHandler InitialLoaded; - private DataGridColumn _currentSortColumn; - private DataGrid productsDataGrid; + /// Backing list of all grid entries + private readonly List SOURCE = new(); + /// Grid entries included in the filter set. If null, all grid entries are shown + private List FilteredInGridEntries; + public string FilterString { get; private set; } + public DataGridCollectionView GridEntries { get; } - private GridEntryCollection _gridEntries; private bool _removeColumnVisivle; - public GridEntryCollection GridEntries { get => _gridEntries; private set => this.RaiseAndSetIfChanged(ref _gridEntries, value); } public bool RemoveColumnVisivle { get => _removeColumnVisivle; private set => this.RaiseAndSetIfChanged(ref _removeColumnVisivle, value); } public List GetVisibleBookEntries() - => GridEntries.InternalList + => GridEntries + .Cast() .BookEntries() .Select(lbe => lbe.LibraryBook) .ToList(); - public IEnumerable GetAllBookEntries() - => GridEntries - .AllItems() + + private IEnumerable GetAllBookEntries() + => SOURCE .BookEntries(); - public ProductsDisplayViewModel() { } - public ProductsDisplayViewModel(List items) + + public ProductsDisplayViewModel() { - GridEntries = new GridEntryCollection(items); + GridEntries = new(SOURCE); + GridEntries.Filter = CollectionFilter; } #region Display Functions - /// - /// Call once on load so we can modify access a private member with reflection - /// - public void RegisterCollectionChanged(ProductsDisplay productsDisplay = null) - { - productsDataGrid ??= productsDisplay?.productsGrid; - - if (GridEntries is null) - return; - - //Avalonia displays items in the DataConncetion from an internal copy of - //the bound list, not the actual bound list. So we need to reflect to get - //the current display order and set each GridEntry.ListIndex correctly. - var DataConnection_PI = typeof(DataGrid).GetProperty("DataConnection", BindingFlags.NonPublic | BindingFlags.Instance); - var DataSource_PI = DataConnection_PI.PropertyType.GetProperty("DataSource", BindingFlags.Public | BindingFlags.Instance); - - GridEntries.CollectionChanged += (s, e) => - { - if (s != GridEntries) return; - - var displayListGE = ((IEnumerable)DataSource_PI.GetValue(DataConnection_PI.GetValue(productsDataGrid))).Cast(); - int index = 0; - foreach (var di in displayListGE) - { - di.ListIndex = index++; - } - }; - } - - /// - /// Only call once per lifetime - /// - public void InitialDisplay(List dbBooks) - { - try - { - GridEntries = new GridEntryCollection(CreateGridEntries(dbBooks)); - GridEntries.CollapseAll(); - - InitialLoaded?.Invoke(this, EventArgs.Empty); - VisibleCountChanged?.Invoke(this, GridEntries.BookEntries().Count()); - - RegisterCollectionChanged(); - } - catch (Exception ex) - { - Serilog.Log.Error(ex, "Error displaying library in {0}", nameof(ProductsDisplayViewModel)); - } - } - /// /// Call when there's been a change to the library /// @@ -106,29 +56,25 @@ namespace LibationAvalonia.ViewModels { try { - //List is already displayed. Replace all items with new ones, refilter, and re-sort - string existingFilter = GridEntries?.Filter; - var newEntries = CreateGridEntries(dbBooks); + var existingSeriesEntries = SOURCE.SeriesEntries().ToList(); - var existingSeriesEntries = GridEntries.AllItems().SeriesEntries().ToList(); + SOURCE.Clear(); + SOURCE.AddRange(CreateGridEntries(dbBooks)); - await Dispatcher.UIThread.InvokeAsync(() => + //If replacing the list, preserve user's existing collapse/expand + //state. When resetting a list, default state is cosed. + foreach (var series in existingSeriesEntries) { - GridEntries.ReplaceList(newEntries); + var sEntry = SOURCE.FirstOrDefault(ge => ge.AudibleProductId == series.AudibleProductId); + if (sEntry is SeriesEntry se) + se.Liberate.Expanded = series.Liberate.Expanded; + } - //We're replacing the list, so preserve usere's existing collapse/expand - //state. When resetting a list, default state is open. - foreach (var series in existingSeriesEntries) - { - var sEntry = GridEntries.InternalList.FirstOrDefault(ge => ge.AudibleProductId == series.AudibleProductId); - if (sEntry is SeriesEntry se && !series.Liberate.Expanded) - GridEntries.CollapseItem(se); - } + //Run query on new list + FilteredInGridEntries = QueryResults(SOURCE, FilterString); + + await Dispatcher.UIThread.InvokeAsync(GridEntries.Refresh); - GridEntries.Filter = existingFilter; - ReSort(); - VisibleCountChanged?.Invoke(this, GridEntries.BookEntries().Count()); - }); } catch (Exception ex) { @@ -136,7 +82,7 @@ namespace LibationAvalonia.ViewModels } } - private static IEnumerable CreateGridEntries(IEnumerable dbBooks) + private static List CreateGridEntries(IEnumerable dbBooks) { var geList = dbBooks .Where(lb => lb.Book.IsProduct()) @@ -159,81 +105,74 @@ namespace LibationAvalonia.ViewModels geList.Add(seriesEntry); geList.AddRange(seriesEntry.Children); } - return geList.OrderByDescending(e => e.DateAdded); + + var bookList = geList.OrderByDescending(e => e.DateAdded).ToList(); + + //ListIndex is used by RowComparer to make column sort stable + int index = 0; + foreach (GridEntry di in bookList) + di.ListIndex = index++; + + return bookList; } public void ToggleSeriesExpanded(SeriesEntry seriesEntry) { - if (seriesEntry.Liberate.Expanded) - GridEntries.CollapseItem(seriesEntry); - else - GridEntries.ExpandItem(seriesEntry); - - VisibleCountChanged?.Invoke(this, GridEntries.BookEntries().Count()); + seriesEntry.Liberate.Expanded = !seriesEntry.Liberate.Expanded; + GridEntries.Refresh(); } #endregion #region Filtering + public async Task Filter(string searchString) { - await Dispatcher.UIThread.InvokeAsync(() => - { - int visibleCount = GridEntries.Count; + if (searchString == FilterString) + return; - if (string.IsNullOrEmpty(searchString)) - GridEntries.RemoveFilter(); - else - GridEntries.Filter = searchString; + FilteredInGridEntries = QueryResults(SOURCE, searchString); - if (visibleCount != GridEntries.Count) - VisibleCountChanged?.Invoke(this, GridEntries.BookEntries().Count()); + FilterString = searchString; - //Re-sort after filtering - ReSort(); - }); + await Dispatcher.UIThread.InvokeAsync(GridEntries.Refresh); } + private bool CollectionFilter(object item) + { + if (item is LibraryBookEntry lbe + && lbe.IsEpisode + && lbe.Parent?.Liberate?.Expanded != true) + return false; + + if (FilteredInGridEntries is null) return true; + + return FilteredInGridEntries.Contains(item); + } + + private static List QueryResults(List entries, string searchString) + { + if (string.IsNullOrEmpty(searchString)) return null; + + var SearchResults = SearchEngineCommands.Search(searchString); + + var booksFilteredIn = entries.BookEntries().Join(SearchResults.Docs, lbe => lbe.AudibleProductId, d => d.ProductId, (lbe, d) => (GridEntry)lbe); + + //Find all series containing children that match the search criteria + var seriesFilteredIn = entries.SeriesEntries().Where(s => s.Children.Join(SearchResults.Docs, lbe => lbe.AudibleProductId, d => d.ProductId, (lbe, d) => lbe).Any()); + + return booksFilteredIn.Concat(seriesFilteredIn).ToList(); + } + #endregion - #region Sorting - - public void Sort(DataGridColumn sortColumn) - { - //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. - var comparer = sortColumn.CustomSortComparer as RowComparer; - comparer.SortDirection = null; - - _currentSortColumn = sortColumn; - } - - //Must be invoked on UI thread - private void ReSort() - { - if (_currentSortColumn is null) - { - //Sort ascending and reverse. That's how the comparer is designed to work to be compatible with Avalonia. - var defaultComparer = new RowComparer(ListSortDirection.Descending, nameof(GridEntry.DateAdded)); - GridEntries.InternalList.Sort(defaultComparer); - GridEntries.InternalList.Reverse(); - GridEntries.ResetCollection(); - } - else - { - _currentSortColumn.Sort(((RowComparer)_currentSortColumn.CustomSortComparer).SortDirection ?? ListSortDirection.Ascending); - } - } - - #endregion #region Scan and Remove Books public void DoneRemovingBooks() { - foreach (var item in GridEntries.AllItems()) - item.PropertyChanged -= Item_PropertyChanged; + foreach (var item in SOURCE) + item.PropertyChanged -= GridEntry_PropertyChanged; RemoveColumnVisivle = false; } @@ -248,49 +187,47 @@ namespace LibationAvalonia.ViewModels var result = await MessageBox.ShowConfirmationDialog( null, libraryBooks, - // do not use `$` string interpolation. See impl. - "Are you sure you want to remove {0} from Libation's library?", + // do not use `$` string interpolation. See impl. + "Are you sure you want to remove {0} from Libation's library?", "Remove books from Libation?"); if (result != DialogResult.Yes) return; foreach (var book in selectedBooks) - book.PropertyChanged -= Item_PropertyChanged; + book.PropertyChanged -= GridEntry_PropertyChanged; var idsToRemove = libraryBooks.Select(lb => lb.Book.AudibleProductId).ToList(); + + void BindingList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + if (e.Action != System.Collections.Specialized.NotifyCollectionChangedAction.Reset) + return; + + //After DisplayBooks() re-creates the list, + //re-subscribe to all items' PropertyChanged events. + + foreach (var b in GetAllBookEntries()) + b.PropertyChanged += GridEntry_PropertyChanged; + + GridEntries.CollectionChanged -= BindingList_CollectionChanged; + } + GridEntries.CollectionChanged += BindingList_CollectionChanged; //The RemoveBooksAsync will fire LibrarySizeChanged, which calls ProductsDisplay2.Display(), //so there's no need to remove books from the grid display here. var removeLibraryBooks = await LibraryCommands.RemoveBooksAsync(idsToRemove); - foreach (var b in GetAllBookEntries()) - b.Remove = false; - RemovableCountChanged?.Invoke(this, 0); } - void BindingList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) - { - if (e.Action != System.Collections.Specialized.NotifyCollectionChangedAction.Reset) - return; - - //After ProductsDisplay2.Display() re-creates the list, - //re-subscribe to all items' PropertyChanged events. - - foreach (var b in GetAllBookEntries()) - b.PropertyChanged += Item_PropertyChanged; - - GridEntries.CollectionChanged -= BindingList_CollectionChanged; - } - public async Task ScanAndRemoveBooksAsync(params Account[] accounts) { - foreach (var item in GridEntries.AllItems()) + foreach (var item in SOURCE) { item.Remove = false; - item.PropertyChanged += Item_PropertyChanged; + item.PropertyChanged += GridEntry_PropertyChanged; } RemoveColumnVisivle = true; @@ -303,9 +240,6 @@ namespace LibationAvalonia.ViewModels var allBooks = GetAllBookEntries(); - foreach (var b in allBooks) - b.Remove = false; - var lib = allBooks .Select(lbe => lbe.LibraryBook) .Where(lb => !lb.Book.HasLiberated()); @@ -327,7 +261,7 @@ namespace LibationAvalonia.ViewModels } } - private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e) + private void GridEntry_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(GridEntry.Remove) && sender is LibraryBookEntry lbEntry) { diff --git a/Source/LibationAvalonia/ViewModels/RowComparer.cs b/Source/LibationAvalonia/ViewModels/RowComparer.cs index 73effeea..71a912ee 100644 --- a/Source/LibationAvalonia/ViewModels/RowComparer.cs +++ b/Source/LibationAvalonia/ViewModels/RowComparer.cs @@ -13,25 +13,19 @@ namespace LibationAvalonia.ViewModels /// sorted by series index, ascending. Stable sorting is achieved by comparing the GridEntry.ListIndex /// properties when 2 items compare equal. /// - internal class RowComparer : IComparer, IComparer + internal class RowComparer : IComparer, IComparer, IComparer { 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; } - public ListSortDirection? SortDirection { get; set; } public RowComparer(DataGridColumn column) { Column = column; PropertyName = Column.SortMemberPath; } - public RowComparer(ListSortDirection direction, string propertyName) - { - SortDirection = direction; - PropertyName = propertyName; - } public int Compare(object x, object y) { @@ -42,7 +36,7 @@ namespace LibationAvalonia.ViewModels var geA = (GridEntry)x; var geB = (GridEntry)y; - SortDirection ??= GetSortOrder(); + var sortDirection = GetSortOrder(); SeriesEntry parentA = null; SeriesEntry parentB = null; @@ -54,16 +48,16 @@ namespace LibationAvalonia.ViewModels //both a and b are top-level grid entries if (parentA is null && parentB is null) - return InternalCompare(geA, geB); + return InternalCompare(geA, geB, sortDirection); //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; + return sortDirection is ListSortDirection.Ascending ? -1 : 1; else - return InternalCompare(geA, parentB); + return InternalCompare(geA, parentB, sortDirection); } //a is a child, b is a top-level @@ -71,24 +65,24 @@ namespace LibationAvalonia.ViewModels { // a is a child of b, parent is always first if (parentA == geB) - return SortDirection is ListSortDirection.Ascending ? 1 : -1; + return sortDirection is ListSortDirection.Ascending ? 1 : -1; else - return InternalCompare(parentA, geB); + return InternalCompare(parentA, geB, sortDirection); } //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); + return geA.SeriesIndex.CompareTo(geB.SeriesIndex) * (sortDirection is ListSortDirection.Ascending ? 1 : -1); //a and b are children of different series. - return InternalCompare(parentA, parentB); + return InternalCompare(parentA, parentB, sortDirection); } //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 InternalCompare(GridEntry x, GridEntry y) + private int InternalCompare(GridEntry x, GridEntry y, ListSortDirection? sortDirection) { var val1 = x.GetMemberValue(PropertyName); var val2 = y.GetMemberValue(PropertyName); @@ -98,7 +92,7 @@ namespace LibationAvalonia.ViewModels //If items compare equal, compare them by their positions in the the list. //This is how you achieve a stable sort. if (compareResult == 0) - return x.ListIndex.CompareTo(y.ListIndex); + return x.ListIndex.CompareTo(y.ListIndex) * (sortDirection is ListSortDirection.Ascending ? 1 : -1); else return compareResult; } diff --git a/Source/LibationAvalonia/ViewModels/SeriesEntry.cs b/Source/LibationAvalonia/ViewModels/SeriesEntry.cs index 7589b3a2..bd457171 100644 --- a/Source/LibationAvalonia/ViewModels/SeriesEntry.cs +++ b/Source/LibationAvalonia/ViewModels/SeriesEntry.cs @@ -55,7 +55,7 @@ namespace LibationAvalonia.ViewModels public SeriesEntry(LibraryBook parent, IEnumerable children) { - Liberate = new LiberateButtonStatus(IsSeries) { Expanded = true }; + Liberate = new LiberateButtonStatus(IsSeries); SeriesIndex = -1; LibraryBook = parent; diff --git a/Source/LibationAvalonia/Views/MainWindow/MainWindow.axaml.cs b/Source/LibationAvalonia/Views/MainWindow/MainWindow.axaml.cs index 7fde4711..19a3c17a 100644 --- a/Source/LibationAvalonia/Views/MainWindow/MainWindow.axaml.cs +++ b/Source/LibationAvalonia/Views/MainWindow/MainWindow.axaml.cs @@ -174,13 +174,12 @@ namespace LibationAvalonia.Views public void ProductsDisplay_Initialized1(object sender, EventArgs e) { - if (sender is ProductsDisplay products) - _viewModel.ProductsDisplay.RegisterCollectionChanged(products); + } - private void MainWindow_LibraryLoaded(object sender, List dbBooks) + private async void MainWindow_LibraryLoaded(object sender, List dbBooks) { - _viewModel.ProductsDisplay.InitialDisplay(dbBooks); + await _viewModel.ProductsDisplay.DisplayBooks(dbBooks); } private void InitializeComponent() diff --git a/Source/LibationAvalonia/Views/ProductsDisplay.axaml b/Source/LibationAvalonia/Views/ProductsDisplay.axaml index fb5a9939..82e1277d 100644 --- a/Source/LibationAvalonia/Views/ProductsDisplay.axaml +++ b/Source/LibationAvalonia/Views/ProductsDisplay.axaml @@ -20,16 +20,25 @@ CanUserReorderColumns="True"> - - + IsVisible="{Binding RemoveColumnVisivle}" + PropertyChanged="RemoveColumn_PropertyChanged" + Header="Remove" + IsReadOnly="False" + SortMemberPath="Remove" + Width="75"> + + + + + + + diff --git a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs index 6f015b8c..b09fc6e4 100644 --- a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs +++ b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs @@ -27,20 +27,25 @@ namespace LibationAvalonia.Views if (Design.IsDesignMode) { using var context = DbContexts.GetContext(); - List sampleEntries = new() + List sampleEntries = new() { - new LibraryBookEntry(context.GetLibraryBook_Flat_NoTracking("B00DCD0OXU")), - new LibraryBookEntry(context.GetLibraryBook_Flat_NoTracking("B017V4IM1G")), - new LibraryBookEntry(context.GetLibraryBook_Flat_NoTracking("B017V4IWVG")), - new LibraryBookEntry(context.GetLibraryBook_Flat_NoTracking("B017V4JA2Q")), - new LibraryBookEntry(context.GetLibraryBook_Flat_NoTracking("B017V4NUPO")), - new LibraryBookEntry(context.GetLibraryBook_Flat_NoTracking("B017V4NMX4")), - new LibraryBookEntry(context.GetLibraryBook_Flat_NoTracking("B017V4NOZ0")), - new LibraryBookEntry(context.GetLibraryBook_Flat_NoTracking("B017WJ5ZK6")), + //context.GetLibraryBook_Flat_NoTracking("B00DCD0OXU"), + context.GetLibraryBook_Flat_NoTracking("B017V4IM1G"), + context.GetLibraryBook_Flat_NoTracking("B017V4IWVG"), + context.GetLibraryBook_Flat_NoTracking("B017V4JA2Q"), + context.GetLibraryBook_Flat_NoTracking("B017V4NUPO"), + context.GetLibraryBook_Flat_NoTracking("B017V4NMX4"), + context.GetLibraryBook_Flat_NoTracking("B017V4NOZ0"), + context.GetLibraryBook_Flat_NoTracking("B017WJ5ZK6") }; - DataContext = new ProductsDisplayViewModel(sampleEntries); + + var pdvm = new ProductsDisplayViewModel(); + pdvm.DisplayBooks(sampleEntries); + DataContext = pdvm; + return; } + Configure_ColumnCustomization(); foreach (var column in productsGrid.Columns) @@ -51,7 +56,7 @@ namespace LibationAvalonia.Views private void ProductsGrid_Sorting(object sender, DataGridColumnEventArgs e) { - _viewModel.Sort(e.Column); + } private void RemoveColumn_PropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)