Use ReactiveUI.

Sort of fix remove book checkbox column.
This commit is contained in:
Michael Bucari-Tovo 2022-07-12 18:55:10 -06:00
parent 5f45d28b9f
commit 6e091230cf
11 changed files with 171 additions and 159 deletions

View File

@ -1,35 +1,15 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Utils;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using LibationWinForms.AvaloniaUI.ViewModels; using LibationWinForms.AvaloniaUI.ViewModels;
using System;
using System.Linq;
namespace LibationWinForms.AvaloniaUI.Controls namespace LibationWinForms.AvaloniaUI.Controls
{ {
/// <summary> The purpose of this extension is to immediately commit any check state changes to the viewmodel </summary> /// <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>
public partial class DataGridCheckBoxColumnExt : DataGridCheckBoxColumn public partial class DataGridCheckBoxColumnExt : DataGridCheckBoxColumn
{ {
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)
{
gentry.Remove = cbox.IsChecked;
FindDataGridParent(cbox)?.CommitEdit(DataGridEditingUnit.Cell, false);
}
}
DataGrid? FindDataGridParent(IControl? control)
{
if (control?.Parent is null) return null;
else if (control?.Parent is DataGrid dg) return dg;
else return FindDataGridParent(control?.Parent);
}
} }
} }

View File

@ -2,8 +2,8 @@
{ {
public class BookTags public class BookTags
{ {
public string Tags { get; init; } private string _tags;
public bool IsSeries { get; init; } public string Tags { get => _tags; init { _tags = value; HasTags = !string.IsNullOrEmpty(_tags); } }
public bool HasTags => !string.IsNullOrEmpty(Tags); public bool HasTags { get; init; }
} }
} }

View File

