Major UI refactoring.

This commit is contained in:
Michael Bucari-Tovo 2021-08-09 15:00:24 -06:00
parent af48641281
commit d48bd5ad07
13 changed files with 769 additions and 653 deletions

View File

@ -13,7 +13,7 @@
<!-- <PublishSingleFile>true</PublishSingleFile> --> <!-- <PublishSingleFile>true</PublishSingleFile> -->
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<Version>5.4.9.0</Version> <Version>5.4.9.79</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -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));
}
}
}

View File

@ -40,7 +40,7 @@ namespace LibationWinForms.Dialogs
var orderedGridEntries = _libraryBooks var orderedGridEntries = _libraryBooks
.Select(lb => new RemovableGridEntry(new GridEntry(lb))) .Select(lb => new RemovableGridEntry(new GridEntry(lb)))
.OrderByDescending(ge => ge.GridEntry.Purchase_Date) .OrderByDescending(ge => ge.GridEntry.PurchaseDate)
.ToList(); .ToList();
_removableGridEntries = orderedGridEntries.ToSortableBindingList(); _removableGridEntries = orderedGridEntries.ToSortableBindingList();
@ -173,10 +173,10 @@ namespace LibationWinForms.Dialogs
NotifyPropertyChanged(); NotifyPropertyChanged();
} }
} }
public string Title => GetDisplayValue(nameof(GridEntry.Title), GridEntry.Title); public string Title => GridEntry.Title;
public string Authors => GetDisplayValue(nameof(GridEntry.Authors), GridEntry.Authors); public string Authors => GridEntry.Authors;
public string Misc => GetDisplayValue(nameof(GridEntry.Misc), GridEntry.Misc); public string Misc => GridEntry.Misc;
public string DatePurchased => GetDisplayValue(nameof(GridEntry.Purchase_Date), GridEntry.Purchase_Date); public string DatePurchased => GridEntry.PurchaseDate;
private bool _remove = false; private bool _remove = false;
private Image _cover; private Image _cover;
@ -185,7 +185,7 @@ namespace LibationWinForms.Dialogs
{ {
GridEntry = gridEntry; 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); (bool isDefault, byte[] picture) = FileManager.PictureStorage.GetPicture(picDef);
if (isDefault) if (isDefault)
@ -196,19 +196,12 @@ namespace LibationWinForms.Dialogs
private void PictureStorage_PictureCached(object sender, string pictureId) 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); Cover = WindowsDesktopUtilities.WinAudibleImageServer.GetImage(pictureId, FileManager.PictureSize._80x80);
FileManager.PictureStorage.PictureCached -= PictureStorage_PictureCached; 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 = "") => private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

View File

@ -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);
}
}
}

View File

