Add column customizations

This commit is contained in:
Michael Bucari-Tovo 2022-07-13 18:47:43 -06:00
parent c727286d22
commit e23e267d17
8 changed files with 157 additions and 78 deletions

View File

@ -33,7 +33,7 @@
</MenuItem.Styles> </MenuItem.Styles>
<MenuItem Name="autoScanLibraryToolStripMenuItem" Click="autoScanLibraryToolStripMenuItem_Click" Header="A_uto Scan Library"> <MenuItem Name="autoScanLibraryToolStripMenuItem" Click="autoScanLibraryToolStripMenuItem_Click" Header="A_uto Scan Library">
<MenuItem.Icon> <MenuItem.Icon>
<CheckBox Name="autoScanLibraryToolStripMenuItemCheckbox" BorderThickness="0" IsHitTestVisible="False">Toggle _Me0</CheckBox> <CheckBox Name="autoScanLibraryToolStripMenuItemCheckbox" BorderThickness="0" IsHitTestVisible="False" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<MenuItem Name="noAccountsYetAddAccountToolStripMenuItem" Click="noAccountsYetAddAccountToolStripMenuItem_Click" Header="No accounts yet. A_dd Account..." /> <MenuItem Name="noAccountsYetAddAccountToolStripMenuItem" Click="noAccountsYetAddAccountToolStripMenuItem_Click" Header="No accounts yet. A_dd Account..." />

View File

@ -1,16 +1,12 @@
using ApplicationServices; using ApplicationServices;
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Threading;
using DataLayer; using DataLayer;
using LibationWinForms.AvaloniaUI.ViewModels; using LibationWinForms.AvaloniaUI.ViewModels;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
namespace LibationWinForms.AvaloniaUI.Views namespace LibationWinForms.AvaloniaUI.Views
{ {
@ -20,7 +16,6 @@ namespace LibationWinForms.AvaloniaUI.Views
private ItemsRepeater _repeater; private ItemsRepeater _repeater;
private ScrollViewer _scroller; private ScrollViewer _scroller;
private int _selectedIndex; private int _selectedIndex;
private Random _random = new Random(0);
private TrackedQueue2<ProcessBook2> Queue => _viewModel.Items; private TrackedQueue2<ProcessBook2> Queue => _viewModel.Items;
@ -101,6 +96,8 @@ namespace LibationWinForms.AvaloniaUI.Views
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
} }
#region Add Books to Queue
private bool isBookInQueue(LibraryBook libraryBook) private bool isBookInQueue(LibraryBook libraryBook)
=> Queue.Any(b => b?.LibraryBook?.Book?.AudibleProductId == libraryBook.Book.AudibleProductId); => Queue.Any(b => b?.LibraryBook?.Book?.AudibleProductId == libraryBook.Book.AudibleProductId);
@ -165,6 +162,8 @@ namespace LibationWinForms.AvaloniaUI.Views
_viewModel.AddToQueue(procs); _viewModel.AddToQueue(procs);
} }
#endregion
#region Control event handlers #region Control event handlers
private async void ProcessBookControl2_CancelButtonClicked(ProcessBook2 item) private async void ProcessBookControl2_CancelButtonClicked(ProcessBook2 item)

View File