@ -21,7 +21,7 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
SomeRemoved SomeRemoved
} }
/// <summary>The View Model base for the DataGridView</summary> /// <summary>The View Model base for the DataGridView</summary>
public abstract class GridEntry2 : AsyncNotifyPropertyChanged2, IMemberComparable public abstract class GridEntry2 : ViewModelBase
{ {
[Browsable(false)] public string AudibleProductId => Book.AudibleProductId; [Browsable(false)] public string AudibleProductId => Book.AudibleProductId;
[Browsable(false)] public LibraryBook LibraryBook { get; protected set; } [Browsable(false)] public LibraryBook LibraryBook { get; protected set; }
@ -32,30 +32,35 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
#region Model properties exposed to the view #region Model properties exposed to the view
private Avalonia.Media.Imaging.Bitmap _cover;
private string _purchaseDate;
private string _series;
private string _title;
private string _length;
private string _authors;
private string _narrators;
private string _category;
private string _misc;
private string _description;
private string _productRating;
private string _myRating;
public Avalonia.Media.Imaging.Bitmap Cover { get => _cover; protected set { this.RaiseAndSetIfChanged(ref _cover, value); } }
public string PurchaseDate { get => _purchaseDate; protected set { this.RaiseAndSetIfChanged(ref _purchaseDate, value); } }
public string Series { get => _series; protected set { this.RaiseAndSetIfChanged(ref _series, value); } }
public string Title { get => _title; protected set { this.RaiseAndSetIfChanged(ref _title, value); } }
public string Length { get => _length; protected set { this.RaiseAndSetIfChanged(ref _length, value); } }
public string Authors { get => _authors; protected set { this.RaiseAndSetIfChanged(ref _authors, value); } }
public string Narrators { get => _narrators; protected set { this.RaiseAndSetIfChanged(ref _narrators, value); } }
public string Category { get => _category; protected set { this.RaiseAndSetIfChanged(ref _category, value); } }
public string Misc { get => _misc; protected set { this.RaiseAndSetIfChanged(ref _misc, value); } }
public string Description { get => _description; protected set { this.RaiseAndSetIfChanged(ref _description, value); } }
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; protected bool? _remove = false;
public abstract bool? Remove { get; set; } public abstract bool? Remove { get; set; }
public abstract LiberateButtonStatus2 Liberate { get; } public abstract LiberateButtonStatus2 Liberate { get; }
public Avalonia.Media.Imaging.Bitmap Cover
{
get => _cover;
protected set
{
_cover = value;
NotifyPropertyChanged();
}
}
public string PurchaseDate { get; protected set; }
public string Series { get; protected set; }
public string Title { get; protected set; }
public string Length { get; protected set; }
public string Authors { get; set; }
public string Narrators { get; protected set; }
public string Category { get; protected set; }
public string Misc { get; protected set; }
public string Description { get; protected set; }
public string ProductRating { get; protected set; }
public string MyRating { get; protected set; }
public abstract BookTags BookTags { get; } public abstract BookTags BookTags { get; }
#endregion #endregion
@ -87,7 +92,6 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
#region Cover Art #region Cover Art
private Avalonia.Media.Imaging.Bitmap _cover;
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,13 +1,12 @@
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using DataLayer; using DataLayer;
using ReactiveUI;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace LibationWinForms.AvaloniaUI.ViewModels namespace LibationWinForms.AvaloniaUI.ViewModels
{ {
public class LiberateButtonStatus2 : IComparable, INotifyPropertyChanged public class LiberateButtonStatus2 : ViewModelBase, IComparable
{ {
public LiberatedStatus BookStatus { get; set; } public LiberatedStatus BookStatus { get; set; }
public LiberatedStatus? PdfStatus { get; set; } public LiberatedStatus? PdfStatus { get; set; }
@ -18,10 +17,9 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
get => _expanded; get => _expanded;
set set
{ {
_expanded = value; this.RaiseAndSetIfChanged(ref _expanded, value);
NotifyPropertyChanged(); this.RaisePropertyChanged(nameof(Image));
NotifyPropertyChanged(nameof(Image)); this.RaisePropertyChanged(nameof(ToolTip));
NotifyPropertyChanged(nameof(ToolTip));
} }
} }
public bool IsSeries { get; init; } public bool IsSeries { get; init; }
@ -30,13 +28,7 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
static Dictionary<string, Bitmap> images = new(); static Dictionary<string, Bitmap> images = new();
public event PropertyChangedEventHandler PropertyChanged; /// <summary> Defines the Liberate column's sorting behavior </summary>
public void NotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
/// <summary>
/// Defines the Liberate column's sorting behavior
/// </summary>
public int CompareTo(object obj) public int CompareTo(object obj)
{ {
if (obj is not LiberateButtonStatus2 second) return -1; if (obj is not LiberateButtonStatus2 second) return -1;
@ -49,14 +41,13 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
else return BookStatus.CompareTo(second.BookStatus); else return BookStatus.CompareTo(second.BookStatus);
} }
private Bitmap GetLiberateIcon() private Bitmap GetLiberateIcon()
{ {
if (IsSeries) if (IsSeries)
return Expanded ? GetFromresc("minus") : GetFromresc("plus"); return Expanded ? GetFromResources("minus") : GetFromResources("plus");
if (BookStatus == LiberatedStatus.Error) if (BookStatus == LiberatedStatus.Error)
return GetFromresc("error"); return GetFromResources("error");
string image_lib = BookStatus switch string image_lib = BookStatus switch
{ {
@ -75,7 +66,7 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
_ => throw new Exception("Unexpected PDF state") _ => throw new Exception("Unexpected PDF state")
}; };
return GetFromresc($"liberate_{image_lib}{image_pdf}"); return GetFromResources($"liberate_{image_lib}{image_pdf}");
} }
private string GetTooltip() private string GetTooltip()
{ {
@ -113,7 +104,7 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
return mouseoverText; return mouseoverText;
} }
private static Bitmap GetFromresc(string rescName) private static Bitmap GetFromResources(string rescName)
{ {
if (images.ContainsKey(rescName)) return images[rescName]; if (images.ContainsKey(rescName)) return images[rescName];

View File

@ -2,6 +2,7 @@
using DataLayer; using DataLayer;
using Dinah.Core; using Dinah.Core;
using LibationWinForms.GridView; using LibationWinForms.GridView;
using ReactiveUI;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
@ -27,8 +28,9 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
set set
{ {
_remove = value.HasValue ? value.Value : false; _remove = value.HasValue ? value.Value : false;
Parent?.ChildRemoveUpdate(); Parent?.ChildRemoveUpdate();
NotifyPropertyChanged(); this.RaisePropertyChanged(nameof(Remove));
} }
} }
@ -84,20 +86,6 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
Description = TrimTextToWord(LongDescription, 62); Description = TrimTextToWord(LongDescription, 62);
SeriesIndex = Book.SeriesLink.FirstOrDefault()?.Index ?? 0; SeriesIndex = Book.SeriesLink.FirstOrDefault()?.Index ?? 0;
NotifyPropertyChanged(nameof(Title));
NotifyPropertyChanged(nameof(Series));
NotifyPropertyChanged(nameof(Length));
NotifyPropertyChanged(nameof(MyRating));
NotifyPropertyChanged(nameof(PurchaseDate));
NotifyPropertyChanged(nameof(ProductRating));
NotifyPropertyChanged(nameof(Authors));
NotifyPropertyChanged(nameof(Narrators));
NotifyPropertyChanged(nameof(Category));
NotifyPropertyChanged(nameof(Misc));
NotifyPropertyChanged(nameof(LongDescription));
NotifyPropertyChanged(nameof(Description));
NotifyPropertyChanged(nameof(SeriesIndex));
UserDefinedItem.ItemChanged += UserDefinedItem_ItemChanged; UserDefinedItem.ItemChanged += UserDefinedItem_ItemChanged;
} }
@ -120,18 +108,18 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
switch (itemName) switch (itemName)
{ {
case nameof(udi.Tags): case nameof(udi.Tags):
Book.UserDefinedItem.Tags = udi.Tags; Book.UserDefinedItem.Tags = udi.Tags;
NotifyPropertyChanged(nameof(BookTags)); this.RaisePropertyChanged(nameof(BookTags));
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;
NotifyPropertyChanged(nameof(Liberate)); this.RaisePropertyChanged(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;
NotifyPropertyChanged(nameof(Liberate)); this.RaisePropertyChanged(nameof(Liberate));
break; break;
} }
} }

View File

@ -1,8 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using ApplicationServices; using ApplicationServices;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
@ -10,6 +8,7 @@ using DataLayer;
using Dinah.Core; using Dinah.Core;
using FileLiberator; using FileLiberator;
using LibationFileManager; using LibationFileManager;
using ReactiveUI;
namespace LibationWinForms.AvaloniaUI.ViewModels namespace LibationWinForms.AvaloniaUI.ViewModels
{ {
@ -36,10 +35,9 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
/// <summary> /// <summary>
/// This is the viewmodel for queued processables /// This is the viewmodel for queued processables
/// </summary> /// </summary>
public class ProcessBook2 : INotifyPropertyChanged public class ProcessBook2 : ViewModelBase
{ {
public event EventHandler Completed; public event EventHandler Completed;
public event PropertyChangedEventHandler PropertyChanged;
public LibraryBook LibraryBook { get; private set; } public LibraryBook LibraryBook { get; private set; }
@ -53,14 +51,14 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
private Bitmap _cover; private Bitmap _cover;
#region Properties exposed to the view #region Properties exposed to the view
public ProcessBookResult Result { get => _result; set { _result = value; NotifyPropertyChanged(); NotifyPropertyChanged(nameof(StatusText)); } } public ProcessBookResult Result { get => _result; set { this.RaiseAndSetIfChanged(ref _result, value); this.RaisePropertyChanged(nameof(StatusText)); } }
public ProcessBookStatus Status { get => _status; set { _status = value; NotifyPropertyChanged(); NotifyPropertyChanged(nameof(BackgroundColor)); NotifyPropertyChanged(nameof(IsFinished)); NotifyPropertyChanged(nameof(IsDownloading)); NotifyPropertyChanged(nameof(Queued)); } } public ProcessBookStatus Status { get => _status; set { this.RaiseAndSetIfChanged(ref _status, value); this.RaisePropertyChanged(nameof(BackgroundColor)); this.RaisePropertyChanged(nameof(IsFinished)); this.RaisePropertyChanged(nameof(IsDownloading)); this.RaisePropertyChanged(nameof(Queued)); } }
public string Narrator { get => _narrator; set { _narrator = value; NotifyPropertyChanged(); } } public string Narrator { get => _narrator; set { this.RaiseAndSetIfChanged(ref _narrator, value); } }
public string Author { get => _author; set { _author = value; NotifyPropertyChanged(); } } public string Author { get => _author; set { this.RaiseAndSetIfChanged(ref _author, value); } }
public string Title { get => _title; set { _title = value; NotifyPropertyChanged(); } } public string Title { get => _title; set { this.RaiseAndSetIfChanged(ref _title, value); } }
public int Progress { get => _progress; private set { _progress = value; NotifyPropertyChanged(); } } public int Progress { get => _progress; private set { this.RaiseAndSetIfChanged(ref _progress, value); } }
public string ETA { get => _eta; private set { _eta = value; NotifyPropertyChanged(); } } public string ETA { get => _eta; private set { this.RaiseAndSetIfChanged(ref _eta, value); } }
public Bitmap Cover { get => _cover; private set { _cover = value; NotifyPropertyChanged(); } } public Bitmap Cover { get => _cover; private set { this.RaiseAndSetIfChanged(ref _cover, value); } }
public bool IsFinished => Status is not ProcessBookStatus.Queued and not ProcessBookStatus.Working; public bool IsFinished => Status is not ProcessBookStatus.Queued and not ProcessBookStatus.Working;
public bool IsDownloading => Status is ProcessBookStatus.Working; public bool IsDownloading => Status is ProcessBookStatus.Working;
public bool Queued => Status is ProcessBookStatus.Queued; public bool Queued => Status is ProcessBookStatus.Queued;
@ -91,8 +89,6 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
private Processable _currentProcessable; private Processable _currentProcessable;
private readonly Queue<Func<Processable>> Processes = new(); private readonly Queue<Func<Processable>> Processes = new();
private readonly ProcessQueue.LogMe Logger; private readonly ProcessQueue.LogMe Logger;
public void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
public ProcessBook2(LibraryBook libraryBook, ProcessQueue.LogMe logme) public ProcessBook2(LibraryBook libraryBook, ProcessQueue.LogMe logme)
{ {

View File

@ -7,31 +7,25 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
{ {
public class ProcessQueueViewModel : ViewModelBase, ProcessQueue.ILogForm public class ProcessQueueViewModel : ViewModelBase, ProcessQueue.ILogForm
{ {
public ObservableCollection<LogEntry> LogEntries { get; } = new();
public string QueueHeader => "this is a header!";
private TrackedQueue2<ProcessBook2> _items = new(); private TrackedQueue2<ProcessBook2> _items = new();
public ProcessQueueViewModel() { }
public TrackedQueue2<ProcessBook2> Items public TrackedQueue2<ProcessBook2> Items
{ {
get => _items; get => _items;
set => this.RaiseAndSetIfChanged(ref _items, value); set => this.RaiseAndSetIfChanged(ref _items, value);
} }
public ObservableCollection<LogEntry> LogEntries { get; } = new();
public ProcessBook2 SelectedItem { get; set; } public ProcessBook2 SelectedItem { get; set; }
public void WriteLine(string text) public void WriteLine(string text)
{ {
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
LogEntries.Add(new() LogEntries.Add(new()
{ {
LogDate = DateTime.Now, LogDate = DateTime.Now,
LogMessage = text.Trim() LogMessage = text.Trim()
})); }));
} }
} }
public class LogEntry public class LogEntry
@ -40,5 +34,4 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
public string LogDateString => LogDate.ToShortTimeString(); public string LogDateString => LogDate.ToShortTimeString();
public string LogMessage { get; init; } public string LogMessage { get; init; }
} }
} }

View File

@ -1,6 +1,6 @@
using DataLayer; using DataLayer;
using Dinah.Core; using Dinah.Core;
using LibationWinForms.GridView; using ReactiveUI;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
@ -21,13 +21,8 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
var removeCount = Children.Count(c => c.Remove == true); var removeCount = Children.Count(c => c.Remove == true);
if (removeCount == 0) _remove = removeCount == 0 ? false : (removeCount == Children.Count ? true : null);
_remove = false; this.RaisePropertyChanged(nameof(Remove));
else if (removeCount == Children.Count)
_remove = true;
else
_remove = null;
NotifyPropertyChanged(nameof(Remove));
} }
#region Model properties exposed to the view #region Model properties exposed to the view
@ -36,7 +31,7 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
get => _remove; get => _remove;
set set
{ {
_remove = value.HasValue ? value : false; _remove = value.HasValue ? value.Value : false;
suspendCounting = true; suspendCounting = true;
@ -44,13 +39,12 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
item.Remove = value; item.Remove = value;
suspendCounting = false; suspendCounting = false;
this.RaisePropertyChanged(nameof(Remove));
NotifyPropertyChanged();
} }
} }
public override LiberateButtonStatus2 Liberate { get; } public override LiberateButtonStatus2 Liberate { get; }
public override BookTags BookTags { get; } = new() { IsSeries = true }; public override BookTags BookTags { get; } = new();
#endregion #endregion
@ -95,19 +89,6 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
int bookLenMins = Children.Sum(c => c.LibraryBook.Book.LengthInMinutes); int bookLenMins = Children.Sum(c => c.LibraryBook.Book.LengthInMinutes);
Length = bookLenMins == 0 ? "" : $"{bookLenMins / 60} hr {bookLenMins % 60} min"; Length = bookLenMins == 0 ? "" : $"{bookLenMins / 60} hr {bookLenMins % 60} min";
NotifyPropertyChanged(nameof(Title));
NotifyPropertyChanged(nameof(Series));
NotifyPropertyChanged(nameof(Length));
NotifyPropertyChanged(nameof(MyRating));
NotifyPropertyChanged(nameof(PurchaseDate));
NotifyPropertyChanged(nameof(ProductRating));
NotifyPropertyChanged(nameof(Authors));
NotifyPropertyChanged(nameof(Narrators));
NotifyPropertyChanged(nameof(Category));
NotifyPropertyChanged(nameof(Misc));
NotifyPropertyChanged(nameof(LongDescription));
NotifyPropertyChanged(nameof(Description));
} }
#region Data Sorting #region Data Sorting