@ -1,86 +1,159 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Drawing; using System.Drawing;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using ApplicationServices; using ApplicationServices;
using DataLayer; using DataLayer;
using Dinah.Core.Drawing;
namespace LibationWinForms namespace LibationWinForms
{ {
internal class GridEntry internal class GridEntry : INotifyPropertyChanged, IObjectMemberComparable
{ {
private LibraryBook libraryBook { get; } public const string LIBERATE_COLUMN_NAME = "Liberate";
private Book book => libraryBook.Book; public const string EDIT_TAGS_COLUMN_NAME = "DisplayTags";
public Book GetBook() => book; #region implementation properties
public LibraryBook GetLibraryBook() => libraryBook; // hide from public fields from Data Source GUI with [Browsable(false)]
public GridEntry(LibraryBook libraryBook) => this.libraryBook = libraryBook;
// hide from public fields from Data Source GUI with [Browsable(false)]
[Browsable(false)]
public string AudibleProductId => Book.AudibleProductId;
[Browsable(false)] [Browsable(false)]
public string AudibleProductId => book.AudibleProductId; public string Tags => Book.UserDefinedItem.Tags;
[Browsable(false)] [Browsable(false)]
public string Tags => book.UserDefinedItem.Tags; public IEnumerable<string> TagsEnumerated => Book.UserDefinedItem.TagsEnumerated;
[Browsable(false)] [Browsable(false)]
public IEnumerable<string> TagsEnumerated => book.UserDefinedItem.TagsEnumerated; public LibraryBook LibraryBook { get; }
[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);
// displayValues is what gets displayed #endregion
// 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<string, string> displayValues { get; } = new Dictionary<string, string>();
public bool TryDisplayValue(string key, out string value) => displayValues.TryGetValue(key, out value);
public Image Cover => public event PropertyChangedEventHandler PropertyChanged;
WindowsDesktopUtilities.WinAudibleImageServer.GetImage(book.PictureId, FileManager.PictureSize._80x80); 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; Title = Book.Title;
return getSortName(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; private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") =>
public string Narrators => book.NarratorNames; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
public int Length #region Data Source properties
public Image Cover
{ {
get get
{ {
displayValues[nameof(Length)] return _cover;
= book.LengthInMinutes == 0
? ""
: $"{book.LengthInMinutes / 60} hr {book.LengthInMinutes % 60} min";
return book.LengthInMinutes;
} }
} private set
public string Series
{
get
{ {
displayValues[nameof(Series)] = book.SeriesNames; _cover = value;
return getSortName(book.SeriesNames); 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<string, Func<object>> _compareValues { get; }
private static Dictionary<Type, IComparer> _objectComparers;
public object GetMemberValue(string propertyName) => _compareValues[propertyName]();
public IComparer GetComparer(Type propertyType) => _objectComparers[propertyType];
/// <summary>
/// Instantiate comparers for every type needed to sort columns.
/// </summary>
static GridEntry()
{
_objectComparers = new Dictionary<Type, IComparer>()
{
{ typeof(string), new ObjectComparer<string>() },
{ typeof(int), new ObjectComparer<int>() },
{ typeof(float), new ObjectComparer<float>() },
{ typeof(DateTime), new ObjectComparer<DateTime>() },
{ typeof(LiberatedState), new ObjectComparer<LiberatedState>() },
};
}
/// <summary>
/// Create getters for all member values by name
/// </summary>
Dictionary<string, Func<object>> 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) private static string getSortName(string unformattedName)
{ {
var sortName = unformattedName var sortName = unformattedName
@ -95,104 +168,115 @@ namespace LibationWinForms
return sortName; return sortName;
} }
private string descriptionCache = null; #endregion
public string Description
#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 var libState = liberatedStatus switch
if (descriptionCache is null)
{ {
if (book.Description is null) LiberatedState.Liberated => "Liberated",
descriptionCache = ""; LiberatedState.PartialDownload => "File has been at least\r\npartially downloaded",
else LiberatedState.NotDownloaded => "Book NOT downloaded",
{ _ => throw new Exception("Unexpected liberation state")
var doc = new HtmlAgilityPack.HtmlDocument(); };
doc.LoadHtml(book.Description);
var noHtml = doc.DocumentNode.InnerText; var pdfState = pdfStatus switch
descriptionCache {
= noHtml.Length < 63 PdfState.Downloaded => "\r\nPDF downloaded",
? noHtml PdfState.NotDownloaded => "\r\nPDF NOT downloaded",
: noHtml.Substring(0, 60) + "..."; 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); // get image
// 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
{ {
displayValues[nameof(Product_Rating)] = starString(book.Rating); var image_lib
return firstScore(book.Rating); = 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 /// <summary>
/// This information should not change during <see cref="GridEntry"/> lifetime, so call only once.
/// </summary>
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) + "...";
}
/// <summary>
/// This information should not change during <see cref="GridEntry"/> lifetime, so call only once.
/// </summary>
private static string GetMiscDisplay(LibraryBook libraryBook)
{ {
get // max 5 text rows
{
displayValues[nameof(Purchase_Date)] = libraryBook.DateAdded.ToString("d"); var details = new List<string>();
return libraryBook.DateAdded.ToString("yyyy-MM-dd HH:mm:ss");
} 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 private static string GetStarString(Rating rating)
{ => (rating?.FirstScore != null && rating?.FirstScore > 0f)
get ? rating?.ToStarString()
{ : "";
displayValues[nameof(My_Rating)] = starString(book.UserDefinedItem.Rating);
return firstScore(book.UserDefinedItem.Rating);
}
}
private string starString(Rating rating) #endregion
=> (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<string>();
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);
}
}
}
} }

View File

@ -0,0 +1,11 @@
using System;
using System.Collections;
namespace LibationWinForms
{
interface IObjectMemberComparable
{
IComparer GetComparer(Type propertyType);
object GetMemberValue(string valueName);
}
}

View File

@ -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;
}
}
}
}

View File

@ -0,0 +1,10 @@
using System;
using System.Collections;
namespace LibationWinForms
{
class ObjectComparer<T> : IComparer where T : IComparable
{
public int Compare(object x, object y) => ((T)x).CompareTo((T)y);
}
}

View File

@ -0,0 +1,21 @@
using System.Collections.Generic;
using System.ComponentModel;
namespace LibationWinForms
{
class ObjectMemberComparer<T> : IComparer<T> 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;
}
}

View File

@ -28,146 +28,198 @@
/// </summary> /// </summary>
private void InitializeComponent() private void InitializeComponent()
{ {
this.components = new System.ComponentModel.Container(); this.components = new System.ComponentModel.Container();
this.gridEntryBindingSource = new System.Windows.Forms.BindingSource(this.components); System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
this.gridEntryDataGridView = new System.Windows.Forms.DataGridView(); this.gridEntryBindingSource = new System.Windows.Forms.BindingSource(this.components);
this.dataGridViewImageColumn1 = new System.Windows.Forms.DataGridViewImageColumn(); this.gridEntryDataGridView = new System.Windows.Forms.DataGridView();
this.dataGridViewTextBoxColumn1 = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.dataGridViewImageButtonBoxColumn1 = new LibationWinForms.LiberateDataGridViewImageButtonColumn();
this.dataGridViewTextBoxColumn2 = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.dataGridViewImageColumn1 = new System.Windows.Forms.DataGridViewImageColumn();
this.dataGridViewTextBoxColumn3 = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.dataGridViewTextBoxColumn1 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn4 = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.dataGridViewTextBoxColumn2 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn5 = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.dataGridViewTextBoxColumn3 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn6 = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.dataGridViewTextBoxColumn4 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn7 = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.dataGridViewTextBoxColumn5 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn8 = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.dataGridViewTextBoxColumn6 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn9 = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.dataGridViewTextBoxColumn7 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn10 = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.ProductRating = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dataGridViewTextBoxColumn11 = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.PurchaseDate = new System.Windows.Forms.DataGridViewTextBoxColumn();
((System.ComponentModel.ISupportInitialize)(this.gridEntryBindingSource)).BeginInit(); this.MyRating = new System.Windows.Forms.DataGridViewTextBoxColumn();
((System.ComponentModel.ISupportInitialize)(this.gridEntryDataGridView)).BeginInit(); this.dataGridViewTextBoxColumn11 = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.SuspendLayout(); this.dataGridViewImageButtonBoxColumn2 = new LibationWinForms.EditTagsDataGridViewImageButtonColumn();
// ((System.ComponentModel.ISupportInitialize)(this.gridEntryBindingSource)).BeginInit();
// gridEntryBindingSource ((System.ComponentModel.ISupportInitialize)(this.gridEntryDataGridView)).BeginInit();
// this.SuspendLayout();
this.gridEntryBindingSource.DataSource = typeof(LibationWinForms.GridEntry); //
// // gridEntryBindingSource
// gridEntryDataGridView //
// this.gridEntryBindingSource.DataSource = typeof(LibationWinForms.GridEntry);
this.gridEntryDataGridView.AutoGenerateColumns = false; //
this.gridEntryDataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; // gridEntryDataGridView
this.gridEntryDataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { //
this.dataGridViewImageColumn1, this.gridEntryDataGridView.AllowUserToAddRows = false;
this.dataGridViewTextBoxColumn1, this.gridEntryDataGridView.AllowUserToDeleteRows = false;
this.dataGridViewTextBoxColumn2, this.gridEntryDataGridView.AutoGenerateColumns = false;
this.dataGridViewTextBoxColumn3, this.gridEntryDataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridViewTextBoxColumn4, this.gridEntryDataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.dataGridViewTextBoxColumn5, this.dataGridViewImageButtonBoxColumn1,
this.dataGridViewTextBoxColumn6, this.dataGridViewImageColumn1,
this.dataGridViewTextBoxColumn7, this.dataGridViewTextBoxColumn1,
this.dataGridViewTextBoxColumn8, this.dataGridViewTextBoxColumn2,
this.dataGridViewTextBoxColumn9, this.dataGridViewTextBoxColumn3,
this.dataGridViewTextBoxColumn10, this.dataGridViewTextBoxColumn4,
this.dataGridViewTextBoxColumn11}); this.dataGridViewTextBoxColumn5,
this.gridEntryDataGridView.DataSource = this.gridEntryBindingSource; this.dataGridViewTextBoxColumn6,
this.gridEntryDataGridView.Location = new System.Drawing.Point(54, 58); this.dataGridViewTextBoxColumn7,
this.gridEntryDataGridView.Name = "gridEntryDataGridView"; this.ProductRating,
this.gridEntryDataGridView.Size = new System.Drawing.Size(300, 220); this.PurchaseDate,
this.gridEntryDataGridView.TabIndex = 0; this.MyRating,
// this.dataGridViewTextBoxColumn11,
// dataGridViewImageColumn1 this.dataGridViewImageButtonBoxColumn2});
// this.gridEntryDataGridView.DataSource = this.gridEntryBindingSource;
this.dataGridViewImageColumn1.DataPropertyName = "Cover"; dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
this.dataGridViewImageColumn1.HeaderText = "Cover"; dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Window;
this.dataGridViewImageColumn1.Name = "dataGridViewImageColumn1"; dataGridViewCellStyle1.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.dataGridViewImageColumn1.ReadOnly = true; dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.ControlText;
// dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
// dataGridViewTextBoxColumn1 dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
// dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
this.dataGridViewTextBoxColumn1.DataPropertyName = "Title"; this.gridEntryDataGridView.DefaultCellStyle = dataGridViewCellStyle1;
this.dataGridViewTextBoxColumn1.HeaderText = "Title"; this.gridEntryDataGridView.Dock = System.Windows.Forms.DockStyle.Fill;
this.dataGridViewTextBoxColumn1.Name = "dataGridViewTextBoxColumn1"; this.gridEntryDataGridView.Location = new System.Drawing.Point(0, 0);
this.dataGridViewTextBoxColumn1.ReadOnly = true; this.gridEntryDataGridView.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
// this.gridEntryDataGridView.Name = "gridEntryDataGridView";
// dataGridViewTextBoxColumn2 this.gridEntryDataGridView.ReadOnly = true;
// this.gridEntryDataGridView.RowHeadersVisible = false;
this.dataGridViewTextBoxColumn2.DataPropertyName = "Authors"; this.gridEntryDataGridView.RowTemplate.Height = 82;
this.dataGridViewTextBoxColumn2.HeaderText = "Authors"; this.gridEntryDataGridView.Size = new System.Drawing.Size(1503, 380);
this.dataGridViewTextBoxColumn2.Name = "dataGridViewTextBoxColumn2"; this.gridEntryDataGridView.TabIndex = 0;
this.dataGridViewTextBoxColumn2.ReadOnly = true; //
// // dataGridViewImageButtonBoxColumn1
// dataGridViewTextBoxColumn3 //
// this.dataGridViewImageButtonBoxColumn1.DataPropertyName = "Liberate";
this.dataGridViewTextBoxColumn3.DataPropertyName = "Narrators"; this.dataGridViewImageButtonBoxColumn1.HeaderText = "Liberate";
this.dataGridViewTextBoxColumn3.HeaderText = "Narrators"; this.dataGridViewImageButtonBoxColumn1.Name = "dataGridViewImageButtonBoxColumn1";
this.dataGridViewTextBoxColumn3.Name = "dataGridViewTextBoxColumn3"; this.dataGridViewImageButtonBoxColumn1.ReadOnly = true;
this.dataGridViewTextBoxColumn3.ReadOnly = true; this.dataGridViewImageButtonBoxColumn1.Resizable = System.Windows.Forms.DataGridViewTriState.False;
// this.dataGridViewImageButtonBoxColumn1.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic;
// dataGridViewTextBoxColumn4 this.dataGridViewImageButtonBoxColumn1.Width = 70;
// //
this.dataGridViewTextBoxColumn4.DataPropertyName = "Length"; // dataGridViewImageColumn1
this.dataGridViewTextBoxColumn4.HeaderText = "Length"; //
this.dataGridViewTextBoxColumn4.Name = "dataGridViewTextBoxColumn4"; this.dataGridViewImageColumn1.DataPropertyName = "Cover";
this.dataGridViewTextBoxColumn4.ReadOnly = true; this.dataGridViewImageColumn1.HeaderText = "Cover";
// this.dataGridViewImageColumn1.Name = "dataGridViewImageColumn1";
// dataGridViewTextBoxColumn5 this.dataGridViewImageColumn1.ReadOnly = true;
// this.dataGridViewImageColumn1.Resizable = System.Windows.Forms.DataGridViewTriState.False;
this.dataGridViewTextBoxColumn5.DataPropertyName = "Series"; this.dataGridViewImageColumn1.ToolTipText = "Cover Art";
this.dataGridViewTextBoxColumn5.HeaderText = "Series"; this.dataGridViewImageColumn1.Width = 80;
this.dataGridViewTextBoxColumn5.Name = "dataGridViewTextBoxColumn5"; //
this.dataGridViewTextBoxColumn5.ReadOnly = true; // dataGridViewTextBoxColumn1
// //
// dataGridViewTextBoxColumn6 this.dataGridViewTextBoxColumn1.DataPropertyName = "Title";
// this.dataGridViewTextBoxColumn1.HeaderText = "Title";
this.dataGridViewTextBoxColumn6.DataPropertyName = "Description"; this.dataGridViewTextBoxColumn1.Name = "dataGridViewTextBoxColumn1";
this.dataGridViewTextBoxColumn6.HeaderText = "Description"; this.dataGridViewTextBoxColumn1.ReadOnly = true;
this.dataGridViewTextBoxColumn6.Name = "dataGridViewTextBoxColumn6"; this.dataGridViewTextBoxColumn1.ToolTipText = "Book Title";
this.dataGridViewTextBoxColumn6.ReadOnly = true; this.dataGridViewTextBoxColumn1.Width = 200;
// //
// dataGridViewTextBoxColumn7 // dataGridViewTextBoxColumn2
// //
this.dataGridViewTextBoxColumn7.DataPropertyName = "Category"; this.dataGridViewTextBoxColumn2.DataPropertyName = "Authors";
this.dataGridViewTextBoxColumn7.HeaderText = "Category"; this.dataGridViewTextBoxColumn2.HeaderText = "Authors";
this.dataGridViewTextBoxColumn7.Name = "dataGridViewTextBoxColumn7"; this.dataGridViewTextBoxColumn2.Name = "dataGridViewTextBoxColumn2";
this.dataGridViewTextBoxColumn7.ReadOnly = true; this.dataGridViewTextBoxColumn2.ReadOnly = true;
// this.dataGridViewTextBoxColumn2.ToolTipText = "Book Author(s)";
// dataGridViewTextBoxColumn8 //
// // dataGridViewTextBoxColumn3
this.dataGridViewTextBoxColumn8.DataPropertyName = "Product_Rating"; //
this.dataGridViewTextBoxColumn8.HeaderText = "Product_Rating"; this.dataGridViewTextBoxColumn3.DataPropertyName = "Narrators";
this.dataGridViewTextBoxColumn8.Name = "dataGridViewTextBoxColumn8"; this.dataGridViewTextBoxColumn3.HeaderText = "Narrators";
this.dataGridViewTextBoxColumn8.ReadOnly = true; this.dataGridViewTextBoxColumn3.Name = "dataGridViewTextBoxColumn3";
// this.dataGridViewTextBoxColumn3.ReadOnly = true;
// dataGridViewTextBoxColumn9 this.dataGridViewTextBoxColumn3.ToolTipText = "Audiobook Narrator(s)";
// //
this.dataGridViewTextBoxColumn9.DataPropertyName = "Purchase_Date"; // dataGridViewTextBoxColumn4
this.dataGridViewTextBoxColumn9.HeaderText = "Purchase_Date"; //
this.dataGridViewTextBoxColumn9.Name = "dataGridViewTextBoxColumn9"; this.dataGridViewTextBoxColumn4.DataPropertyName = "Length";
this.dataGridViewTextBoxColumn9.ReadOnly = true; this.dataGridViewTextBoxColumn4.HeaderText = "Length";
// this.dataGridViewTextBoxColumn4.Name = "dataGridViewTextBoxColumn4";
// dataGridViewTextBoxColumn10 this.dataGridViewTextBoxColumn4.ReadOnly = true;
// this.dataGridViewTextBoxColumn4.ToolTipText = "Recording Length";
this.dataGridViewTextBoxColumn10.DataPropertyName = "My_Rating"; //
this.dataGridViewTextBoxColumn10.HeaderText = "My_Rating"; // dataGridViewTextBoxColumn5
this.dataGridViewTextBoxColumn10.Name = "dataGridViewTextBoxColumn10"; //
this.dataGridViewTextBoxColumn10.ReadOnly = true; this.dataGridViewTextBoxColumn5.DataPropertyName = "Series";
// this.dataGridViewTextBoxColumn5.HeaderText = "Series";
// dataGridViewTextBoxColumn11 this.dataGridViewTextBoxColumn5.Name = "dataGridViewTextBoxColumn5";
// this.dataGridViewTextBoxColumn5.ReadOnly = true;
this.dataGridViewTextBoxColumn11.DataPropertyName = "Misc"; this.dataGridViewTextBoxColumn5.ToolTipText = "Book Series";
this.dataGridViewTextBoxColumn11.HeaderText = "Misc"; //
this.dataGridViewTextBoxColumn11.Name = "dataGridViewTextBoxColumn11"; // dataGridViewTextBoxColumn6
this.dataGridViewTextBoxColumn11.ReadOnly = true; //
// this.dataGridViewTextBoxColumn6.DataPropertyName = "Description";
// ProductsGrid this.dataGridViewTextBoxColumn6.HeaderText = "Description";
// this.dataGridViewTextBoxColumn6.Name = "dataGridViewTextBoxColumn6";
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.dataGridViewTextBoxColumn6.ReadOnly = true;
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; //
this.Controls.Add(this.gridEntryDataGridView); // dataGridViewTextBoxColumn7
this.Name = "ProductsGrid"; //
this.Size = new System.Drawing.Size(434, 329); this.dataGridViewTextBoxColumn7.DataPropertyName = "Category";
((System.ComponentModel.ISupportInitialize)(this.gridEntryBindingSource)).EndInit(); this.dataGridViewTextBoxColumn7.HeaderText = "Category";
((System.ComponentModel.ISupportInitialize)(this.gridEntryDataGridView)).EndInit(); this.dataGridViewTextBoxColumn7.Name = "dataGridViewTextBoxColumn7";
this.ResumeLayout(false); 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.BindingSource gridEntryBindingSource;
private System.Windows.Forms.DataGridView gridEntryDataGridView; private System.Windows.Forms.DataGridView gridEntryDataGridView;
private System.Windows.Forms.DataGridViewImageColumn dataGridViewImageColumn1; private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn8;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn1; private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn9;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn2; private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn10;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn3; private LiberateDataGridViewImageButtonColumn dataGridViewImageButtonBoxColumn1;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn4; private System.Windows.Forms.DataGridViewImageColumn dataGridViewImageColumn1;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn5; private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn1;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn6; private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn2;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn7; private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn3;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn8; private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn4;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn9; private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn5;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn10; private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn6;
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn11; 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;
}
} }