@ -1,13 +1,124 @@
using System; using Avalonia.Controls;
using LibationFileManager;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LibationWinForms.AvaloniaUI.Views.ProductsGrid namespace LibationWinForms.AvaloniaUI.Views.ProductsGrid
{ {
public partial class ProductsDisplay2 public partial class ProductsDisplay2
{ {
private void Configure_ColumnCustomization() { } ContextMenu contextMenuStrip1 = new ContextMenu();
private void Configure_ColumnCustomization()
{
if (Design.IsDesignMode) return;
productsGrid.ColumnDisplayIndexChanged += ProductsGrid_ColumnDisplayIndexChanged;
var config = Configuration.Instance;
var gridColumnsVisibilities = config.GridColumnsVisibilities;
var displayIndices = config.GridColumnsDisplayIndices;
var contextMenu = new ContextMenu();
contextMenu.MenuClosed += ContextMenu_MenuClosed;
contextMenu.ContextMenuOpening += ContextMenu_ContextMenuOpening;
List<Control> menuItems = new();
contextMenu.Items = menuItems;
menuItems.Add(new MenuItem { Header = "Show / Hide Columns" });
menuItems.Add(new MenuItem { Header = "-" });
var HeaderCell_PI = typeof(DataGridColumn).GetProperty("HeaderCell", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
foreach (var column in productsGrid.Columns)
{
var itemName = column.SortMemberPath;
if (itemName == nameof(ViewModels.GridEntry2.Remove))
continue;
menuItems.Add
(
new MenuItem
{
Header = ((string)column.Header).Replace((char)0xa,' '),
Tag = column,
Margin = new Avalonia.Thickness(6 ,0),
Icon = new CheckBox
{
Width = 50,
}
}
);
var headercell = HeaderCell_PI.GetValue(column) as DataGridColumnHeader;
headercell.ContextMenu = contextMenu;
column.IsVisible = gridColumnsVisibilities.GetValueOrDefault(itemName, true);
}
//We must set DisplayIndex properties in ascending order
foreach (var itemName in displayIndices.OrderBy(i => i.Value).Select(i => i.Key))
{
if (!productsGrid.Columns.Any(c => c.SortMemberPath == itemName))
continue;
var column = productsGrid.Columns
.Single(c => c.SortMemberPath == itemName);
column.DisplayIndex = displayIndices.GetValueOrDefault(itemName, productsGrid.Columns.IndexOf(column));
}
//Remove column is always first;
removeGVColumn.DisplayIndex = 0;
removeGVColumn.CanUserReorder = false;
}
private void ContextMenu_ContextMenuOpening(object sender, System.ComponentModel.CancelEventArgs e)
{
var contextMenu = sender as ContextMenu;
foreach (var mi in contextMenu.Items.OfType<MenuItem>())
{
if (mi.Tag is DataGridColumn column)
{
var cbox = mi.Icon as CheckBox;
cbox.IsChecked = column.IsVisible;
}
}
}
private void ContextMenu_MenuClosed(object sender, Avalonia.Interactivity.RoutedEventArgs e)
{
var contextMenu = sender as ContextMenu;
var config = Configuration.Instance;
var dictionary = config.GridColumnsVisibilities;
foreach (var mi in contextMenu.Items.OfType<MenuItem>())
{
if (mi.Tag is DataGridColumn column)
{
var cbox = mi.Icon as CheckBox;
column.IsVisible = cbox.IsChecked == true;
dictionary[column.SortMemberPath] = cbox.IsChecked == true;
}
}
//If all columns are hidden, register the context menu on the grid so users can unhide.
if (!productsGrid.Columns.Any(c => c.IsVisible))
productsGrid.ContextMenu = contextMenu;
else
productsGrid.ContextMenu = null;
config.GridColumnsVisibilities = dictionary;
}
private void ProductsGrid_ColumnDisplayIndexChanged(object sender, Avalonia.Controls.DataGridColumnEventArgs e)
{
var config = Configuration.Instance;
var dictionary = config.GridColumnsDisplayIndices;
dictionary[e.Column.SortMemberPath] = e.Column.DisplayIndex;
config.GridColumnsDisplayIndices = dictionary;
}
} }
} }

View File

@ -25,8 +25,8 @@ namespace LibationWinForms.AvaloniaUI.Views.ProductsGrid
VisibleCountChanged?.Invoke(this, bindingList.BookEntries().Count()); VisibleCountChanged?.Invoke(this, bindingList.BookEntries().Count());
//Avalonia displays items in the DataConncetion from an internal copy of //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 bound list, not the actual bound list. So we need to reflect to get
//the current display order and set the GridEntry.ListIndex correctly. //the current display order and set each GridEntry.ListIndex correctly.
var DataConnection_PI = typeof(DataGrid).GetProperty("DataConnection", BindingFlags.NonPublic | BindingFlags.Instance); var DataConnection_PI = typeof(DataGrid).GetProperty("DataConnection", BindingFlags.NonPublic | BindingFlags.Instance);
var DataSource_PI = DataConnection_PI.PropertyType.GetProperty("DataSource", BindingFlags.Public | BindingFlags.Instance); var DataSource_PI = DataConnection_PI.PropertyType.GetProperty("DataSource", BindingFlags.Public | BindingFlags.Instance);
@ -41,7 +41,7 @@ namespace LibationWinForms.AvaloniaUI.Views.ProductsGrid
}; };
//Assign the viewmodel after we subscribe to CollectionChanged //Assign the viewmodel after we subscribe to CollectionChanged
//to ensure that out handler executes first. //so that out handler executes first.
productsGrid.DataContext = _viewModel; productsGrid.DataContext = _viewModel;
} }
else else
@ -52,7 +52,6 @@ namespace LibationWinForms.AvaloniaUI.Views.ProductsGrid
bindingList.Filter = existingFilter; bindingList.Filter = existingFilter;
ReSort(); ReSort();
} }
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -14,8 +14,24 @@ namespace LibationWinForms.AvaloniaUI.Views.ProductsGrid
{ {
private void Configure_ScanAndRemove() { } private void Configure_ScanAndRemove() { }
private bool RemoveColumnVisible
{
get => removeGVColumn.IsVisible;
set
{
if (value)
{
foreach (var book in bindingList.AllItems())
book.Remove = false;
}
removeGVColumn.DisplayIndex = 0;
removeGVColumn.CanUserReorder = value;
removeGVColumn.IsVisible = value;
}
}
public void CloseRemoveBooksColumn() public void CloseRemoveBooksColumn()
=> removeGVColumn.IsVisible = false; => RemoveColumnVisible = false;
public async Task RemoveCheckedBooksAsync() public async Task RemoveCheckedBooksAsync()
{ {
@ -39,6 +55,7 @@ namespace LibationWinForms.AvaloniaUI.Views.ProductsGrid
RemovableCountChanged?.Invoke(this, GetAllBookEntries().Count(lbe => lbe.Remove is true)); RemovableCountChanged?.Invoke(this, GetAllBookEntries().Count(lbe => lbe.Remove is true));
} }
public async Task ScanAndRemoveBooksAsync(params Account[] accounts) public async Task ScanAndRemoveBooksAsync(params Account[] accounts)
{ {
RemovableCountChanged?.Invoke(this, 0); RemovableCountChanged?.Invoke(this, 0);

View File

@ -1,38 +1,16 @@
using Avalonia.Controls; using Avalonia.Controls;
using LibationWinForms.AvaloniaUI.ViewModels; using LibationWinForms.AvaloniaUI.ViewModels;
using System; using System;
using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LibationWinForms.AvaloniaUI.Views.ProductsGrid namespace LibationWinForms.AvaloniaUI.Views.ProductsGrid
{ {
public partial class ProductsDisplay2 public partial class ProductsDisplay2
{ {
private DataGridColumn CurrentSortColumn;
private void Configure_Sorting() { } private void Configure_Sorting() { }
private void RegisterCustomColumnComparers()
{
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);
}
private void ReSort() private void ReSort()
{ {
if (CurrentSortColumn is null) if (CurrentSortColumn is null)
@ -46,11 +24,6 @@ namespace LibationWinForms.AvaloniaUI.Views.ProductsGrid
} }
} }
private DataGridColumn CurrentSortColumn;
private void ProductsGrid_Sorting(object sender, DataGridColumnEventArgs e) private void ProductsGrid_Sorting(object sender, DataGridColumnEventArgs e)
{ {
//Force the comparer to get the current sort order. We can't //Force the comparer to get the current sort order. We can't

View File

@ -7,8 +7,13 @@
mc:Ignorable="d" d:DesignWidth="1560" d:DesignHeight="400" mc:Ignorable="d" d:DesignWidth="1560" d:DesignHeight="400"
x:Class="LibationWinForms.AvaloniaUI.Views.ProductsGrid.ProductsDisplay2"> x:Class="LibationWinForms.AvaloniaUI.Views.ProductsGrid.ProductsDisplay2">
<Grid> <Grid>
<DataGrid Name="productsGrid" AutoGenerateColumns="False" Items="{Binding GridEntries}" >
<DataGrid
Name="productsGrid"
AutoGenerateColumns="False"
Items="{Binding GridEntries}"
CanUserReorderColumns="True">
<DataGrid.Columns> <DataGrid.Columns>
@ -26,10 +31,10 @@
</DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn> </DataGridTemplateColumn>
<DataGridTemplateColumn Width="80" Header="Cover"> <DataGridTemplateColumn CanUserSort="False" Width="80" Header="Cover" SortMemberPath="Cover">
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
<DataTemplate> <DataTemplate>
<Image Tapped="Cover_Click" Height="80" Source="{Binding Cover}" /> <Image Tapped="Cover_Click" Height="80" Source="{Binding Cover}" ToolTip.Tip="Click to see full size" />
</DataTemplate> </DataTemplate>
</DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn> </DataGridTemplateColumn>
@ -88,7 +93,7 @@
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
<DataTemplate> <DataTemplate>
<Border BorderThickness="3" Height="80"> <Border BorderThickness="3" Height="80">
<TextBlock Tapped="Description_Click" VerticalAlignment="Center" TextWrapping="Wrap" Text="{Binding Description}" /> <TextBlock Tapped="Description_Click" VerticalAlignment="Center" TextWrapping="Wrap" ToolTip.Tip="Click to see full description" Text="{Binding Description}" />
</Border> </Border>
</DataTemplate> </DataTemplate>
</DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn.CellTemplate>

View File

@ -33,20 +33,6 @@ namespace LibationWinForms.AvaloniaUI.Views.ProductsGrid
private GridEntryBindingList2 bindingList => _viewModel.GridEntries; private GridEntryBindingList2 bindingList => _viewModel.GridEntries;
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()
{ {
@ -59,6 +45,11 @@ namespace LibationWinForms.AvaloniaUI.Views.ProductsGrid
Configure_ScanAndRemove(); Configure_ScanAndRemove();
Configure_Sorting(); Configure_Sorting();
foreach ( var column in productsGrid.Columns)
{
column.CustomSortComparer = new RowComparer(column);
}
if (Design.IsDesignMode) if (Design.IsDesignMode)
{ {
using var context = DbContexts.GetContext(); using var context = DbContexts.GetContext();
@ -78,22 +69,6 @@ namespace LibationWinForms.AvaloniaUI.Views.ProductsGrid
productsGrid.LoadingRow += ProductsGrid_LoadingRow; 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];
RegisterCustomColumnComparers();
} }
private static object tagObj = new(); private static object tagObj = new();