diff --git a/Source/ApplicationServices/LibraryCommands.cs b/Source/ApplicationServices/LibraryCommands.cs
index 8d6f5a05..21123ec5 100644
--- a/Source/ApplicationServices/LibraryCommands.cs
+++ b/Source/ApplicationServices/LibraryCommands.cs
@@ -439,7 +439,7 @@ namespace ApplicationServices
udi.SetPdfStatus(pdfStatus);
if (rating is not null)
- udi.Rating = rating;
+ udi.UpdateRating(rating.OverallRating, rating.PerformanceRating, rating.StoryRating);
});
public static int UpdateBookStatus(this Book book, LiberatedStatus bookStatus)
@@ -497,7 +497,7 @@ namespace ApplicationServices
context.Attach(book.UserDefinedItem.Rating).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
}
- var qtyChanges = context.SaveChanges();
+ var qtyChanges = context.SaveChanges();
if (qtyChanges > 0)
BookUserDefinedItemCommitted?.Invoke(null, books);
diff --git a/Source/DataLayer/EfClasses/UserDefinedItem.cs b/Source/DataLayer/EfClasses/UserDefinedItem.cs
index 177eb79b..e97387ae 100644
--- a/Source/DataLayer/EfClasses/UserDefinedItem.cs
+++ b/Source/DataLayer/EfClasses/UserDefinedItem.cs
@@ -95,9 +95,9 @@ namespace DataLayer
#region Rating
// owned: not an optional one-to-one
/// The user's individual book rating
- public Rating Rating { get; set; } = new Rating(0, 0, 0);
+ public Rating Rating { get; private set; } = new Rating(0, 0, 0);
- public void UpdateRating(float overallRating, float performanceRating, float storyRating)
+ public void UpdateRating(float overallRating, float performanceRating, float storyRating)
=> Rating.Update(overallRating, performanceRating, storyRating);
#endregion
diff --git a/Source/LibationAvalonia/Controls/RatingBox.axaml b/Source/LibationAvalonia/Controls/MyRatingCellEditor.axaml
similarity index 59%
rename from Source/LibationAvalonia/Controls/RatingBox.axaml
rename to Source/LibationAvalonia/Controls/MyRatingCellEditor.axaml
index 4f3f6a03..27b2592d 100644
--- a/Source/LibationAvalonia/Controls/RatingBox.axaml
+++ b/Source/LibationAvalonia/Controls/MyRatingCellEditor.axaml
@@ -3,49 +3,49 @@
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.RatingBox">
-
+ x:Class="LibationAvalonia.Controls.MyRatingCellEditor">
+
+
-
-
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/Source/LibationAvalonia/Controls/MyRatingCellEditor.axaml.cs b/Source/LibationAvalonia/Controls/MyRatingCellEditor.axaml.cs
new file mode 100644
index 00000000..89a5ec91
--- /dev/null
+++ b/Source/LibationAvalonia/Controls/MyRatingCellEditor.axaml.cs
@@ -0,0 +1,108 @@
+using Avalonia;
+using Avalonia.Controls;
+using DataLayer;
+using System.Linq;
+
+namespace LibationAvalonia.Controls
+{
+ public partial class MyRatingCellEditor : UserControl
+ {
+ private const string SOLID_STAR = "★";
+ private const string HOLLOW_STAR = "☆";
+
+ public static readonly StyledProperty RatingProperty =
+ AvaloniaProperty.Register(nameof(Rating));
+
+ public bool AllRatingsVisible { get; set; }
+ public Rating Rating
+ {
+ get { return GetValue(RatingProperty); }
+ set { SetValue(RatingProperty, value); }
+ }
+ public MyRatingCellEditor()
+ {
+ InitializeComponent();
+ if (Design.IsDesignMode)
+ Rating = new Rating(5, 4, 3);
+ }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ if (change.Property.Name == nameof(Rating) && Rating is not null)
+ {
+ int rating = 0;
+ foreach (TextBlock star in panelOverall.Children)
+ star.Tag = star.Text = Rating.OverallRating > rating++ ? SOLID_STAR : HOLLOW_STAR;
+
+ rating = 0;
+ foreach (TextBlock star in panelPerform.Children)
+ star.Tag = star.Text = Rating.PerformanceRating > rating++ ? SOLID_STAR : HOLLOW_STAR;
+
+ rating = 0;
+ foreach (TextBlock star in panelStory.Children)
+ star.Tag = star.Text = Rating.StoryRating > rating++ ? SOLID_STAR : HOLLOW_STAR;
+
+ SetVisible(AllRatingsVisible);
+ }
+ base.OnPropertyChanged(change);
+ }
+
+ private void SetVisible(bool allVisible)
+ {
+ tblockOverall.IsVisible = panelOverall.IsVisible = allVisible || Rating?.OverallRating > 0;
+ tblockPerform.IsVisible = panelPerform.IsVisible = allVisible || Rating?.PerformanceRating > 0;
+ tblockStory.IsVisible = panelStory.IsVisible = allVisible || Rating?.StoryRating > 0;
+ }
+
+ public void Panel_PointerExited(object sender, Avalonia.Input.PointerEventArgs e)
+ {
+ var panel = sender as Panel;
+ var stackPanel = panel.Children.OfType().Single();
+
+ //Restore defaults
+ foreach (TextBlock child in stackPanel.Children)
+ child.Text = (string)child.Tag;
+ }
+
+ public void Star_PointerEntered(object sender, Avalonia.Input.PointerEventArgs e)
+ {
+ var thisTbox = sender as TextBlock;
+ var stackPanel = thisTbox.Parent as StackPanel;
+ var star = SOLID_STAR;
+
+ foreach (TextBlock child in stackPanel.Children)
+ {
+ child.Text = star;
+ if (child == thisTbox) star = HOLLOW_STAR;
+ }
+ }
+
+ public void Star_Tapped(object sender, Avalonia.Input.TappedEventArgs e)
+ {
+ var overall = Rating.OverallRating;
+ var perform = Rating.PerformanceRating;
+ var story = Rating.StoryRating;
+
+ var thisTbox = sender as TextBlock;
+ var stackPanel = thisTbox.Parent as StackPanel;
+
+ int newRatingValue = 0;
+ foreach (var tbox in stackPanel.Children)
+ {
+ newRatingValue++;
+ if (tbox == thisTbox) break;
+ }
+
+ if (stackPanel == panelOverall)
+ overall = newRatingValue;
+ else if (stackPanel == panelPerform)
+ perform = newRatingValue;
+ else if (stackPanel == panelStory)
+ story = newRatingValue;
+
+ if (overall + perform + story == 0f) return;
+
+ Rating = new Rating(overall, perform, story);
+ }
+ }
+}
diff --git a/Source/LibationAvalonia/Controls/MyRatingGridColumn.axaml b/Source/LibationAvalonia/Controls/MyRatingGridColumn.axaml
new file mode 100644
index 00000000..3c77bb14
--- /dev/null
+++ b/Source/LibationAvalonia/Controls/MyRatingGridColumn.axaml
@@ -0,0 +1,8 @@
+
+
+
diff --git a/Source/LibationAvalonia/Controls/MyRatingGridColumn.axaml.cs b/Source/LibationAvalonia/Controls/MyRatingGridColumn.axaml.cs
new file mode 100644
index 00000000..c9e67099
--- /dev/null
+++ b/Source/LibationAvalonia/Controls/MyRatingGridColumn.axaml.cs
@@ -0,0 +1,66 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Interactivity;
+using Avalonia.Markup.Xaml;
+using Avalonia.Media;
+using DataLayer;
+
+namespace LibationAvalonia.Controls
+{
+ public partial class MyRatingGridColumn : DataGridBoundColumn
+ {
+ private static Rating DefaultRating => new Rating(0, 0, 0);
+ public MyRatingGridColumn()
+ {
+ AvaloniaXamlLoader.Load(this);
+ BindingTarget = MyRatingCellEditor.RatingProperty;
+ }
+
+ protected override IControl GenerateElement(DataGridCell cell, object dataItem)
+ {
+ var myRatingElement = new MyRatingCellEditor
+ {
+ Name = "CellMyRatingDisplay",
+ HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Left,
+ VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
+ AllRatingsVisible = false,
+ Margin = new Thickness(3),
+ IsEnabled = false
+ };
+
+ if (Binding != null)
+ {
+ myRatingElement.Bind(BindingTarget, Binding);
+ }
+ return myRatingElement;
+ }
+
+ protected override IControl GenerateEditingElementDirect(DataGridCell cell, object dataItem)
+ {
+ var myRatingElement = new MyRatingCellEditor
+ {
+ Name = "CellMyRatingCellEditor",
+ HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Left,
+ VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
+ AllRatingsVisible = true,
+ Margin = new Thickness(3)
+ };
+
+ return myRatingElement;
+ }
+
+ protected override object PrepareCellForEdit(IControl editingElement, RoutedEventArgs editingEventArgs)
+ => editingElement is MyRatingCellEditor myRating
+ ? myRating.Rating
+ : DefaultRating;
+
+ protected override void CancelCellEdit(IControl editingElement, object uneditedValue)
+ {
+ if (editingElement is MyRatingCellEditor myRating)
+ {
+ var uneditedRating = uneditedValue as Rating;
+ myRating.Rating = uneditedRating ?? DefaultRating;
+ }
+ }
+ }
+}
diff --git a/Source/LibationAvalonia/Controls/RatingBox.axaml.cs b/Source/LibationAvalonia/Controls/RatingBox.axaml.cs
deleted file mode 100644
index f517ec5c..00000000
--- a/Source/LibationAvalonia/Controls/RatingBox.axaml.cs
+++ /dev/null
@@ -1,125 +0,0 @@
-using Avalonia;
-using Avalonia.Controls;
-using DataLayer;
-using NPOI.POIFS.Storage;
-using System.Linq;
-
-namespace LibationAvalonia.Controls
-{
- public partial class RatingBox : UserControl
- {
- private const string SOLID_STAR = "★";
- private const string HOLLOW_STAR = "☆";
- private static readonly char[] FIVE_STARS = { '★', '★', '★', '★', '★' };
-
- public static readonly StyledProperty RatingProperty =
- AvaloniaProperty.Register(nameof(Rating));
-
- public Rating Rating
- {
- get { return GetValue(RatingProperty); }
- set { SetValue(RatingProperty, value); }
- }
-
- protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
- {
- if (change.Property.Name == nameof(Rating) && Rating is not null)
- {
- tblockOverallRating.Text = StarRating((int)Rating.OverallRating);
- tblockPerformRating.Text = StarRating((int)Rating.PerformanceRating);
- tblockStoryRating.Text = StarRating((int)Rating.StoryRating);
-
- if (this.IsPointerOver)
- RatingBox_PointerEntered(this, null);
- else
- RatingBox_PointerExited(this, null);
- }
- base.OnPropertyChanged(change);
- }
- public RatingBox()
- {
- InitializeComponent();
- PointerEntered += RatingBox_PointerEntered;
- PointerExited += RatingBox_PointerExited;
- }
-
- private void RatingBox_PointerExited(object sender, Avalonia.Input.PointerEventArgs e)
- {
- tblockOverall.IsVisible = Rating?.OverallRating > 0;
- tblockPerform.IsVisible = Rating?.PerformanceRating > 0;
- tblockStory.IsVisible = Rating?.StoryRating > 0;
- }
-
- private void RatingBox_PointerEntered(object sender, Avalonia.Input.PointerEventArgs e)
- {
- tblockOverall.IsVisible = true;
- tblockPerform.IsVisible = true;
- tblockStory.IsVisible = true;
- }
-
- private static string StarRating(int rating) => new string(FIVE_STARS, 0, rating);
- public void Panel_PointerExited(object sender, Avalonia.Input.PointerEventArgs e)
- {
- var panel = sender as Panel;
- var stackPanel = panel.Children.OfType().Single();
-
- panel.Children.OfType().Single().IsVisible = true;
- stackPanel.IsVisible = false;
-
- foreach (TextBlock child in stackPanel.Children)
- child.Text = HOLLOW_STAR;
- }
-
- public void Panel_PointerEntered(object sender, Avalonia.Input.PointerEventArgs e)
- {
- var panel = sender as Panel;
-
- panel.Children.OfType().Single().IsVisible = false;
- panel.Children.OfType().Single().IsVisible = true;
- }
-
- public void Star_PointerEntered(object sender, Avalonia.Input.PointerEventArgs e)
- {
- var thisTbox = sender as TextBlock;
- var stackPanel = thisTbox.Parent as StackPanel;
-
- var star = SOLID_STAR;
-
- foreach (TextBlock child in stackPanel.Children)
- {
- child.Text = star;
- if (child == thisTbox) star = HOLLOW_STAR;
- }
- }
-
- public void Star_Tapped(object sender, Avalonia.Input.TappedEventArgs e)
- {
- var overall = Rating.OverallRating;
- var perform = Rating.PerformanceRating;
- var story = Rating.StoryRating;
-
- var thisTbox = sender as TextBlock;
- var stackPanel = thisTbox.Parent as StackPanel;
-
- int newRating = 0;
- foreach (var tbox in stackPanel.Children)
- {
- newRating++;
- if (tbox == thisTbox) break;
- }
-
- var ratingName = ((Panel)stackPanel.Parent).Children.OfType().Single().Name;
-
- if (ratingName == tblockOverallRating.Name)
- overall = newRating;
- else if (ratingName == tblockPerformRating.Name)
- perform = newRating;
- else if (ratingName == tblockStoryRating.Name)
- story = newRating;
-
- if (overall + perform + story == 0f) return;
-
- Rating = new Rating(overall, perform, story);
- }
- }
-}
diff --git a/Source/LibationAvalonia/ViewModels/GridEntry.cs b/Source/LibationAvalonia/ViewModels/GridEntry.cs
index 71007456..bea4fadb 100644
--- a/Source/LibationAvalonia/ViewModels/GridEntry.cs
+++ b/Source/LibationAvalonia/ViewModels/GridEntry.cs
@@ -45,7 +45,7 @@ namespace LibationAvalonia.ViewModels
public string Misc { get; protected set; }
public string Description { get; protected set; }
public string ProductRating { get; protected set; }
- public string MyRatingString => Book.UserDefinedItem.Rating?.ToStarString()?.DefaultIfNullOrWhiteSpace("");
+ public string MyRatingString => MyRating?.ToStarString()?.DefaultIfNullOrWhiteSpace("");
protected Rating _myRating;
public Rating MyRating
{
diff --git a/Source/LibationAvalonia/ViewModels/LibraryBookEntry.cs b/Source/LibationAvalonia/ViewModels/LibraryBookEntry.cs
index a172e87b..c79f4cfd 100644
--- a/Source/LibationAvalonia/ViewModels/LibraryBookEntry.cs
+++ b/Source/LibationAvalonia/ViewModels/LibraryBookEntry.cs
@@ -65,7 +65,9 @@ namespace LibationAvalonia.ViewModels
Title = Book.Title;
Series = Book.SeriesNames();
Length = Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min";
- _myRating = Book.UserDefinedItem.Rating;
+ //Ratings are changed using Update(), which is a problem for Avalonia data bindings because
+ //the reference doesn't change. Clone the rating so that it updates within Avalonia properly.
+ _myRating = new Rating(Book.UserDefinedItem.Rating.OverallRating, Book.UserDefinedItem.Rating.PerformanceRating, Book.UserDefinedItem.Rating.StoryRating);
PurchaseDate = libraryBook.DateAdded.ToString("d");
ProductRating = Book.Rating?.ToStarString()?.DefaultIfNullOrWhiteSpace("");
Authors = Book.AuthorNames();
diff --git a/Source/LibationAvalonia/ViewModels/SeriesEntry.cs b/Source/LibationAvalonia/ViewModels/SeriesEntry.cs
index 67a46db7..c0ca2965 100644
--- a/Source/LibationAvalonia/ViewModels/SeriesEntry.cs
+++ b/Source/LibationAvalonia/ViewModels/SeriesEntry.cs
@@ -68,7 +68,9 @@ namespace LibationAvalonia.ViewModels
Title = Book.Title;
Series = Book.SeriesNames();
- _myRating = Book.UserDefinedItem.Rating;
+ //Ratings are changed using Update(), which is a problem for Avalonia data bindings because
+ //the reference doesn't change. Clone the rating so that it updates within Avalonia properly.
+ _myRating = new Rating(Book.UserDefinedItem.Rating.OverallRating, Book.UserDefinedItem.Rating.PerformanceRating, Book.UserDefinedItem.Rating.StoryRating);
ProductRating = Book.Rating?.ToStarString()?.DefaultIfNullOrWhiteSpace("");
Authors = Book.AuthorNames();
Narrators = Book.NarratorNames();
diff --git a/Source/LibationAvalonia/Views/ProductsDisplay.axaml b/Source/LibationAvalonia/Views/ProductsDisplay.axaml
index 7175106d..0a86d97f 100644
--- a/Source/LibationAvalonia/Views/ProductsDisplay.axaml
+++ b/Source/LibationAvalonia/Views/ProductsDisplay.axaml
@@ -140,7 +140,7 @@
-
+
@@ -160,16 +160,9 @@
-
-
-
-
-
-
-
-
-
+
+
diff --git a/Source/LibationSearchEngine/SearchEngine.cs b/Source/LibationSearchEngine/SearchEngine.cs
index c4537d1f..52404903 100644
--- a/Source/LibationSearchEngine/SearchEngine.cs
+++ b/Source/LibationSearchEngine/SearchEngine.cs
@@ -90,8 +90,8 @@ namespace LibationSearchEngine
["ProductRating"] = lb => lb.Book.Rating.OverallRating.ToLuceneString(),
["Rating"] = lb => lb.Book.Rating.OverallRating.ToLuceneString(),
- ["UserRating"] = lb => userRating(lb.Book),
- ["MyRating"] = lb => userRating(lb.Book)
+ ["UserRating"] = lb => userOverallRating(lb.Book),
+ ["MyRating"] = lb => userOverallRating(lb.Book)
}
);
@@ -136,7 +136,7 @@ namespace LibationSearchEngine
var narrators = lb.Book.Narrators.Select(a => a.Name).ToArray();
return authors.Intersect(narrators).Any();
}
- private static string userRating(Book book) => book.UserDefinedItem.Rating.OverallRating.ToLuceneString();
+ private static string userOverallRating(Book book) => book.UserDefinedItem.Rating.OverallRating.ToLuceneString();
private static bool isLiberated(Book book) => book.UserDefinedItem.BookStatus == LiberatedStatus.Liberated;
private static bool liberatedError(Book book) => book.UserDefinedItem.BookStatus == LiberatedStatus.Error;
@@ -300,7 +300,7 @@ namespace LibationSearchEngine
// fields are key value pairs. MULTIPLE FIELDS CAN POTENTIALLY HAVE THE SAME KEY.
// ie: must remove old before adding new else will create unwanted duplicates.
- var v1 = userRating(book);
+ var v1 = userOverallRating(book);
d.RemoveField("UserRating");
d.AddNotAnalyzed("UserRating", v1);
d.RemoveField("MyRating");
diff --git a/Source/LibationWinForms/GridView/MyRatingCellEditor.cs b/Source/LibationWinForms/GridView/MyRatingCellEditor.cs
index 7691b32e..6230b0eb 100644
--- a/Source/LibationWinForms/GridView/MyRatingCellEditor.cs
+++ b/Source/LibationWinForms/GridView/MyRatingCellEditor.cs
@@ -39,10 +39,10 @@ namespace LibationWinForms.GridView
private void Star_MouseEnter(object sender, EventArgs e)
{
var thisTbox = sender as Label;
- var stackPanel = thisTbox.Parent as Panel;
+ var panel = thisTbox.Parent as Panel;
var star = SOLID_STAR;
- foreach (Label child in stackPanel.Controls)
+ foreach (Label child in panel.Controls)
{
child.Text = star;
if (child == thisTbox) star = HOLLOW_STAR;
diff --git a/Source/LibationWinForms/GridView/MyRatingGridViewColumn.cs b/Source/LibationWinForms/GridView/MyRatingGridViewColumn.cs
index ea174443..331c6123 100644
--- a/Source/LibationWinForms/GridView/MyRatingGridViewColumn.cs
+++ b/Source/LibationWinForms/GridView/MyRatingGridViewColumn.cs
@@ -26,7 +26,7 @@ namespace LibationWinForms.GridView
internal class MyRatingGridViewCell : DataGridViewTextBoxCell
{
- private Rating DefaultRating => new Rating(0, 0, 0);
+ private static Rating DefaultRating => new Rating(0, 0, 0);
public override object DefaultNewRowValue => DefaultRating;
public override Type EditType => typeof(MyRatingCellEditor);
public override Type ValueType => typeof(Rating);
@@ -45,6 +45,7 @@ namespace LibationWinForms.GridView
if (value is Rating rating)
{
var starString = rating.ToStarString();
+ ToolTipText = starString;
base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, starString, starString, errorText, cellStyle, advancedBorderStyle, paintParts);
}
else