View File

@ -1,12 +1,11 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using ApplicationServices; using ApplicationServices;
using DataLayer; using DataLayer;
using Dinah.Core; using Dinah.Core;
using Dinah.Core.Collections.Generic;
using Dinah.Core.DataBinding;
using Dinah.Core.Windows.Forms; using Dinah.Core.Windows.Forms;
using LibationWinForms.Dialogs; using LibationWinForms.Dialogs;
@ -30,158 +29,54 @@ namespace LibationWinForms
public event EventHandler<int> VisibleCountChanged; public event EventHandler<int> VisibleCountChanged;
public event EventHandler BackupCountsChanged; public event EventHandler BackupCountsChanged;
private const string EDIT_TAGS = "Edit Tags";
private const string LIBERATE = "Liberate";
// alias // alias
private DataGridView dataGridView => gridEntryDataGridView; private DataGridView _dataGridView => gridEntryDataGridView;
public ProductsGrid() public ProductsGrid()
{ {
InitializeComponent(); 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 // 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 EnableDoubleBuffering();
private void replaceFormatted(object sender, DataGridViewCellFormattingEventArgs e) }
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]; // handle grid button click: https://stackoverflow.com/a/13687844
if (col is DataGridViewTextBoxColumn textCol && getGridEntry(e.RowIndex).TryDisplayValue(textCol.Name, out string value)) 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; case GridEntry.LIBERATE_COLUMN_NAME:
// it's the wrong way and will infinitely call CellFormatting on each assign await Liberate_Click(liveGridEntry);
break;
// this is the correct way. will actually set FormattedValue (and EditedFormattedValue) while leaving Value as-is for sorting case GridEntry.EDIT_TAGS_COLUMN_NAME:
e.Value = value; EditTags_Click(liveGridEntry);
break;
getCell(e).ToolTipText = value;
} }
} }
private void hiddenFormatting(object sender, DataGridViewCellFormattingEventArgs e) private async Task Liberate_Click(GridEntry liveGridEntry)
{ {
var dgv = (DataGridView)sender; var libraryBook = liveGridEntry.LibraryBook;
// 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();
// liberated: open explorer to file // liberated: open explorer to file
if (TransitionalFileLocator.Audio_Exists(libraryBook.Book)) if (TransitionalFileLocator.Audio_Exists(libraryBook.Book))
@ -195,146 +90,24 @@ namespace LibationWinForms
// else: liberate // else: liberate
await BookLiberation.ProcessorAutomationController.BackupSingleBookAsync(libraryBook, (_, __) => RefreshRow(libraryBook.Book.AudibleProductId)); 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); var bookDetailsForm = new BookDetailsDialog(liveGridEntry.Title, liveGridEntry.Tags);
// 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);
if (bookDetailsForm.ShowDialog() != DialogResult.OK) if (bookDetailsForm.ShowDialog() != DialogResult.OK)
return; return;
var qtyChanges = LibraryCommands.UpdateTags(liveGridEntry.GetBook(), bookDetailsForm.NewTags); var qtyChanges = LibraryCommands.UpdateTags(liveGridEntry.LibraryBook.Book, bookDetailsForm.NewTags);
if (qtyChanges == 0) if (qtyChanges == 0)
return; return;
// force a re-draw, and re-apply filters //Re-apply filters
Filter();
// needed to update text colors
dgv.InvalidateRow(e.RowIndex);
filter();
} }
#endregion #endregion
private static void drawImage(DataGridViewCellPaintingEventArgs e, Bitmap image) #region UI display functions
{
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
private bool hasBeenDisplayed = false; private bool hasBeenDisplayed = false;
public void Display() public void Display()
@ -352,15 +125,15 @@ namespace LibationWinForms
// if no data. hide all columns. return // if no data. hide all columns. return
if (!lib.Any()) if (!lib.Any())
{ {
for (var i = dataGridView.ColumnCount - 1; i >= 0; i--) for (var i = _dataGridView.ColumnCount - 1; i >= 0; i--)
dataGridView.Columns.RemoveAt(i); _dataGridView.Columns.RemoveAt(i);
return; return;
} }
var orderedGridEntries = lib var orderedGridEntries = lib
.Select(lb => new GridEntry(lb)).ToList() .Select(lb => new GridEntry(lb)).ToList()
// default load order // default load order
.OrderByDescending(ge => ge.Purchase_Date) .OrderByDescending(ge => ge.PurchaseDate)
//// more advanced example: sort by author, then series, then title //// more advanced example: sort by author, then series, then title
//.OrderBy(ge => ge.Authors) //.OrderBy(ge => ge.Authors)
// .ThenBy(ge => ge.Series) // .ThenBy(ge => ge.Series)
@ -370,49 +143,83 @@ namespace LibationWinForms
// //
// BIND // BIND
// //
gridEntryBindingSource.DataSource = orderedGridEntries.ToSortableBindingList(); gridEntryBindingSource.DataSource = new SortableBindingList2<GridEntry>(orderedGridEntries);
// //
// FILTER // FILTER
// //
filter(); Filter();
BackupCountsChanged?.Invoke(this, EventArgs.Empty); 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 #region filter
string _filterSearchString; string _filterSearchString;
private void filter() => Filter(_filterSearchString); private void Filter() => Filter(_filterSearchString);
public void Filter(string searchString) public void Filter(string searchString)
{ {
_filterSearchString = searchString; _filterSearchString = searchString;
if (dataGridView.Rows.Count == 0) if (_dataGridView.Rows.Count == 0)
return; return;
var searchResults = SearchEngineCommands.Search(searchString); var searchResults = SearchEngineCommands.Search(searchString);
var productIds = searchResults.Docs.Select(d => d.ProductId).ToList(); var productIds = searchResults.Docs.Select(d => d.ProductId).ToList();
// https://stackoverflow.com/a/18942430 // https://stackoverflow.com/a/18942430
var currencyManager = (CurrencyManager)BindingContext[dataGridView.DataSource]; var currencyManager = (CurrencyManager)BindingContext[_dataGridView.DataSource];
currencyManager.SuspendBinding(); currencyManager.SuspendBinding();
{ {
for (var r = dataGridView.RowCount - 1; r >= 0; r--) for (var r = _dataGridView.RowCount - 1; r >= 0; r--)
dataGridView.Rows[r].Visible = productIds.Contains(getGridEntry(r).AudibleProductId); _dataGridView.Rows[r].Visible = productIds.Contains(getGridEntry(r).AudibleProductId);
} }
currencyManager.ResumeBinding(); currencyManager.ResumeBinding();
VisibleCountChanged?.Invoke(this, dataGridView.AsEnumerable().Count(r => r.Visible)); VisibleCountChanged?.Invoke(this, _dataGridView.AsEnumerable().Count(r => r.Visible));
} }
#endregion #endregion
private int getRowId(Func<GridEntry, bool> func) => dataGridView.GetRowIdOfBoundItem(func); #region DataGridView Macros
private GridEntry getGridEntry(int rowIndex) => dataGridView.GetBoundItem<GridEntry>(rowIndex); private int getRowIndex(Func<GridEntry, bool> func) => _dataGridView.GetRowIdOfBoundItem(func);
private GridEntry getGridEntry(int rowIndex) => _dataGridView.GetBoundItem<GridEntry>(rowIndex);
private DataGridViewCell getCell(DataGridViewCellFormattingEventArgs e) => _dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex];
private DataGridViewCell getCell(DataGridViewCellFormattingEventArgs e) => getCell(e.RowIndex, e.ColumnIndex); #endregion
private DataGridViewCell getCell(DataGridViewCellPaintingEventArgs e) => getCell(e.RowIndex, e.ColumnIndex);
private DataGridViewCell getCell(int rowIndex, int columnIndex) => dataGridView.Rows[rowIndex].Cells[columnIndex];
} }
} }

View File

@ -1,64 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <root>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true"> <xsd:element name="root" msdata:IsDataSet="true">
@ -120,4 +60,19 @@
<metadata name="gridEntryBindingSource.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> <metadata name="gridEntryBindingSource.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value> <value>17, 17</value>
</metadata> </metadata>
<metadata name="dataGridViewImageButtonBoxColumn1.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="ProductRating.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="PurchaseDate.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="MyRating.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="dataGridViewImageButtonBoxColumn2.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
</root> </root>

View File

@ -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<T> : BindingList<T> where T : IObjectMemberComparable
{
private ObjectMemberComparer<T> Comparer = new();
private bool isSorted;
private ListSortDirection listSortDirection;
private PropertyDescriptor propertyDescriptor;
public SortableBindingList2() : base(new List<T>()) { }
public SortableBindingList2(IEnumerable<T> enumeration) : base(new List<T>(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<T> itemsList = (List<T>)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;
}
}
}