Fix null file bug and add context menu to my ratings column

This commit is contained in:
Michael Bucari-Tovo 2022-12-31 21:09:30 -07:00
parent 613cfdd903
commit a7bf30954d
14 changed files with 118 additions and 144 deletions

View File

@ -1,5 +0,0 @@
<DataGridCheckBoxColumn xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="LibationAvalonia.Controls.DataGridCheckBoxColumnExt">
</DataGridCheckBoxColumn >

View File

@ -1,10 +1,11 @@
using Avalonia.Controls; using Avalonia.Controls;
using LibationAvalonia.ViewModels; using LibationAvalonia.ViewModels;
using System; using System;
using System.Linq;
namespace LibationAvalonia.Controls namespace LibationAvalonia.Controls
{ {
public partial class DataGridCheckBoxColumnExt : DataGridCheckBoxColumn public class DataGridCheckBoxColumnExt : DataGridCheckBoxColumn
{ {
protected override IControl GenerateEditingElementDirect(DataGridCell cell, object dataItem) protected override IControl GenerateEditingElementDirect(DataGridCell cell, object dataItem)
{ {

View File

@ -1,6 +1,5 @@
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using LibationAvalonia.ViewModels; using LibationAvalonia.ViewModels;
using System; using System;
using System.Reflection; using System.Reflection;
@ -19,36 +18,46 @@ namespace LibationAvalonia.Controls
=> GetCellValueMethod.Invoke(column, new object[] { item, column.ClipboardContentBinding })?.ToString() ?? ""; => GetCellValueMethod.Invoke(column, new object[] { item, column.ClipboardContentBinding })?.ToString() ?? "";
public string CellClipboardContents => GetCellValue(Column, GridEntry); public string CellClipboardContents => GetCellValue(Column, GridEntry);
public DataGridTemplateColumnExt Column { get; init; } public DataGridColumn Column { get; init; }
public GridEntry GridEntry { get; init; } public GridEntry GridEntry { get; init; }
public ContextMenu ContextMenu { get; init; } public ContextMenu ContextMenu { get; init; }
public AvaloniaList<MenuItem> ContextMenuItems public AvaloniaList<MenuItem> ContextMenuItems
=> ContextMenu.Items as AvaloniaList<MenuItem>; => ContextMenu.Items as AvaloniaList<MenuItem>;
} }
public partial class DataGridTemplateColumnExt : DataGridTemplateColumn internal static class DataGridContextMenus
{ {
public event EventHandler<DataGridCellContextMenuStripNeededEventArgs> CellContextMenuStripNeeded; public static event EventHandler<DataGridCellContextMenuStripNeededEventArgs> CellContextMenuStripNeeded;
private static readonly ContextMenu ContextMenu = new(); private static readonly ContextMenu ContextMenu = new();
private static readonly AvaloniaList<MenuItem> MenuItems = new(); private static readonly AvaloniaList<MenuItem> MenuItems = new();
private static readonly PropertyInfo OwningColumnProperty;
public DataGridTemplateColumnExt() static DataGridContextMenus()
{ {
AvaloniaXamlLoader.Load(this);
ContextMenu.Items = MenuItems; ContextMenu.Items = MenuItems;
OwningColumnProperty = typeof(DataGridCell).GetProperty("OwningColumn", BindingFlags.Instance | BindingFlags.NonPublic);
} }
private void Cell_ContextRequested(object sender, ContextRequestedEventArgs e) public static void AttachContextMenuToCell(this DataGridCell cell)
{
if (cell.ContextMenu is null)
{
cell.ContextRequested += Cell_ContextRequested;
cell.ContextMenu = ContextMenu;
}
}
private static void Cell_ContextRequested(object sender, ContextRequestedEventArgs e)
{ {
if (sender is DataGridCell cell && cell.DataContext is GridEntry entry) if (sender is DataGridCell cell && cell.DataContext is GridEntry entry)
{ {
var args = new DataGridCellContextMenuStripNeededEventArgs var args = new DataGridCellContextMenuStripNeededEventArgs
{ {
Column = this, Column = OwningColumnProperty.GetValue(cell) as DataGridColumn,
GridEntry = entry, GridEntry = entry,
ContextMenu = ContextMenu ContextMenu = ContextMenu
}; };
args.ContextMenuItems.Clear(); args.ContextMenuItems.Clear();
CellContextMenuStripNeeded?.Invoke(sender, args); CellContextMenuStripNeeded?.Invoke(sender, args);
@ -58,16 +67,5 @@ namespace LibationAvalonia.Controls
else else
e.Handled = true; e.Handled = true;
} }
protected override IControl GenerateElement(DataGridCell cell, object dataItem)
{
if (cell.ContextMenu is null)
{
cell.ContextRequested += Cell_ContextRequested;
cell.ContextMenu = ContextMenu;
}
return base.GenerateElement(cell, dataItem);
}
} }
} }

View File

@ -1,17 +1,15 @@
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using DataLayer; using DataLayer;
namespace LibationAvalonia.Controls namespace LibationAvalonia.Controls
{ {
public partial class MyRatingGridColumn : DataGridBoundColumn public class DataGridMyRatingColumn : DataGridBoundColumn
{ {
private static Rating DefaultRating => new Rating(0, 0, 0); private static Rating DefaultRating => new Rating(0, 0, 0);
public MyRatingGridColumn() public DataGridMyRatingColumn()
{ {
AvaloniaXamlLoader.Load(this);
BindingTarget = MyRatingCellEditor.RatingProperty; BindingTarget = MyRatingCellEditor.RatingProperty;
} }
@ -20,40 +18,26 @@ namespace LibationAvalonia.Controls
var myRatingElement = new MyRatingCellEditor var myRatingElement = new MyRatingCellEditor
{ {
Name = "CellMyRatingDisplay", Name = "CellMyRatingDisplay",
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Left, IsEditingMode = false
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
IsEditingMode = false,
Margin = new Thickness(3),
IsEnabled = false
}; };
//Create a panel that fills the cell to host the rating tool tip ToolTip.SetTip(myRatingElement, "Click to change ratings");
var panel = new Panel cell?.AttachContextMenuToCell();
{
Background = Avalonia.Media.Brushes.Transparent,
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Stretch,
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Stretch,
};
panel.Children.Add(myRatingElement);
ToolTip.SetTip(panel, "Click to change ratings");
if (Binding != null) if (Binding != null)
{ {
myRatingElement.Bind(BindingTarget, Binding); myRatingElement.Bind(BindingTarget, Binding);
} }
return panel;
return myRatingElement;
} }
protected override IControl GenerateEditingElementDirect(DataGridCell cell, object dataItem) protected override IControl GenerateEditingElementDirect(DataGridCell cell, object dataItem)
{ {
var myRatingElement = new MyRatingCellEditor var myRatingElement = new MyRatingCellEditor
{ {
Name = "CellMyRatingCellEditor", Name = "CellMyRatingEditor",
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Left, IsEditingMode = true
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
IsEditingMode = true,
Margin = new Thickness(3)
}; };
return myRatingElement; return myRatingElement;

View File

@ -1,7 +0,0 @@
<DataGridTemplateColumn xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="LibationAvalonia.Controls.DataGridTemplateColumnExt">
</DataGridTemplateColumn>

View File

@ -0,0 +1,15 @@
using Avalonia.Controls;
using System;
using System.Linq;
namespace LibationAvalonia.Controls
{
public partial class DataGridTemplateColumnExt : DataGridTemplateColumn
{
protected override IControl GenerateElement(DataGridCell cell, object dataItem)
{
cell?.AttachContextMenuToCell();
return base.GenerateElement(cell, dataItem);
}
}
}

View File

@ -2,10 +2,11 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="115" d:DesignHeight="80"
x:Class="LibationAvalonia.Controls.MyRatingCellEditor"> x:Class="LibationAvalonia.Controls.MyRatingCellEditor">
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto,Auto"> <Panel Background="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid Name="ratingsGrid" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="3,0,0,0" ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto,Auto">
<Grid.Styles> <Grid.Styles>
<Style Selector="TextBlock"> <Style Selector="TextBlock">
<Setter Property="FontSize" Value="11" /> <Setter Property="FontSize" Value="11" />
@ -49,4 +50,5 @@
</StackPanel> </StackPanel>
</Panel> </Panel>
</Grid> </Grid>
</Panel>
</UserControl> </UserControl>

View File

@ -14,11 +14,8 @@ namespace LibationAvalonia.Controls
AvaloniaProperty.Register<MyRatingCellEditor, Rating>(nameof(Rating)); AvaloniaProperty.Register<MyRatingCellEditor, Rating>(nameof(Rating));
public bool IsEditingMode { get; set; } public bool IsEditingMode { get; set; }
public Rating Rating public Rating Rating { get => GetValue(RatingProperty); set => SetValue(RatingProperty, value); }
{
get { return GetValue(RatingProperty); }
set { SetValue(RatingProperty, value); }
}
public MyRatingCellEditor() public MyRatingCellEditor()
{ {
InitializeComponent(); InitializeComponent();
@ -44,16 +41,17 @@ namespace LibationAvalonia.Controls
foreach (TextBlock star in panelStory.Children) foreach (TextBlock star in panelStory.Children)
star.Tag = star.Text = Rating.StoryRating > rating++ ? SOLID_STAR : blankValue; star.Tag = star.Text = Rating.StoryRating > rating++ ? SOLID_STAR : blankValue;
SetVisible(IsEditingMode); SetVisible();
} }
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
} }
private void SetVisible(bool allVisible) private void SetVisible()
{ {
tblockOverall.IsVisible = panelOverall.IsVisible = allVisible || Rating?.OverallRating > 0; ratingsGrid.IsEnabled = IsEditingMode;
tblockPerform.IsVisible = panelPerform.IsVisible = allVisible || Rating?.PerformanceRating > 0; tblockOverall.IsVisible = panelOverall.IsVisible = IsEditingMode || Rating?.OverallRating > 0;
tblockStory.IsVisible = panelStory.IsVisible = allVisible || Rating?.StoryRating > 0; tblockPerform.IsVisible = panelPerform.IsVisible = IsEditingMode || Rating?.PerformanceRating > 0;
tblockStory.IsVisible = panelStory.IsVisible = IsEditingMode || Rating?.StoryRating > 0;
} }
public void Panel_PointerExited(object sender, Avalonia.Input.PointerEventArgs e) public void Panel_PointerExited(object sender, Avalonia.Input.PointerEventArgs e)

View File

@ -1,8 +0,0 @@
<DataGridBoundColumn xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="LibationAvalonia.Controls.MyRatingGridColumn">
</DataGridBoundColumn>

View File

@ -131,7 +131,7 @@ namespace LibationAvalonia.Dialogs
var selectedFiles = await StorageProvider.OpenFilePickerAsync(openFileDialogOptions); var selectedFiles = await StorageProvider.OpenFilePickerAsync(openFileDialogOptions);
var selectedFile = selectedFiles.SingleOrDefault(); var selectedFile = selectedFiles.SingleOrDefault();
if (!selectedFile.TryGetUri(out var uri)) return; if (selectedFile?.TryGetUri(out var uri) is not true) return;
try try
{ {
@ -291,7 +291,7 @@ namespace LibationAvalonia.Dialogs
var selectedFile = await StorageProvider.SaveFilePickerAsync(options); var selectedFile = await StorageProvider.SaveFilePickerAsync(options);
if (!selectedFile.TryGetUri(out var uri)) return; if (selectedFile?.TryGetUri(out var uri) is not true) return;
try try
{ {

View File

@ -51,7 +51,7 @@ namespace LibationAvalonia.Dialogs
{ {
Title = $"Save Sover Image", Title = $"Save Sover Image",
SuggestedStartLocation = new Avalonia.Platform.Storage.FileIO.BclStorageFolder(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)), SuggestedStartLocation = new Avalonia.Platform.Storage.FileIO.BclStorageFolder(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)),
SuggestedFileName = $"{PictureFileName}.jpg", SuggestedFileName = PictureFileName,
DefaultExtension = "jpg", DefaultExtension = "jpg",
ShowOverwritePrompt = true, ShowOverwritePrompt = true,
FileTypeChoices = new FilePickerFileType[] FileTypeChoices = new FilePickerFileType[]
@ -62,7 +62,7 @@ namespace LibationAvalonia.Dialogs
var selectedFile = await StorageProvider.SaveFilePickerAsync(options); var selectedFile = await StorageProvider.SaveFilePickerAsync(options);
if (!selectedFile.TryGetUri(out var uri)) return; if (selectedFile?.TryGetUri(out var uri) is not true) return;
try try
{ {

View File

@ -34,7 +34,7 @@ namespace LibationAvalonia.Views
var selectedFile = await StorageProvider.SaveFilePickerAsync(options); var selectedFile = await StorageProvider.SaveFilePickerAsync(options);
if (!selectedFile.TryGetUri(out var uri)) return; if (selectedFile?.TryGetUri(out var uri) is not true) return;
var ext = System.IO.Path.GetExtension(uri.LocalPath); var ext = System.IO.Path.GetExtension(uri.LocalPath);
switch (ext) switch (ext)

View File

@ -160,8 +160,7 @@
</DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn.CellTemplate>
</controls:DataGridTemplateColumnExt> </controls:DataGridTemplateColumnExt>
<controls:DataGridMyRatingColumn IsReadOnly="false" Width="115" Header="My Rating" CanUserSort="True" SortMemberPath="MyRating" ClipboardContentBinding="{Binding MyRatingString}" Binding="{Binding MyRating, Mode=TwoWay}" />
<controls:MyRatingGridColumn IsReadOnly="false" Width="115" Header="My Rating" CanUserSort="True" SortMemberPath="MyRating" ClipboardContentBinding="{Binding MyRatingString}" Binding="{Binding MyRating, Mode=TwoWay}" />
<controls:DataGridTemplateColumnExt Width="135" Header="Misc" CanUserSort="True" SortMemberPath="Misc" ClipboardContentBinding="{Binding Misc}"> <controls:DataGridTemplateColumnExt Width="135" Header="Misc" CanUserSort="True" SortMemberPath="Misc" ClipboardContentBinding="{Binding Misc}">
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>

View File

@ -71,6 +71,7 @@ namespace LibationAvalonia.Views
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
productsGrid = this.FindControl<DataGrid>(nameof(productsGrid)); productsGrid = this.FindControl<DataGrid>(nameof(productsGrid));
DataGridContextMenus.CellContextMenuStripNeeded += ProductsGrid_CellContextMenuStripNeeded;
} }
#region Cell Context Menu #region Cell Context Menu
@ -121,7 +122,7 @@ namespace LibationAvalonia.Views
var selectedFiles = await this.GetParentWindow().StorageProvider.OpenFilePickerAsync(openFileDialogOptions); var selectedFiles = await this.GetParentWindow().StorageProvider.OpenFilePickerAsync(openFileDialogOptions);
var selectedFile = selectedFiles.SingleOrDefault(); var selectedFile = selectedFiles.SingleOrDefault();
if (selectedFile.TryGetUri(out var uri)) if (selectedFile?.TryGetUri(out var uri) is true)
FilePathCache.Insert(entry.AudibleProductId, uri.LocalPath); FilePathCache.Insert(entry.AudibleProductId, uri.LocalPath);
} }
catch (Exception ex) catch (Exception ex)
@ -179,10 +180,6 @@ namespace LibationAvalonia.Views
foreach (var column in productsGrid.Columns) foreach (var column in productsGrid.Columns)
{ {
//Wire up column context menu
if (column is DataGridTemplateColumnExt tc)
tc.CellContextMenuStripNeeded += ProductsGrid_CellContextMenuStripNeeded;
var itemName = column.SortMemberPath; var itemName = column.SortMemberPath;
if (itemName == nameof(GridEntry.Remove)) if (itemName == nameof(GridEntry.Remove))