View File

@ -1,3 +1,4 @@
using Avalonia.Controls;
using ReactiveUI; using ReactiveUI;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -7,5 +8,6 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
{ {
public class ViewModelBase : ReactiveObject public class ViewModelBase : ReactiveObject
{ {
} }
} }

View File

@ -11,7 +11,7 @@
<DataGrid Name="productsGrid" AutoGenerateColumns="False" Items="{Binding GridEntries}"> <DataGrid Name="productsGrid" AutoGenerateColumns="False" Items="{Binding GridEntries}">
<DataGrid.Columns> <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 CanUserSort="True" Width="75" Header="Liberate" SortMemberPath="Liberate">
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
@ -147,7 +147,7 @@
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
<DataTemplate> <DataTemplate>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Button IsVisible="{Binding !BookTags.IsSeries}" Width="100" Height="80" Click="OnTagsButtonClick" ToolTip.Tip="Click to edit tags" > <Button IsVisible="{Binding !Liberate.IsSeries}" Width="100" Height="80" Click="OnTagsButtonClick" ToolTip.Tip="Click to edit tags" >
<Panel> <Panel>
<Image IsVisible="{Binding !BookTags.HasTags}" Stretch="None" Source="/AvaloniaUI/Assets/edit_25x25.png" /> <Image IsVisible="{Binding !BookTags.HasTags}" Stretch="None" Source="/AvaloniaUI/Assets/edit_25x25.png" />
<TextBlock IsVisible="{Binding BookTags.HasTags}" FontSize="12" TextWrapping="WrapWithOverflow" Text="{Binding BookTags.Tags}"/> <TextBlock IsVisible="{Binding BookTags.HasTags}" FontSize="12" TextWrapping="WrapWithOverflow" Text="{Binding BookTags.Tags}"/>

