From d48bd5ad0770fef13141511461ad07b839929d25 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Mon, 9 Aug 2021 15:00:24 -0600 Subject: [PATCH] Major UI refactoring. --- LibationLauncher/LibationLauncher.csproj | 2 +- .../DataGridViewImageButtonColumn.cs | 50 +++ LibationWinForms/Dialogs/RemoveBooksDialog.cs | 21 +- .../EditTagsDataGridViewImageButtonColumn.cs | 26 ++ LibationWinForms/GridEntry.cs | 356 +++++++++------- LibationWinForms/IObjectMemberComparable.cs | 11 + .../LiberateDataGridViewImageButtonColumn.cs | 30 ++ LibationWinForms/ObjectComparer[T].cs | 10 + LibationWinForms/ObjectMemberComparer[T].cs | 21 + LibationWinForms/ProductsGrid.Designer.cs | 363 ++++++++++------- LibationWinForms/ProductsGrid.cs | 383 +++++------------- LibationWinForms/ProductsGrid.resx | 77 +--- LibationWinForms/SortableBindingList2[T].cs | 72 ++++ 13 files changed, 769 insertions(+), 653 deletions(-) create mode 100644 LibationWinForms/DataGridViewImageButtonColumn.cs create mode 100644 LibationWinForms/EditTagsDataGridViewImageButtonColumn.cs create mode 100644 LibationWinForms/IObjectMemberComparable.cs create mode 100644 LibationWinForms/LiberateDataGridViewImageButtonColumn.cs create mode 100644 LibationWinForms/ObjectComparer[T].cs create mode 100644 LibationWinForms/ObjectMemberComparer[T].cs create mode 100644 LibationWinForms/SortableBindingList2[T].cs diff --git a/LibationLauncher/LibationLauncher.csproj b/LibationLauncher/LibationLauncher.csproj index c0d121d3..5af2cd25 100644 --- a/LibationLauncher/LibationLauncher.csproj +++ b/LibationLauncher/LibationLauncher.csproj @@ -13,7 +13,7 @@ win-x64 - 5.4.9.0 + 5.4.9.79 diff --git a/LibationWinForms/DataGridViewImageButtonColumn.cs b/LibationWinForms/DataGridViewImageButtonColumn.cs new file mode 100644 index 00000000..31d41780 --- /dev/null +++ b/LibationWinForms/DataGridViewImageButtonColumn.cs @@ -0,0 +1,50 @@ +using System.Drawing; +using System.Windows.Forms; + +namespace LibationWinForms +{ + public abstract class DataGridViewImageButtonColumn : DataGridViewButtonColumn + { + private DataGridViewImageButtonCell _cellTemplate; + public override DataGridViewCell CellTemplate + { + get => GetCellTemplate(); + set + { + if (value is DataGridViewImageButtonCell cellTemplate) + _cellTemplate = cellTemplate; + } + } + + protected abstract DataGridViewImageButtonCell NewCell(); + + private DataGridViewImageButtonCell GetCellTemplate() + { + if (_cellTemplate is null) + return NewCell(); + else + return _cellTemplate; + } + + public override object Clone() + { + var clone = (DataGridViewImageButtonColumn)base.Clone(); + clone._cellTemplate = _cellTemplate; + + return clone; + } + } + + public class DataGridViewImageButtonCell : DataGridViewButtonCell + { + protected void DrawImage(Graphics graphics, Bitmap image, Rectangle cellBounds) + { + var w = image.Width; + var h = image.Height; + var x = cellBounds.Left + (cellBounds.Width - w) / 2; + var y = cellBounds.Top + (cellBounds.Height - h) / 2; + + graphics.DrawImage(image, new Rectangle(x, y, w, h)); + } + } +} diff --git a/LibationWinForms/Dialogs/RemoveBooksDialog.cs b/LibationWinForms/Dialogs/RemoveBooksDialog.cs index 273176cb..7bfafb8c 100644 --- a/LibationWinForms/Dialogs/RemoveBooksDialog.cs +++ b/LibationWinForms/Dialogs/RemoveBooksDialog.cs @@ -40,7 +40,7 @@ namespace LibationWinForms.Dialogs var orderedGridEntries = _libraryBooks .Select(lb => new RemovableGridEntry(new GridEntry(lb))) - .OrderByDescending(ge => ge.GridEntry.Purchase_Date) + .OrderByDescending(ge => ge.GridEntry.PurchaseDate) .ToList(); _removableGridEntries = orderedGridEntries.ToSortableBindingList(); @@ -173,10 +173,10 @@ namespace LibationWinForms.Dialogs NotifyPropertyChanged(); } } - public string Title => GetDisplayValue(nameof(GridEntry.Title), GridEntry.Title); - public string Authors => GetDisplayValue(nameof(GridEntry.Authors), GridEntry.Authors); - public string Misc => GetDisplayValue(nameof(GridEntry.Misc), GridEntry.Misc); - public string DatePurchased => GetDisplayValue(nameof(GridEntry.Purchase_Date), GridEntry.Purchase_Date); + public string Title => GridEntry.Title; + public string Authors => GridEntry.Authors; + public string Misc => GridEntry.Misc; + public string DatePurchased => GridEntry.PurchaseDate; private bool _remove = false; private Image _cover; @@ -185,7 +185,7 @@ namespace LibationWinForms.Dialogs { GridEntry = gridEntry; - var picDef = new FileManager.PictureDefinition(GridEntry.GetBook().PictureId, FileManager.PictureSize._80x80); + var picDef = new FileManager.PictureDefinition(GridEntry.LibraryBook.Book.PictureId, FileManager.PictureSize._80x80); (bool isDefault, byte[] picture) = FileManager.PictureStorage.GetPicture(picDef); if (isDefault) @@ -196,19 +196,12 @@ namespace LibationWinForms.Dialogs private void PictureStorage_PictureCached(object sender, string pictureId) { - if (pictureId == GridEntry.GetBook().PictureId) + if (pictureId == GridEntry.LibraryBook.Book.PictureId) { Cover = WindowsDesktopUtilities.WinAudibleImageServer.GetImage(pictureId, FileManager.PictureSize._80x80); FileManager.PictureStorage.PictureCached -= PictureStorage_PictureCached; } } - - private string GetDisplayValue(string propertyName, string defaultValue) - { - if (GridEntry.TryDisplayValue(propertyName, out string value)) - return value; - return defaultValue; - } private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); diff --git a/LibationWinForms/EditTagsDataGridViewImageButtonColumn.cs b/LibationWinForms/EditTagsDataGridViewImageButtonColumn.cs new file mode 100644 index 00000000..fdee638f --- /dev/null +++ b/LibationWinForms/EditTagsDataGridViewImageButtonColumn.cs @@ -0,0 +1,26 @@ +using System.Drawing; +using System.Windows.Forms; + +namespace LibationWinForms +{ + public class EditTagsDataGridViewImageButtonColumn : DataGridViewImageButtonColumn + { + protected override DataGridViewImageButtonCell NewCell() + => new EditTagsDataGridViewImageButtonCell(); + } + + internal class EditTagsDataGridViewImageButtonCell : DataGridViewImageButtonCell + { + private static readonly Bitmap ButtonImage = Properties.Resources.edit_tags_25x25; + protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts) + { + if (((string)value).Length == 0) + { + base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, null, null, null, cellStyle, advancedBorderStyle, paintParts); + DrawImage(graphics, ButtonImage, cellBounds); + } + else + base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts); + } + } +} diff --git a/LibationWinForms/GridEntry.cs b/LibationWinForms/GridEntry.cs index 5f24c447..d20c6553 100644 --- a/LibationWinForms/GridEntry.cs +++ b/LibationWinForms/GridEntry.cs @@ -1,86 +1,159 @@ using System; +using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Linq; +using System.Runtime.CompilerServices; using ApplicationServices; using DataLayer; +using Dinah.Core.Drawing; namespace LibationWinForms { - internal class GridEntry + internal class GridEntry : INotifyPropertyChanged, IObjectMemberComparable { - private LibraryBook libraryBook { get; } - private Book book => libraryBook.Book; + public const string LIBERATE_COLUMN_NAME = "Liberate"; + public const string EDIT_TAGS_COLUMN_NAME = "DisplayTags"; - public Book GetBook() => book; - public LibraryBook GetLibraryBook() => libraryBook; - - public GridEntry(LibraryBook libraryBook) => this.libraryBook = libraryBook; - - // hide from public fields from Data Source GUI with [Browsable(false)] + #region implementation properties + // hide from public fields from Data Source GUI with [Browsable(false)] + [Browsable(false)] + public string AudibleProductId => Book.AudibleProductId; [Browsable(false)] - public string AudibleProductId => book.AudibleProductId; + public string Tags => Book.UserDefinedItem.Tags; [Browsable(false)] - public string Tags => book.UserDefinedItem.Tags; + public IEnumerable TagsEnumerated => Book.UserDefinedItem.TagsEnumerated; [Browsable(false)] - public IEnumerable TagsEnumerated => book.UserDefinedItem.TagsEnumerated; - [Browsable(false)] - public string PictureId => book.PictureId; - [Browsable(false)] - public LiberatedState Liberated_Status => LibraryCommands.Liberated_Status(book); - [Browsable(false)] - public PdfState Pdf_Status => LibraryCommands.Pdf_Status(book); + public LibraryBook LibraryBook { get; } - // displayValues is what gets displayed - // the value that gets returned from the property is the cell's value - // this allows for the value to be sorted one way and displayed another - // eg: - // orig title: The Computer - // formatReplacement: The Computer - // value for sorting: Computer - private Dictionary displayValues { get; } = new Dictionary(); - public bool TryDisplayValue(string key, out string value) => displayValues.TryGetValue(key, out value); + #endregion - public Image Cover => - WindowsDesktopUtilities.WinAudibleImageServer.GetImage(book.PictureId, FileManager.PictureSize._80x80); + public event PropertyChangedEventHandler PropertyChanged; + private Book Book => LibraryBook.Book; + private Image _cover; - public string Title + public GridEntry(LibraryBook libraryBook) { - get + LibraryBook = libraryBook; + + _compareValues = CreatePropertyValueDictionary(); + + //Get cover art. If it's default, subscribe to PictureCached + var picDef = new FileManager.PictureDefinition(Book.PictureId, FileManager.PictureSize._80x80); + (bool isDefault, byte[] picture) = FileManager.PictureStorage.GetPicture(picDef); + + if (isDefault) + FileManager.PictureStorage.PictureCached += PictureStorage_PictureCached; + + //Mutable property. Set the field so PropertyChanged doesn't fire. + _cover = ImageReader.ToImage(picture); + + //Immutable properties { - displayValues[nameof(Title)] = book.Title; - return getSortName(book.Title); + Title = Book.Title; + Series = Book.SeriesNames; + Length = Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min"; + MyRating = GetStarString(Book.UserDefinedItem.Rating); + PurchaseDate = libraryBook.DateAdded.ToString("d"); + ProductRating = GetStarString(Book.Rating); + Authors = Book.AuthorNames; + Narrators = Book.NarratorNames; + Category = string.Join(" > ", Book.CategoriesNames); + Misc = GetMiscDisplay(libraryBook); + Description = GetDescriptionDisplay(Book); + } + + //DisplayTags and Liberate are live. + } + + private void PictureStorage_PictureCached(object sender, string pictureId) + { + if (pictureId == Book.PictureId) + { + Cover = WindowsDesktopUtilities.WinAudibleImageServer.GetImage(pictureId, FileManager.PictureSize._80x80); + FileManager.PictureStorage.PictureCached -= PictureStorage_PictureCached; } } - public string Authors => book.AuthorNames; - public string Narrators => book.NarratorNames; + private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") => + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - public int Length + #region Data Source properties + public Image Cover { get { - displayValues[nameof(Length)] - = book.LengthInMinutes == 0 - ? "" - : $"{book.LengthInMinutes / 60} hr {book.LengthInMinutes % 60} min"; - - return book.LengthInMinutes; + return _cover; } - } - - public string Series - { - get + private set { - displayValues[nameof(Series)] = book.SeriesNames; - return getSortName(book.SeriesNames); + _cover = value; + NotifyPropertyChanged(); } } - private static string[] sortPrefixIgnores { get; } = new[] { "the", "a", "an" }; + public string ProductRating { get; } + public string PurchaseDate { get; } + public string MyRating { get; } + public string Series { get; } + public string Title { get; } + public string Length { get; } + public string Authors { get; } + public string Narrators { get; } + public string Category { get; } + public string Misc { get; } + public string Description { get; } + + public string DisplayTags => string.Join("\r\n", TagsEnumerated); + public (LiberatedState, PdfState) Liberate => (LibraryCommands.Liberated_Status(Book), LibraryCommands.Pdf_Status(Book)); + + #endregion + + #region Data Sorting + + private Dictionary> _compareValues { get; } + private static Dictionary _objectComparers; + + public object GetMemberValue(string propertyName) => _compareValues[propertyName](); + public IComparer GetComparer(Type propertyType) => _objectComparers[propertyType]; + + /// + /// Instantiate comparers for every type needed to sort columns. + /// + static GridEntry() + { + _objectComparers = new Dictionary() + { + { typeof(string), new ObjectComparer() }, + { typeof(int), new ObjectComparer() }, + { typeof(float), new ObjectComparer() }, + { typeof(DateTime), new ObjectComparer() }, + { typeof(LiberatedState), new ObjectComparer() }, + }; + } + /// + /// Create getters for all member values by name + /// + Dictionary> CreatePropertyValueDictionary() => new() + { + { nameof(Title), () => getSortName(Book.Title)}, + { nameof(Series),() => getSortName(Book.SeriesNames)}, + { nameof(Length), () => Book.LengthInMinutes}, + { nameof(MyRating), () => Book.UserDefinedItem.Rating.FirstScore}, + { nameof(PurchaseDate), () => LibraryBook.DateAdded}, + { nameof(ProductRating), () => Book.Rating.FirstScore}, + { nameof(Authors), () => Authors}, + { nameof(Narrators), () => Narrators}, + { nameof(Description), () => Description}, + { nameof(Category), () => Category}, + { nameof(Misc), () => Misc}, + { nameof(DisplayTags), () => DisplayTags}, + { nameof(Liberate), () => Liberate.Item1} + }; + + private static readonly string[] sortPrefixIgnores = { "the", "a", "an" }; private static string getSortName(string unformattedName) { var sortName = unformattedName @@ -95,104 +168,115 @@ namespace LibationWinForms return sortName; } - private string descriptionCache = null; - public string Description + #endregion + + #region Static library display functions + + public static (string mouseoverText, Bitmap buttonImage) GetLiberateDisplay(LiberatedState liberatedStatus, PdfState pdfStatus) { - get + string text; + Bitmap image; + + // get mouseover text { - // HtmlAgilityPack is expensive. cache results - if (descriptionCache is null) + var libState = liberatedStatus switch { - if (book.Description is null) - descriptionCache = ""; - else - { - var doc = new HtmlAgilityPack.HtmlDocument(); - doc.LoadHtml(book.Description); - var noHtml = doc.DocumentNode.InnerText; - descriptionCache - = noHtml.Length < 63 - ? noHtml - : noHtml.Substring(0, 60) + "..."; - } - } + LiberatedState.Liberated => "Liberated", + LiberatedState.PartialDownload => "File has been at least\r\npartially downloaded", + LiberatedState.NotDownloaded => "Book NOT downloaded", + _ => throw new Exception("Unexpected liberation state") + }; + + var pdfState = pdfStatus switch + { + PdfState.Downloaded => "\r\nPDF downloaded", + PdfState.NotDownloaded => "\r\nPDF NOT downloaded", + PdfState.NoPdf => "", + _ => throw new Exception("Unexpected PDF state") + }; + + text = libState + pdfState; + + if (liberatedStatus == LiberatedState.NotDownloaded || + liberatedStatus == LiberatedState.PartialDownload || + pdfStatus == PdfState.NotDownloaded) + text += "\r\nClick to complete"; - return descriptionCache; } - } - public string Category => string.Join(" > ", book.CategoriesNames); - - // star ratings retain numeric value but display star text. this is needed because just using star text doesn't sort correctly: - // - star - // - star star - // - star 1/2 - - public string Product_Rating - { - get + // get image { - displayValues[nameof(Product_Rating)] = starString(book.Rating); - return firstScore(book.Rating); + var image_lib + = liberatedStatus == LiberatedState.NotDownloaded ? "red" + : liberatedStatus == LiberatedState.PartialDownload ? "yellow" + : liberatedStatus == LiberatedState.Liberated ? "green" + : throw new Exception("Unexpected liberation state"); + var image_pdf + = pdfStatus == PdfState.NoPdf ? "" + : pdfStatus == PdfState.NotDownloaded ? "_pdf_no" + : pdfStatus == PdfState.Downloaded ? "_pdf_yes" + : throw new Exception("Unexpected PDF state"); + + image = (Bitmap)Properties.Resources.ResourceManager.GetObject($"liberate_{image_lib}{image_pdf}"); } + + return (text, image); } - public string Purchase_Date + /// + /// This information should not change during lifetime, so call only once. + /// + private static string GetDescriptionDisplay(Book book) + { + var doc = new HtmlAgilityPack.HtmlDocument(); + doc.LoadHtml(book.Description); + var noHtml = doc.DocumentNode.InnerText; + return + noHtml.Length < 63? + noHtml : + noHtml.Substring(0, 60) + "..."; + } + + /// + /// This information should not change during lifetime, so call only once. + /// + private static string GetMiscDisplay(LibraryBook libraryBook) { - get - { - displayValues[nameof(Purchase_Date)] = libraryBook.DateAdded.ToString("d"); - return libraryBook.DateAdded.ToString("yyyy-MM-dd HH:mm:ss"); - } + // max 5 text rows + + var details = new List(); + + var locale + = string.IsNullOrWhiteSpace(libraryBook.Book.Locale) + ? "[unknown]" + : libraryBook.Book.Locale; + var acct + = string.IsNullOrWhiteSpace(libraryBook.Account) + ? "[unknown]" + : libraryBook.Account; + details.Add($"Account: {locale} - {acct}"); + + if (libraryBook.Book.HasPdf) + details.Add("Has PDF"); + if (libraryBook.Book.IsAbridged) + details.Add("Abridged"); + if (libraryBook.Book.DatePublished.HasValue) + details.Add($"Date pub'd: {libraryBook.Book.DatePublished.Value:MM/dd/yyyy}"); + // this goes last since it's most likely to have a line-break + if (!string.IsNullOrWhiteSpace(libraryBook.Book.Publisher)) + details.Add($"Pub: {libraryBook.Book.Publisher.Trim()}"); + + if (!details.Any()) + return "[details not imported]"; + + return string.Join("\r\n", details); } - public string My_Rating - { - get - { - displayValues[nameof(My_Rating)] = starString(book.UserDefinedItem.Rating); - return firstScore(book.UserDefinedItem.Rating); - } - } + private static string GetStarString(Rating rating) + => (rating?.FirstScore != null && rating?.FirstScore > 0f) + ? rating?.ToStarString() + : ""; - private string starString(Rating rating) - => (rating?.FirstScore != null && rating?.FirstScore > 0f) - ? rating?.ToStarString() - : ""; - private string firstScore(Rating rating) => rating?.FirstScore.ToString("0.0"); - - // max 5 text rows - public string Misc - { - get - { - var details = new List(); - - var locale - = string.IsNullOrWhiteSpace(book.Locale) - ? "[unknown]" - : book.Locale; - var acct - = string.IsNullOrWhiteSpace(libraryBook.Account) - ? "[unknown]" - : libraryBook.Account; - details.Add($"Account: {locale} - {acct}"); - - if (book.HasPdf) - details.Add("Has PDF"); - if (book.IsAbridged) - details.Add("Abridged"); - if (book.DatePublished.HasValue) - details.Add($"Date pub'd: {book.DatePublished.Value:MM/dd/yyyy}"); - // this goes last since it's most likely to have a line-break - if (!string.IsNullOrWhiteSpace(book.Publisher)) - details.Add($"Pub: {book.Publisher.Trim()}"); - - if (!details.Any()) - return "[details not imported]"; - - return string.Join("\r\n", details); - } - } - } + #endregion + } } diff --git a/LibationWinForms/IObjectMemberComparable.cs b/LibationWinForms/IObjectMemberComparable.cs new file mode 100644 index 00000000..8bc5b762 --- /dev/null +++ b/LibationWinForms/IObjectMemberComparable.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections; + +namespace LibationWinForms +{ + interface IObjectMemberComparable + { + IComparer GetComparer(Type propertyType); + object GetMemberValue(string valueName); + } +} diff --git a/LibationWinForms/LiberateDataGridViewImageButtonColumn.cs b/LibationWinForms/LiberateDataGridViewImageButtonColumn.cs new file mode 100644 index 00000000..63d64257 --- /dev/null +++ b/LibationWinForms/LiberateDataGridViewImageButtonColumn.cs @@ -0,0 +1,30 @@ +using ApplicationServices; +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace LibationWinForms +{ + public class LiberateDataGridViewImageButtonColumn : DataGridViewImageButtonColumn + { + protected override DataGridViewImageButtonCell NewCell() + => new LiberateDataGridViewImageButtonCell(); + } + + internal class LiberateDataGridViewImageButtonCell : DataGridViewImageButtonCell + { + protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts) + { + base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, null, null, null, cellStyle, advancedBorderStyle, paintParts); + + if (value is (LiberatedState liberatedState, PdfState pdfState)) + { + (string mouseoverText, Bitmap buttonImage) = GridEntry.GetLiberateDisplay(liberatedState, pdfState); + + DrawImage(graphics, buttonImage, cellBounds); + + ToolTipText = mouseoverText; + } + } + } +} diff --git a/LibationWinForms/ObjectComparer[T].cs b/LibationWinForms/ObjectComparer[T].cs new file mode 100644 index 00000000..2bc69d8d --- /dev/null +++ b/LibationWinForms/ObjectComparer[T].cs @@ -0,0 +1,10 @@ +using System; +using System.Collections; + +namespace LibationWinForms +{ + class ObjectComparer : IComparer where T : IComparable + { + public int Compare(object x, object y) => ((T)x).CompareTo((T)y); + } +} diff --git a/LibationWinForms/ObjectMemberComparer[T].cs b/LibationWinForms/ObjectMemberComparer[T].cs new file mode 100644 index 00000000..00f29510 --- /dev/null +++ b/LibationWinForms/ObjectMemberComparer[T].cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.ComponentModel; + +namespace LibationWinForms +{ + class ObjectMemberComparer : IComparer where T : IObjectMemberComparable + { + public ListSortDirection Direction { get; set; } = ListSortDirection.Ascending; + public string PropertyName { get; set; } + + public int Compare(T x, T y) + { + var val1 = x.GetMemberValue(PropertyName); + var val2 = y.GetMemberValue(PropertyName); + + return DirMult * x.GetComparer(val1.GetType()).Compare(val1, val2); + } + + private int DirMult => Direction == ListSortDirection.Descending ? -1 : 1; + } +} diff --git a/LibationWinForms/ProductsGrid.Designer.cs b/LibationWinForms/ProductsGrid.Designer.cs index a81c8abf..0a74a123 100644 --- a/LibationWinForms/ProductsGrid.Designer.cs +++ b/LibationWinForms/ProductsGrid.Designer.cs @@ -28,146 +28,198 @@ /// private void InitializeComponent() { - this.components = new System.ComponentModel.Container(); - this.gridEntryBindingSource = new System.Windows.Forms.BindingSource(this.components); - this.gridEntryDataGridView = new System.Windows.Forms.DataGridView(); - this.dataGridViewImageColumn1 = new System.Windows.Forms.DataGridViewImageColumn(); - this.dataGridViewTextBoxColumn1 = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.dataGridViewTextBoxColumn2 = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.dataGridViewTextBoxColumn3 = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.dataGridViewTextBoxColumn4 = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.dataGridViewTextBoxColumn5 = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.dataGridViewTextBoxColumn6 = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.dataGridViewTextBoxColumn7 = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.dataGridViewTextBoxColumn8 = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.dataGridViewTextBoxColumn9 = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.dataGridViewTextBoxColumn10 = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.dataGridViewTextBoxColumn11 = new System.Windows.Forms.DataGridViewTextBoxColumn(); - ((System.ComponentModel.ISupportInitialize)(this.gridEntryBindingSource)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.gridEntryDataGridView)).BeginInit(); - this.SuspendLayout(); - // - // gridEntryBindingSource - // - this.gridEntryBindingSource.DataSource = typeof(LibationWinForms.GridEntry); - // - // gridEntryDataGridView - // - this.gridEntryDataGridView.AutoGenerateColumns = false; - this.gridEntryDataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; - this.gridEntryDataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { - this.dataGridViewImageColumn1, - this.dataGridViewTextBoxColumn1, - this.dataGridViewTextBoxColumn2, - this.dataGridViewTextBoxColumn3, - this.dataGridViewTextBoxColumn4, - this.dataGridViewTextBoxColumn5, - this.dataGridViewTextBoxColumn6, - this.dataGridViewTextBoxColumn7, - this.dataGridViewTextBoxColumn8, - this.dataGridViewTextBoxColumn9, - this.dataGridViewTextBoxColumn10, - this.dataGridViewTextBoxColumn11}); - this.gridEntryDataGridView.DataSource = this.gridEntryBindingSource; - this.gridEntryDataGridView.Location = new System.Drawing.Point(54, 58); - this.gridEntryDataGridView.Name = "gridEntryDataGridView"; - this.gridEntryDataGridView.Size = new System.Drawing.Size(300, 220); - this.gridEntryDataGridView.TabIndex = 0; - // - // dataGridViewImageColumn1 - // - this.dataGridViewImageColumn1.DataPropertyName = "Cover"; - this.dataGridViewImageColumn1.HeaderText = "Cover"; - this.dataGridViewImageColumn1.Name = "dataGridViewImageColumn1"; - this.dataGridViewImageColumn1.ReadOnly = true; - // - // dataGridViewTextBoxColumn1 - // - this.dataGridViewTextBoxColumn1.DataPropertyName = "Title"; - this.dataGridViewTextBoxColumn1.HeaderText = "Title"; - this.dataGridViewTextBoxColumn1.Name = "dataGridViewTextBoxColumn1"; - this.dataGridViewTextBoxColumn1.ReadOnly = true; - // - // dataGridViewTextBoxColumn2 - // - this.dataGridViewTextBoxColumn2.DataPropertyName = "Authors"; - this.dataGridViewTextBoxColumn2.HeaderText = "Authors"; - this.dataGridViewTextBoxColumn2.Name = "dataGridViewTextBoxColumn2"; - this.dataGridViewTextBoxColumn2.ReadOnly = true; - // - // dataGridViewTextBoxColumn3 - // - this.dataGridViewTextBoxColumn3.DataPropertyName = "Narrators"; - this.dataGridViewTextBoxColumn3.HeaderText = "Narrators"; - this.dataGridViewTextBoxColumn3.Name = "dataGridViewTextBoxColumn3"; - this.dataGridViewTextBoxColumn3.ReadOnly = true; - // - // dataGridViewTextBoxColumn4 - // - this.dataGridViewTextBoxColumn4.DataPropertyName = "Length"; - this.dataGridViewTextBoxColumn4.HeaderText = "Length"; - this.dataGridViewTextBoxColumn4.Name = "dataGridViewTextBoxColumn4"; - this.dataGridViewTextBoxColumn4.ReadOnly = true; - // - // dataGridViewTextBoxColumn5 - // - this.dataGridViewTextBoxColumn5.DataPropertyName = "Series"; - this.dataGridViewTextBoxColumn5.HeaderText = "Series"; - this.dataGridViewTextBoxColumn5.Name = "dataGridViewTextBoxColumn5"; - this.dataGridViewTextBoxColumn5.ReadOnly = true; - // - // dataGridViewTextBoxColumn6 - // - this.dataGridViewTextBoxColumn6.DataPropertyName = "Description"; - this.dataGridViewTextBoxColumn6.HeaderText = "Description"; - this.dataGridViewTextBoxColumn6.Name = "dataGridViewTextBoxColumn6"; - this.dataGridViewTextBoxColumn6.ReadOnly = true; - // - // dataGridViewTextBoxColumn7 - // - this.dataGridViewTextBoxColumn7.DataPropertyName = "Category"; - this.dataGridViewTextBoxColumn7.HeaderText = "Category"; - this.dataGridViewTextBoxColumn7.Name = "dataGridViewTextBoxColumn7"; - this.dataGridViewTextBoxColumn7.ReadOnly = true; - // - // dataGridViewTextBoxColumn8 - // - this.dataGridViewTextBoxColumn8.DataPropertyName = "Product_Rating"; - this.dataGridViewTextBoxColumn8.HeaderText = "Product_Rating"; - this.dataGridViewTextBoxColumn8.Name = "dataGridViewTextBoxColumn8"; - this.dataGridViewTextBoxColumn8.ReadOnly = true; - // - // dataGridViewTextBoxColumn9 - // - this.dataGridViewTextBoxColumn9.DataPropertyName = "Purchase_Date"; - this.dataGridViewTextBoxColumn9.HeaderText = "Purchase_Date"; - this.dataGridViewTextBoxColumn9.Name = "dataGridViewTextBoxColumn9"; - this.dataGridViewTextBoxColumn9.ReadOnly = true; - // - // dataGridViewTextBoxColumn10 - // - this.dataGridViewTextBoxColumn10.DataPropertyName = "My_Rating"; - this.dataGridViewTextBoxColumn10.HeaderText = "My_Rating"; - this.dataGridViewTextBoxColumn10.Name = "dataGridViewTextBoxColumn10"; - this.dataGridViewTextBoxColumn10.ReadOnly = true; - // - // dataGridViewTextBoxColumn11 - // - this.dataGridViewTextBoxColumn11.DataPropertyName = "Misc"; - this.dataGridViewTextBoxColumn11.HeaderText = "Misc"; - this.dataGridViewTextBoxColumn11.Name = "dataGridViewTextBoxColumn11"; - this.dataGridViewTextBoxColumn11.ReadOnly = true; - // - // ProductsGrid - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.Controls.Add(this.gridEntryDataGridView); - this.Name = "ProductsGrid"; - this.Size = new System.Drawing.Size(434, 329); - ((System.ComponentModel.ISupportInitialize)(this.gridEntryBindingSource)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.gridEntryDataGridView)).EndInit(); - this.ResumeLayout(false); + this.components = new System.ComponentModel.Container(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); + this.gridEntryBindingSource = new System.Windows.Forms.BindingSource(this.components); + this.gridEntryDataGridView = new System.Windows.Forms.DataGridView(); + this.dataGridViewImageButtonBoxColumn1 = new LibationWinForms.LiberateDataGridViewImageButtonColumn(); + this.dataGridViewImageColumn1 = new System.Windows.Forms.DataGridViewImageColumn(); + this.dataGridViewTextBoxColumn1 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.dataGridViewTextBoxColumn2 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.dataGridViewTextBoxColumn3 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.dataGridViewTextBoxColumn4 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.dataGridViewTextBoxColumn5 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.dataGridViewTextBoxColumn6 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.dataGridViewTextBoxColumn7 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.ProductRating = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.PurchaseDate = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.MyRating = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.dataGridViewTextBoxColumn11 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.dataGridViewImageButtonBoxColumn2 = new LibationWinForms.EditTagsDataGridViewImageButtonColumn(); + ((System.ComponentModel.ISupportInitialize)(this.gridEntryBindingSource)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.gridEntryDataGridView)).BeginInit(); + this.SuspendLayout(); + // + // gridEntryBindingSource + // + this.gridEntryBindingSource.DataSource = typeof(LibationWinForms.GridEntry); + // + // gridEntryDataGridView + // + this.gridEntryDataGridView.AllowUserToAddRows = false; + this.gridEntryDataGridView.AllowUserToDeleteRows = false; + this.gridEntryDataGridView.AutoGenerateColumns = false; + this.gridEntryDataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.gridEntryDataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.dataGridViewImageButtonBoxColumn1, + this.dataGridViewImageColumn1, + this.dataGridViewTextBoxColumn1, + this.dataGridViewTextBoxColumn2, + this.dataGridViewTextBoxColumn3, + this.dataGridViewTextBoxColumn4, + this.dataGridViewTextBoxColumn5, + this.dataGridViewTextBoxColumn6, + this.dataGridViewTextBoxColumn7, + this.ProductRating, + this.PurchaseDate, + this.MyRating, + this.dataGridViewTextBoxColumn11, + this.dataGridViewImageButtonBoxColumn2}); + this.gridEntryDataGridView.DataSource = this.gridEntryBindingSource; + dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Window; + dataGridViewCellStyle1.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.ControlText; + dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight; + dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText; + dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True; + this.gridEntryDataGridView.DefaultCellStyle = dataGridViewCellStyle1; + this.gridEntryDataGridView.Dock = System.Windows.Forms.DockStyle.Fill; + this.gridEntryDataGridView.Location = new System.Drawing.Point(0, 0); + this.gridEntryDataGridView.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + this.gridEntryDataGridView.Name = "gridEntryDataGridView"; + this.gridEntryDataGridView.ReadOnly = true; + this.gridEntryDataGridView.RowHeadersVisible = false; + this.gridEntryDataGridView.RowTemplate.Height = 82; + this.gridEntryDataGridView.Size = new System.Drawing.Size(1503, 380); + this.gridEntryDataGridView.TabIndex = 0; + // + // dataGridViewImageButtonBoxColumn1 + // + this.dataGridViewImageButtonBoxColumn1.DataPropertyName = "Liberate"; + this.dataGridViewImageButtonBoxColumn1.HeaderText = "Liberate"; + this.dataGridViewImageButtonBoxColumn1.Name = "dataGridViewImageButtonBoxColumn1"; + this.dataGridViewImageButtonBoxColumn1.ReadOnly = true; + this.dataGridViewImageButtonBoxColumn1.Resizable = System.Windows.Forms.DataGridViewTriState.False; + this.dataGridViewImageButtonBoxColumn1.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic; + this.dataGridViewImageButtonBoxColumn1.Width = 70; + // + // dataGridViewImageColumn1 + // + this.dataGridViewImageColumn1.DataPropertyName = "Cover"; + this.dataGridViewImageColumn1.HeaderText = "Cover"; + this.dataGridViewImageColumn1.Name = "dataGridViewImageColumn1"; + this.dataGridViewImageColumn1.ReadOnly = true; + this.dataGridViewImageColumn1.Resizable = System.Windows.Forms.DataGridViewTriState.False; + this.dataGridViewImageColumn1.ToolTipText = "Cover Art"; + this.dataGridViewImageColumn1.Width = 80; + // + // dataGridViewTextBoxColumn1 + // + this.dataGridViewTextBoxColumn1.DataPropertyName = "Title"; + this.dataGridViewTextBoxColumn1.HeaderText = "Title"; + this.dataGridViewTextBoxColumn1.Name = "dataGridViewTextBoxColumn1"; + this.dataGridViewTextBoxColumn1.ReadOnly = true; + this.dataGridViewTextBoxColumn1.ToolTipText = "Book Title"; + this.dataGridViewTextBoxColumn1.Width = 200; + // + // dataGridViewTextBoxColumn2 + // + this.dataGridViewTextBoxColumn2.DataPropertyName = "Authors"; + this.dataGridViewTextBoxColumn2.HeaderText = "Authors"; + this.dataGridViewTextBoxColumn2.Name = "dataGridViewTextBoxColumn2"; + this.dataGridViewTextBoxColumn2.ReadOnly = true; + this.dataGridViewTextBoxColumn2.ToolTipText = "Book Author(s)"; + // + // dataGridViewTextBoxColumn3 + // + this.dataGridViewTextBoxColumn3.DataPropertyName = "Narrators"; + this.dataGridViewTextBoxColumn3.HeaderText = "Narrators"; + this.dataGridViewTextBoxColumn3.Name = "dataGridViewTextBoxColumn3"; + this.dataGridViewTextBoxColumn3.ReadOnly = true; + this.dataGridViewTextBoxColumn3.ToolTipText = "Audiobook Narrator(s)"; + // + // dataGridViewTextBoxColumn4 + // + this.dataGridViewTextBoxColumn4.DataPropertyName = "Length"; + this.dataGridViewTextBoxColumn4.HeaderText = "Length"; + this.dataGridViewTextBoxColumn4.Name = "dataGridViewTextBoxColumn4"; + this.dataGridViewTextBoxColumn4.ReadOnly = true; + this.dataGridViewTextBoxColumn4.ToolTipText = "Recording Length"; + // + // dataGridViewTextBoxColumn5 + // + this.dataGridViewTextBoxColumn5.DataPropertyName = "Series"; + this.dataGridViewTextBoxColumn5.HeaderText = "Series"; + this.dataGridViewTextBoxColumn5.Name = "dataGridViewTextBoxColumn5"; + this.dataGridViewTextBoxColumn5.ReadOnly = true; + this.dataGridViewTextBoxColumn5.ToolTipText = "Book Series"; + // + // dataGridViewTextBoxColumn6 + // + this.dataGridViewTextBoxColumn6.DataPropertyName = "Description"; + this.dataGridViewTextBoxColumn6.HeaderText = "Description"; + this.dataGridViewTextBoxColumn6.Name = "dataGridViewTextBoxColumn6"; + this.dataGridViewTextBoxColumn6.ReadOnly = true; + // + // dataGridViewTextBoxColumn7 + // + this.dataGridViewTextBoxColumn7.DataPropertyName = "Category"; + this.dataGridViewTextBoxColumn7.HeaderText = "Category"; + this.dataGridViewTextBoxColumn7.Name = "dataGridViewTextBoxColumn7"; + this.dataGridViewTextBoxColumn7.ReadOnly = true; + // + // ProductRating + // + this.ProductRating.DataPropertyName = "ProductRating"; + this.ProductRating.HeaderText = "Product Rating"; + this.ProductRating.Name = "ProductRating"; + this.ProductRating.ReadOnly = true; + this.ProductRating.Width = 108; + // + // PurchaseDate + // + this.PurchaseDate.DataPropertyName = "PurchaseDate"; + this.PurchaseDate.HeaderText = "Purchase Date"; + this.PurchaseDate.Name = "PurchaseDate"; + this.PurchaseDate.ReadOnly = true; + // + // MyRating + // + this.MyRating.DataPropertyName = "MyRating"; + this.MyRating.HeaderText = "My Rating"; + this.MyRating.Name = "MyRating"; + this.MyRating.ReadOnly = true; + this.MyRating.Width = 108; + // + // dataGridViewTextBoxColumn11 + // + this.dataGridViewTextBoxColumn11.DataPropertyName = "Misc"; + this.dataGridViewTextBoxColumn11.HeaderText = "Misc"; + this.dataGridViewTextBoxColumn11.Name = "dataGridViewTextBoxColumn11"; + this.dataGridViewTextBoxColumn11.ReadOnly = true; + this.dataGridViewTextBoxColumn11.Width = 135; + // + // dataGridViewImageButtonBoxColumn2 + // + this.dataGridViewImageButtonBoxColumn2.DataPropertyName = "DisplayTags"; + this.dataGridViewImageButtonBoxColumn2.HeaderText = "Edit Tags"; + this.dataGridViewImageButtonBoxColumn2.Name = "dataGridViewImageButtonBoxColumn2"; + this.dataGridViewImageButtonBoxColumn2.ReadOnly = true; + this.dataGridViewImageButtonBoxColumn2.Resizable = System.Windows.Forms.DataGridViewTriState.False; + this.dataGridViewImageButtonBoxColumn2.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic; + // + // ProductsGrid + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.gridEntryDataGridView); + this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + this.Name = "ProductsGrid"; + this.Size = new System.Drawing.Size(1503, 380); + ((System.ComponentModel.ISupportInitialize)(this.gridEntryBindingSource)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.gridEntryDataGridView)).EndInit(); + this.ResumeLayout(false); } @@ -175,17 +227,22 @@ private System.Windows.Forms.BindingSource gridEntryBindingSource; private System.Windows.Forms.DataGridView gridEntryDataGridView; - private System.Windows.Forms.DataGridViewImageColumn dataGridViewImageColumn1; - private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn1; - private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn2; - private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn3; - private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn4; - private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn5; - private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn6; - private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn7; - private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn8; - private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn9; - private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn10; - private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn11; - } + private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn8; + private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn9; + private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn10; + private LiberateDataGridViewImageButtonColumn dataGridViewImageButtonBoxColumn1; + private System.Windows.Forms.DataGridViewImageColumn dataGridViewImageColumn1; + private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn1; + private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn2; + private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn3; + private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn4; + private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn5; + private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn6; + private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn7; + private System.Windows.Forms.DataGridViewTextBoxColumn ProductRating; + private System.Windows.Forms.DataGridViewTextBoxColumn PurchaseDate; + private System.Windows.Forms.DataGridViewTextBoxColumn MyRating; + private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn11; + private EditTagsDataGridViewImageButtonColumn dataGridViewImageButtonBoxColumn2; + } } diff --git a/LibationWinForms/ProductsGrid.cs b/LibationWinForms/ProductsGrid.cs index 5f811bd1..6cea7acc 100644 --- a/LibationWinForms/ProductsGrid.cs +++ b/LibationWinForms/ProductsGrid.cs @@ -1,12 +1,11 @@ using System; using System.Drawing; using System.Linq; +using System.Threading.Tasks; using System.Windows.Forms; using ApplicationServices; using DataLayer; using Dinah.Core; -using Dinah.Core.Collections.Generic; -using Dinah.Core.DataBinding; using Dinah.Core.Windows.Forms; using LibationWinForms.Dialogs; @@ -30,158 +29,54 @@ namespace LibationWinForms public event EventHandler VisibleCountChanged; public event EventHandler BackupCountsChanged; - private const string EDIT_TAGS = "Edit Tags"; - private const string LIBERATE = "Liberate"; - // alias - private DataGridView dataGridView => gridEntryDataGridView; + private DataGridView _dataGridView => gridEntryDataGridView; public ProductsGrid() { InitializeComponent(); - formatDataGridView(); - addLiberateButtons(); - addEditTagsButtons(); - formatColumns(); - - manageLiveImageUpdateSubscriptions(); - - enableDoubleBuffering(); - } - - private void enableDoubleBuffering() - { - var propertyInfo = dataGridView.GetType().GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); - - //var before = (bool)propertyInfo.GetValue(dataGridView); - propertyInfo.SetValue(dataGridView, true, null); - //var after = (bool)propertyInfo.GetValue(dataGridView); - } - - private void formatDataGridView() - { - dataGridView.Dock = DockStyle.Fill; - dataGridView.AllowUserToAddRows = false; - dataGridView.AllowUserToDeleteRows = false; - dataGridView.AutoGenerateColumns = false; - dataGridView.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize; - dataGridView.DefaultCellStyle.WrapMode = DataGridViewTriState.True; - dataGridView.ReadOnly = true; - dataGridView.RowHeadersVisible = false; - - // adjust height for 80x80 pictures. - // this must be done before databinding. or can alter later by iterating through rows - dataGridView.RowTemplate.Height = 82; - dataGridView.CellFormatting += replaceFormatted; - dataGridView.CellFormatting += hiddenFormatting; // sorting breaks filters. must reapply filters after sorting - dataGridView.Sorted += (_, __) => filter(); - } + _dataGridView.Sorted += (_, __) => Filter(); + _dataGridView.CellFormatting += HiddenFormatting; + _dataGridView.CellContentClick += DataGridView_CellContentClick; - #region format text cells. ie: not buttons - private void replaceFormatted(object sender, DataGridViewCellFormattingEventArgs e) + EnableDoubleBuffering(); + } + + private void EnableDoubleBuffering() + { + var propertyInfo = _dataGridView.GetType().GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); + + //var before = (bool)propertyInfo.GetValue(dataGridView); + propertyInfo.SetValue(_dataGridView, true, null); + //var after = (bool)propertyInfo.GetValue(dataGridView); + } + + #region Button controls + + private async void DataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e) { - var col = ((DataGridView)sender).Columns[e.ColumnIndex]; - if (col is DataGridViewTextBoxColumn textCol && getGridEntry(e.RowIndex).TryDisplayValue(textCol.Name, out string value)) + // handle grid button click: https://stackoverflow.com/a/13687844 + if (e.RowIndex < 0 || _dataGridView.Columns[e.ColumnIndex] is not DataGridViewButtonColumn) + return; + + var liveGridEntry = getGridEntry(e.RowIndex); + + switch (_dataGridView.Columns[e.ColumnIndex].DataPropertyName) { - // DO NOT DO THIS: getCell(e).Value = value; - // it's the wrong way and will infinitely call CellFormatting on each assign - - // this is the correct way. will actually set FormattedValue (and EditedFormattedValue) while leaving Value as-is for sorting - e.Value = value; - - getCell(e).ToolTipText = value; + case GridEntry.LIBERATE_COLUMN_NAME: + await Liberate_Click(liveGridEntry); + break; + case GridEntry.EDIT_TAGS_COLUMN_NAME: + EditTags_Click(liveGridEntry); + break; } } - private void hiddenFormatting(object sender, DataGridViewCellFormattingEventArgs e) + private async Task Liberate_Click(GridEntry liveGridEntry) { - var dgv = (DataGridView)sender; - // no action needed for buttons - if (e.RowIndex < 0 || dgv.Columns[e.ColumnIndex] is DataGridViewButtonColumn) - return; - - var isHidden = getGridEntry(e.RowIndex).TagsEnumerated.Contains("hidden"); - - getCell(e).Style - = isHidden - ? new DataGridViewCellStyle { ForeColor = Color.LightGray } - : dgv.DefaultCellStyle; - } - #endregion - - #region liberation buttons - private void addLiberateButtons() - { - dataGridView.Columns.Insert(0, new DataGridViewButtonColumn { HeaderText = LIBERATE }); - - dataGridView.CellPainting += liberate_Paint; - dataGridView.CellContentClick += liberate_Click; - } - - private void liberate_Paint(object sender, DataGridViewCellPaintingEventArgs e) - { - if (!isColumnValid(e, LIBERATE)) - return; - - var cell = getCell(e); - var gridEntry = getGridEntry(e.RowIndex); - var liberatedStatus = gridEntry.Liberated_Status; - var pdfStatus = gridEntry.Pdf_Status; - - // mouseover text - { - var libState = liberatedStatus switch - { - LiberatedState.Liberated => "Liberated", - LiberatedState.PartialDownload => "File has been at least\r\npartially downloaded", - LiberatedState.NotDownloaded => "Book NOT downloaded", - _ => throw new Exception("Unexpected liberation state") - }; - - var pdfState = pdfStatus switch - { - PdfState.Downloaded => "\r\nPDF downloaded", - PdfState.NotDownloaded => "\r\nPDF NOT downloaded", - PdfState.NoPdf => "", - _ => throw new Exception("Unexpected PDF state") - }; - - var text = libState + pdfState; - - if (liberatedStatus == LiberatedState.NotDownloaded || - liberatedStatus == LiberatedState.PartialDownload || - pdfStatus == PdfState.NotDownloaded) - text += "\r\nClick to complete"; - - //DEBUG//cell.Value = text; - cell.ToolTipText = text; - } - - // draw img - { - var image_lib - = liberatedStatus == LiberatedState.NotDownloaded ? "red" - : liberatedStatus == LiberatedState.PartialDownload ? "yellow" - : liberatedStatus == LiberatedState.Liberated ? "green" - : throw new Exception("Unexpected liberation state"); - var image_pdf - = pdfStatus == PdfState.NoPdf ? "" - : pdfStatus == PdfState.NotDownloaded ? "_pdf_no" - : pdfStatus == PdfState.Downloaded ? "_pdf_yes" - : throw new Exception("Unexpected PDF state"); - var image = (Bitmap)Properties.Resources.ResourceManager.GetObject($"liberate_{image_lib}{image_pdf}"); - drawImage(e, image); - } - } - - private async void liberate_Click(object sender, DataGridViewCellEventArgs e) - { - if (!isColumnValid(e, LIBERATE)) - return; - - var libraryBook = getGridEntry(e.RowIndex).GetLibraryBook(); + var libraryBook = liveGridEntry.LibraryBook; // liberated: open explorer to file if (TransitionalFileLocator.Audio_Exists(libraryBook.Book)) @@ -195,146 +90,24 @@ namespace LibationWinForms // else: liberate await BookLiberation.ProcessorAutomationController.BackupSingleBookAsync(libraryBook, (_, __) => RefreshRow(libraryBook.Book.AudibleProductId)); } - #endregion - public void RefreshRow(string productId) + private void EditTags_Click(GridEntry liveGridEntry) { - var rowId = getRowId((ge) => ge.AudibleProductId == productId); - - // update cells incl Liberate button text - dataGridView.InvalidateRow(rowId); - - // needed in case filtering by -IsLiberated and it gets changed to Liberated. want to immediately show the change - filter(); - - BackupCountsChanged?.Invoke(this, EventArgs.Empty); - } - - #region tag buttons - private void addEditTagsButtons() - { - dataGridView.Columns.Add(new DataGridViewButtonColumn { HeaderText = EDIT_TAGS }); - - dataGridView.CellPainting += editTags_Paint; - dataGridView.CellContentClick += editTags_Click; - } - - private void editTags_Paint(object sender, DataGridViewCellPaintingEventArgs e) - { - // DataGridView Image for Button Column: https://stackoverflow.com/a/36253883 - - if (!isColumnValid(e, EDIT_TAGS)) - return; - - var cell = getCell(e); - var gridEntry = getGridEntry(e.RowIndex); - - var displayTags = gridEntry.TagsEnumerated.ToList(); - - if (displayTags.Any()) - cell.Value = string.Join("\r\n", displayTags); - else - { - // if removing all tags: clear previous tag text - cell.Value = ""; - drawImage(e, Properties.Resources.edit_tags_25x25); - } - } - - private void editTags_Click(object sender, DataGridViewCellEventArgs e) - { - // handle grid button click: https://stackoverflow.com/a/13687844 - - var dgv = (DataGridView)sender; - - if (!isColumnValid(e, EDIT_TAGS)) - return; - - var liveGridEntry = getGridEntry(e.RowIndex); - - // EditTagsDialog should display better-formatted title - liveGridEntry.TryDisplayValue(nameof(liveGridEntry.Title), out string value); - - var bookDetailsForm = new BookDetailsDialog(value, liveGridEntry.Tags); + var bookDetailsForm = new BookDetailsDialog(liveGridEntry.Title, liveGridEntry.Tags); if (bookDetailsForm.ShowDialog() != DialogResult.OK) return; - var qtyChanges = LibraryCommands.UpdateTags(liveGridEntry.GetBook(), bookDetailsForm.NewTags); - if (qtyChanges == 0) + var qtyChanges = LibraryCommands.UpdateTags(liveGridEntry.LibraryBook.Book, bookDetailsForm.NewTags); + if (qtyChanges == 0) return; - // force a re-draw, and re-apply filters - - // needed to update text colors - dgv.InvalidateRow(e.RowIndex); - - filter(); + //Re-apply filters + Filter(); } + #endregion - private static void drawImage(DataGridViewCellPaintingEventArgs e, Bitmap image) - { - e.Paint(e.CellBounds, DataGridViewPaintParts.All); - - var w = image.Width; - var h = image.Height; - var x = e.CellBounds.Left + (e.CellBounds.Width - w) / 2; - var y = e.CellBounds.Top + (e.CellBounds.Height - h) / 2; - - e.Graphics.DrawImage(image, new Rectangle(x, y, w, h)); - e.Handled = true; - } - - private bool isColumnValid(DataGridViewCellEventArgs e, string colName) => isColumnValid(e.RowIndex, e.ColumnIndex, colName); - private bool isColumnValid(DataGridViewCellPaintingEventArgs e, string colName) => isColumnValid(e.RowIndex, e.ColumnIndex, colName); - private bool isColumnValid(int rowIndex, int colIndex, string colName) - { - var col = dataGridView.Columns[colIndex]; - return rowIndex >= 0 && col.Name == colName && col is DataGridViewButtonColumn; - } - - private void formatColumns() - { - for (var i = dataGridView.ColumnCount - 1; i >= 0; i--) - { - var col = dataGridView.Columns[i]; - - // initial HeaderText is the lookup name from GridEntry class. any formatting below won't change this - col.Name = col.HeaderText; - - if (!(col is DataGridViewImageColumn || col is DataGridViewButtonColumn)) - col.SortMode = DataGridViewColumnSortMode.Automatic; - - col.HeaderText = col.HeaderText.Replace("_", " "); - - col.Width = col.Name switch - { - LIBERATE => 70, - nameof(GridEntry.Cover) => 80, - nameof(GridEntry.Title) => col.Width * 2, - nameof(GridEntry.Misc) => (int)(col.Width * 1.35), - var n when n.In(nameof(GridEntry.My_Rating), nameof(GridEntry.Product_Rating)) => col.Width + 8, - _ => col.Width - }; - } - } - - #region live update newly downloaded and cached images - private void manageLiveImageUpdateSubscriptions() - { - FileManager.PictureStorage.PictureCached += crossThreadImageUpdate; - Disposed += (_, __) => FileManager.PictureStorage.PictureCached -= crossThreadImageUpdate; - } - - private void crossThreadImageUpdate(object _, string pictureId) - => dataGridView.UIThread(() => updateRowImage(pictureId)); - private void updateRowImage(string pictureId) - { - var rowId = getRowId((ge) => ge.PictureId == pictureId); - if (rowId > -1) - dataGridView.InvalidateRow(rowId); - } - #endregion + #region UI display functions private bool hasBeenDisplayed = false; public void Display() @@ -352,15 +125,15 @@ namespace LibationWinForms // if no data. hide all columns. return if (!lib.Any()) { - for (var i = dataGridView.ColumnCount - 1; i >= 0; i--) - dataGridView.Columns.RemoveAt(i); + for (var i = _dataGridView.ColumnCount - 1; i >= 0; i--) + _dataGridView.Columns.RemoveAt(i); return; } var orderedGridEntries = lib .Select(lb => new GridEntry(lb)).ToList() // default load order - .OrderByDescending(ge => ge.Purchase_Date) + .OrderByDescending(ge => ge.PurchaseDate) //// more advanced example: sort by author, then series, then title //.OrderBy(ge => ge.Authors) // .ThenBy(ge => ge.Series) @@ -370,49 +143,83 @@ namespace LibationWinForms // // BIND // - gridEntryBindingSource.DataSource = orderedGridEntries.ToSortableBindingList(); + gridEntryBindingSource.DataSource = new SortableBindingList2(orderedGridEntries); // // FILTER // - filter(); + Filter(); BackupCountsChanged?.Invoke(this, EventArgs.Empty); } + public void RefreshRow(string productId) + { + var rowId = getRowIndex((ge) => ge.AudibleProductId == productId); + + // update cells incl Liberate button text + _dataGridView.InvalidateRow(rowId); + + // needed in case filtering by -IsLiberated and it gets changed to Liberated. want to immediately show the change + Filter(); + + BackupCountsChanged?.Invoke(this, EventArgs.Empty); + } + + #region format text cells. ie: not buttons + + private void HiddenFormatting(object sender, DataGridViewCellFormattingEventArgs e) + { + var dgv = (DataGridView)sender; + // no action needed for buttons + if (e.RowIndex < 0 || dgv.Columns[e.ColumnIndex] is DataGridViewButtonColumn) + return; + + var isHidden = getGridEntry(e.RowIndex).TagsEnumerated.Contains("hidden"); + + getCell(e).Style + = isHidden + ? new DataGridViewCellStyle { ForeColor = Color.LightGray } + : dgv.DefaultCellStyle; + } + + #endregion + + #endregion + #region filter + string _filterSearchString; - private void filter() => Filter(_filterSearchString); + private void Filter() => Filter(_filterSearchString); public void Filter(string searchString) { _filterSearchString = searchString; - if (dataGridView.Rows.Count == 0) + if (_dataGridView.Rows.Count == 0) return; var searchResults = SearchEngineCommands.Search(searchString); var productIds = searchResults.Docs.Select(d => d.ProductId).ToList(); // https://stackoverflow.com/a/18942430 - var currencyManager = (CurrencyManager)BindingContext[dataGridView.DataSource]; + var currencyManager = (CurrencyManager)BindingContext[_dataGridView.DataSource]; currencyManager.SuspendBinding(); { - for (var r = dataGridView.RowCount - 1; r >= 0; r--) - dataGridView.Rows[r].Visible = productIds.Contains(getGridEntry(r).AudibleProductId); + for (var r = _dataGridView.RowCount - 1; r >= 0; r--) + _dataGridView.Rows[r].Visible = productIds.Contains(getGridEntry(r).AudibleProductId); } currencyManager.ResumeBinding(); - VisibleCountChanged?.Invoke(this, dataGridView.AsEnumerable().Count(r => r.Visible)); + VisibleCountChanged?.Invoke(this, _dataGridView.AsEnumerable().Count(r => r.Visible)); } + #endregion - private int getRowId(Func func) => dataGridView.GetRowIdOfBoundItem(func); + #region DataGridView Macros - private GridEntry getGridEntry(int rowIndex) => dataGridView.GetBoundItem(rowIndex); + private int getRowIndex(Func func) => _dataGridView.GetRowIdOfBoundItem(func); + private GridEntry getGridEntry(int rowIndex) => _dataGridView.GetBoundItem(rowIndex); + private DataGridViewCell getCell(DataGridViewCellFormattingEventArgs e) => _dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex]; - private DataGridViewCell getCell(DataGridViewCellFormattingEventArgs e) => getCell(e.RowIndex, e.ColumnIndex); - - private DataGridViewCell getCell(DataGridViewCellPaintingEventArgs e) => getCell(e.RowIndex, e.ColumnIndex); - - private DataGridViewCell getCell(int rowIndex, int columnIndex) => dataGridView.Rows[rowIndex].Cells[columnIndex]; + #endregion } } diff --git a/LibationWinForms/ProductsGrid.resx b/LibationWinForms/ProductsGrid.resx index d1166daf..688d741a 100644 --- a/LibationWinForms/ProductsGrid.resx +++ b/LibationWinForms/ProductsGrid.resx @@ -1,64 +1,4 @@ - - - + @@ -120,4 +60,19 @@ 17, 17 + + True + + + True + + + True + + + True + + + True + \ No newline at end of file diff --git a/LibationWinForms/SortableBindingList2[T].cs b/LibationWinForms/SortableBindingList2[T].cs new file mode 100644 index 00000000..1a0e1d97 --- /dev/null +++ b/LibationWinForms/SortableBindingList2[T].cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LibationWinForms +{ + class SortableBindingList2 : BindingList where T : IObjectMemberComparable + { + private ObjectMemberComparer Comparer = new(); + + private bool isSorted; + private ListSortDirection listSortDirection; + private PropertyDescriptor propertyDescriptor; + + public SortableBindingList2() : base(new List()) { } + + public SortableBindingList2(IEnumerable enumeration) : base(new List(enumeration)) { } + + protected override bool SupportsSortingCore => true; + + protected override bool IsSortedCore => isSorted; + + protected override PropertyDescriptor SortPropertyCore => propertyDescriptor; + + protected override ListSortDirection SortDirectionCore => listSortDirection; + + protected override bool SupportsSearchingCore => true; + + protected override void ApplySortCore(PropertyDescriptor property, ListSortDirection direction) + { + List itemsList = (List)Items; + + Comparer.PropertyName = property.Name; + Comparer.Direction = direction; + + itemsList.Sort(Comparer); + + propertyDescriptor = property; + listSortDirection = direction; + isSorted = true; + + OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1)); + } + + protected override void RemoveSortCore() + { + isSorted = false; + propertyDescriptor = base.SortPropertyCore; + listSortDirection = base.SortDirectionCore; + + OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1)); + } + + protected override int FindCore(PropertyDescriptor property, object key) + { + int count = Count; + for (int i = 0; i < count; ++i) + { + T element = this[i]; + if (property.GetValue(element).Equals(key)) + { + return i; + } + } + + return -1; + } + } +}