View File

@ -9,6 +9,7 @@ using Dinah.Core.DataBinding;
using FileLiberator; using FileLiberator;
using LibationFileManager; using LibationFileManager;
using LibationWinForms.AvaloniaUI.ViewModels; using LibationWinForms.AvaloniaUI.ViewModels;
using ReactiveUI;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
@ -38,6 +39,20 @@ namespace LibationWinForms.AvaloniaUI.Views
.ToList(); .ToList();
DataGridColumn removeGVColumn; DataGridColumn removeGVColumn;
DataGridColumn liberateGVColumn;
DataGridColumn coverGVColumn;
DataGridColumn titleGVColumn;
DataGridColumn authorsGVColumn;
DataGridColumn narratorsGVColumn;
DataGridColumn lengthGVColumn;
DataGridColumn seriesGVColumn;
DataGridColumn descriptionGVColumn;
DataGridColumn categoryGVColumn;
DataGridColumn productRatingGVColumn;
DataGridColumn purchaseDateGVColumn;
DataGridColumn myRatingGVColumn;
DataGridColumn miscGVColumn;
DataGridColumn tagAndDetailsGVColumn;
public ProductsDisplay2() public ProductsDisplay2()
{ {
InitializeComponent(); InitializeComponent();
@ -45,16 +60,79 @@ namespace LibationWinForms.AvaloniaUI.Views
productsGrid = this.FindControl<DataGrid>(nameof(productsGrid)); productsGrid = this.FindControl<DataGrid>(nameof(productsGrid));
productsGrid.Sorting += Dg1_Sorting; productsGrid.Sorting += Dg1_Sorting;
productsGrid.CanUserSortColumns = true; productsGrid.CanUserSortColumns = true;
productsGrid.LoadingRow += ProductsGrid_LoadingRow;
removeGVColumn = productsGrid.Columns[0]; removeGVColumn = productsGrid.Columns[0];
liberateGVColumn = productsGrid.Columns[1];
coverGVColumn = productsGrid.Columns[2];
titleGVColumn = productsGrid.Columns[3];
authorsGVColumn = productsGrid.Columns[4];
narratorsGVColumn = productsGrid.Columns[5];
lengthGVColumn = productsGrid.Columns[6];
seriesGVColumn = productsGrid.Columns[7];
descriptionGVColumn = productsGrid.Columns[8];
categoryGVColumn = productsGrid.Columns[9];
productRatingGVColumn = productsGrid.Columns[10];
purchaseDateGVColumn = productsGrid.Columns[11];
myRatingGVColumn = productsGrid.Columns[12];
miscGVColumn = productsGrid.Columns[13];
tagAndDetailsGVColumn = productsGrid.Columns[14];
removeGVColumn.CustomSortComparer = new RowComparer(removeGVColumn);
liberateGVColumn.CustomSortComparer = new RowComparer(liberateGVColumn);
titleGVColumn.CustomSortComparer = new RowComparer(titleGVColumn);
authorsGVColumn.CustomSortComparer = new RowComparer(authorsGVColumn);
narratorsGVColumn.CustomSortComparer = new RowComparer(narratorsGVColumn);
lengthGVColumn.CustomSortComparer = new RowComparer(lengthGVColumn);
seriesGVColumn.CustomSortComparer = new RowComparer(seriesGVColumn);
descriptionGVColumn.CustomSortComparer = new RowComparer(descriptionGVColumn);
categoryGVColumn.CustomSortComparer = new RowComparer(categoryGVColumn);
productRatingGVColumn.CustomSortComparer = new RowComparer(productRatingGVColumn);
purchaseDateGVColumn.CustomSortComparer = new RowComparer(purchaseDateGVColumn);
myRatingGVColumn.CustomSortComparer = new RowComparer(myRatingGVColumn);
miscGVColumn.CustomSortComparer = new RowComparer(miscGVColumn);
tagAndDetailsGVColumn.CustomSortComparer = new RowComparer(tagAndDetailsGVColumn);
removeGVColumn.PropertyChanged += RemoveGVColumn_PropertyChanged;
if (Design.IsDesignMode) if (Design.IsDesignMode)
{ {
using var context = DbContexts.GetContext(); using var context = DbContexts.GetContext();
var book = context.GetLibraryBook_Flat_NoTracking("B017V4IM1G"); var book = context.GetLibraryBook_Flat_NoTracking("B017V4IM1G");
productsGrid.DataContext = _viewModel = new ProductsDisplayViewModel(new List<LibraryBook> { book }); productsGrid.DataContext = _viewModel = new ProductsDisplayViewModel(new List<LibraryBook> { book });
return;
} }
} }
private void RemoveGVColumn_PropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)
{
}
private static object tagObj = new();
private static readonly IBrush SeriesBgColor = Brush.Parse("#ffe6ffe6");
private void ProductsGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
if (e.Row.Tag == tagObj)
return;
e.Row.Tag = tagObj;
static IBrush GetRowColor(DataGridRow row)
=> row.DataContext is GridEntry2 gEntry
&& gEntry is LibraryBookEntry2 lbEntry
&& lbEntry.Parent is not null
? SeriesBgColor
: null;
e.Row.Background = GetRowColor(e.Row);
e.Row.DataContextChanged += (sender, e) =>
{
var row = sender as DataGridRow;
row.Background = GetRowColor(row);
};
}
private void InitializeComponent() private void InitializeComponent()
{ {
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
@ -69,6 +147,12 @@ namespace LibationWinForms.AvaloniaUI.Views
public string PropertyName { get; init; } public string PropertyName { get; init; }
public ListSortDirection? SortDirection { get; set; } public ListSortDirection? SortDirection { get; set; }
public RowComparer(DataGridColumn column)
{
Column = column;
PropertyName = column.SortMemberPath;
}
/// <summary> /// <summary>
/// This compare method ensures that all top-level grid entries (standalone books or series parents) /// 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 /// are sorted by PropertyName while all episodes remain immediately beneath their parents and remain
@ -137,24 +221,15 @@ namespace LibationWinForms.AvaloniaUI.Views
} }
} }
Dictionary<DataGridColumn, RowComparer> ColumnComparers = new();
DataGridColumn CurrentSortColumn; DataGridColumn CurrentSortColumn;
private void Dg1_Sorting(object sender, DataGridColumnEventArgs e) private void Dg1_Sorting(object sender, DataGridColumnEventArgs e)
{ {
if (!ColumnComparers.ContainsKey(e.Column)) var comparer = e.Column.CustomSortComparer as RowComparer;
ColumnComparers[e.Column] = new RowComparer
{
Column = e.Column,
PropertyName = e.Column.SortMemberPath
};
//Force the comparer to get the current sort order. We can't //Force the comparer to get the current sort order. We can't
//retrieve it from inside this event handler because Avalonia //retrieve it from inside this event handler because Avalonia
//doesn't set the property until after this event. //doesn't set the property until after this event.
ColumnComparers[e.Column].SortDirection = null; comparer.SortDirection = null;
e.Column.CustomSortComparer = ColumnComparers[e.Column];
CurrentSortColumn = e.Column; CurrentSortColumn = e.Column;
} }
@ -391,8 +466,8 @@ namespace LibationWinForms.AvaloniaUI.Views
//In Avalonia, if you fire PropertyChanged with an empty or invalid property name, nothing is updated. //In Avalonia, if you fire PropertyChanged with an empty or invalid property name, nothing is updated.
//So we must notify for specific properties that we believed changed. //So we must notify for specific properties that we believed changed.
removed.Parent.NotifyPropertyChanged(nameof(SeriesEntrys2.Length)); removed.Parent.RaisePropertyChanged(nameof(SeriesEntrys2.Length));
removed.Parent.NotifyPropertyChanged(nameof(SeriesEntrys2.PurchaseDate)); removed.Parent.RaisePropertyChanged(nameof(SeriesEntrys2.PurchaseDate));
} }
//Remove series that have no children //Remove series that have no children
@ -466,8 +541,8 @@ namespace LibationWinForms.AvaloniaUI.Views
else else
bindingList.CollapseItem(seriesEntry); bindingList.CollapseItem(seriesEntry);
seriesEntry.NotifyPropertyChanged(nameof(SeriesEntrys2.Length)); seriesEntry.RaisePropertyChanged(nameof(SeriesEntrys2.Length));
seriesEntry.NotifyPropertyChanged(nameof(SeriesEntrys2.PurchaseDate)); seriesEntry.RaisePropertyChanged(nameof(SeriesEntrys2.PurchaseDate));
} }
else else
existingEpisodeEntry.UpdateLibraryBook(episodeBook); existingEpisodeEntry.UpdateLibraryBook(episodeBook);
@ -493,7 +568,9 @@ namespace LibationWinForms.AvaloniaUI.Views
if (CurrentSortColumn is null) if (CurrentSortColumn is null)
bindingList.InternalList.Sort((i1, i2) => i2.DateAdded.CompareTo(i1.DateAdded)); bindingList.InternalList.Sort((i1, i2) => i2.DateAdded.CompareTo(i1.DateAdded));
else else
CurrentSortColumn?.Sort(ColumnComparers[CurrentSortColumn].SortDirection.Value); {
CurrentSortColumn.Sort(((RowComparer)CurrentSortColumn.CustomSortComparer).SortDirection ?? ListSortDirection.Ascending);
}
bindingList.ResetCollection(); bindingList.ResetCollection();
} }