Finish MVVM conversion

This commit is contained in:
Michael Bucari-Tovo 2021-08-21 21:17:22 -06:00
parent 9e06c343c1
commit 2a7e185dc3
5 changed files with 89 additions and 38 deletions

View File

@ -163,6 +163,7 @@ namespace ApplicationServices
using var context = DbContexts.GetContext();
var udi = libraryBook.UserDefinedItem;
udi.Tags = newTags;
// Attach() NoTracking entities before SaveChanges()
context.Attach(udi).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
@ -186,6 +187,7 @@ namespace ApplicationServices
using var context = DbContexts.GetContext();
var udi = libraryBook.UserDefinedItem;
udi.BookStatus = liberatedStatus;
// Attach() NoTracking entities before SaveChanges()
context.Attach(udi).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
@ -209,6 +211,7 @@ namespace ApplicationServices
using var context = DbContexts.GetContext();
var udi = libraryBook.UserDefinedItem;
udi.PdfStatus = liberatedStatus;
// Attach() NoTracking entities before SaveChanges()
context.Attach(udi).State = Microsoft.EntityFrameworkCore.EntityState.Modified;

View File

@ -44,8 +44,12 @@ namespace DataLayer
get => _tags;
set
{
_tags = sanitize(value);
ItemChanged?.Invoke(this, nameof(Tags));
var newTags = sanitize(value);
if (_tags != newTags)
{
_tags = newTags;
ItemChanged?.Invoke(this, nameof(Tags));
}
}
}

View File

@ -384,15 +384,14 @@ namespace LibationWinForms
#region liberate menu
private async void beginBookBackupsToolStripMenuItem_Click(object sender, EventArgs e)
=> await BookLiberation.ProcessorAutomationController.BackupAllBooksAsync(updateGridRow);
=> await BookLiberation.ProcessorAutomationController.BackupAllBooksAsync();
private async void beginPdfBackupsToolStripMenuItem_Click(object sender, EventArgs e)
=> await BookLiberation.ProcessorAutomationController.BackupAllPdfsAsync(updateGridRow);
=> await BookLiberation.ProcessorAutomationController.BackupAllPdfsAsync();
private async void convertAllM4bToMp3ToolStripMenuItem_Click(object sender, EventArgs e)
=> await BookLiberation.ProcessorAutomationController.ConvertAllBooksAsync();
private void updateGridRow(object _, LibraryBook libraryBook) => currProductsGrid.RefreshLiberatedStatus(libraryBook);
#endregion
#region Export menu

View File

@ -12,6 +12,9 @@ using Dinah.Core.Drawing;
namespace LibationWinForms
{
/// <summary>
/// The View Model for a LibraryBook
/// </summary>
internal class GridEntry : AsyncNotifyPropertyChanged, IMemberComparable
{
#region implementation properties
@ -26,12 +29,15 @@ namespace LibationWinForms
private Book Book => LibraryBook.Book;
private Image _cover;
private Action _refilter;
public GridEntry(LibraryBook libraryBook)
public GridEntry(LibraryBook libraryBook, Action refilterOnChanged = null)
{
LibraryBook = libraryBook;
_refilter = refilterOnChanged;
_memberValues = CreateMemberValueDictionary();
//Get cover art. If it's default, subscribe to PictureCached
{
(bool isDefault, byte[] picture) = FileManager.PictureStorage.GetPicture(new FileManager.PictureDefinition(Book.PictureId, FileManager.PictureSize._80x80));
@ -58,7 +64,7 @@ namespace LibationWinForms
Description = GetDescriptionDisplay(Book);
}
//DisplayTags and Liberate properties are live.
UserDefinedItem.ItemChanged += UserDefinedItem_ItemChanged;
}
private void PictureStorage_PictureCached(object sender, FileManager.PictureCachedEventArgs e)
@ -70,7 +76,48 @@ namespace LibationWinForms
}
}
#region Data Source properties
#region detect changes to the model and update the view
/// <summary>
/// This event handler receives notifications from the model that it has changed.
/// Save to the database and notify the view that it's changed.
/// </summary>
private void UserDefinedItem_ItemChanged(object sender, string itemName)
{
var udi = sender as UserDefinedItem;
if (udi.Book.AudibleProductId != LibraryBook.Book.AudibleProductId)
return;
switch (itemName)
{
case nameof(udi.Tags):
{
LibraryCommands.UpdateTags(LibraryBook.Book, udi.Tags);
NotifyPropertyChanged(nameof(DisplayTags));
}
break;
case nameof(udi.BookStatus):
{
var status = udi.BookStatus == LiberatedStatus.PartialDownload ? LiberatedStatus.NotLiberated : udi.BookStatus;
LibraryCommands.UpdateBook(LibraryBook.Book, status);
NotifyPropertyChanged(nameof(Liberate));
}
break;
case nameof(udi.PdfStatus):
{
LibraryCommands.UpdatePdf(LibraryBook.Book, udi.PdfStatus);
NotifyPropertyChanged(nameof(Liberate));
}
break;
}
_refilter?.Invoke();
}
#endregion
#region Model properties exposed to the view
public Image Cover
{
get
@ -95,8 +142,22 @@ namespace LibationWinForms
public string Category { get; }
public string Misc { get; }
public string Description { get; }
public string DisplayTags => string.Join("\r\n", Book.UserDefinedItem.TagsEnumerated);
public (LiberatedStatus BookStatus, LiberatedStatus? PdfStatus) Liberate => (LibraryCommands.Liberated_Status(Book), LibraryCommands.Pdf_Status(Book));
public string DisplayTags
{
get=> Book.UserDefinedItem.Tags;
set => Book.UserDefinedItem.Tags = value;
}
public (LiberatedStatus BookStatus, LiberatedStatus? PdfStatus) Liberate
{
get => (LibraryCommands.Liberated_Status(LibraryBook.Book), LibraryCommands.Pdf_Status(LibraryBook.Book));
set
{
LibraryBook.Book.UserDefinedItem.BookStatus = value.BookStatus;
LibraryBook.Book.UserDefinedItem.PdfStatus = value.PdfStatus;
}
}
#endregion
#region Data Sorting
@ -199,5 +260,12 @@ namespace LibationWinForms
}
#endregion
~GridEntry()
{
UserDefinedItem.ItemChanged -= UserDefinedItem_ItemChanged;
FileManager.PictureStorage.PictureCached -= PictureStorage_PictureCached;
}
}
}

View File

@ -86,7 +86,7 @@ namespace LibationWinForms
}
// else: liberate
await BookLiberation.ProcessorAutomationController.BackupSingleBookAsync(libraryBook, (_, __) => RefreshRow(libraryBook.Book.AudibleProductId));
await BookLiberation.ProcessorAutomationController.BackupSingleBookAsync(libraryBook);
}
private void Details_Click(GridEntry liveGridEntry)
@ -95,15 +95,10 @@ namespace LibationWinForms
if (bookDetailsForm.ShowDialog() != DialogResult.OK)
return;
var qtyChanges = LibraryCommands.UpdateUserDefinedItem(liveGridEntry.LibraryBook.Book, bookDetailsForm.NewTags, bookDetailsForm.BookLiberatedStatus, bookDetailsForm.PdfLiberatedStatus);
if (qtyChanges == 0)
return;
liveGridEntry.DisplayTags = bookDetailsForm.NewTags;
liveGridEntry.Liberate = (bookDetailsForm.BookLiberatedStatus, bookDetailsForm.PdfLiberatedStatus);
//Re-apply filters
Filter();
//Update whole GridEntry row
liveGridEntry.NotifyPropertyChanged();
BackupCountsChanged?.Invoke(this, EventArgs.Empty);
}
#endregion
@ -132,7 +127,7 @@ namespace LibationWinForms
}
var orderedGridEntries = lib
.Select(lb => new GridEntry(lb)).ToList()
.Select(lb => new GridEntry(lb, Filter)).ToList()
// default load order
.OrderByDescending(ge => (DateTime)ge.GetMemberValue(nameof(ge.PurchaseDate)))
//// more advanced example: sort by author, then series, then title
@ -150,19 +145,6 @@ namespace LibationWinForms
BackupCountsChanged?.Invoke(this, EventArgs.Empty);
}
public void RefreshRow(string productId)
{
var liveGridEntry = getGridEntry((ge) => ge.AudibleProductId == productId);
// update GridEntry Liberate cell
liveGridEntry?.NotifyPropertyChanged(nameof(liveGridEntry.Liberate));
// needed in case filtering by -IsLiberated and it gets changed to Liberated. want to immediately show the change
Filter();
BackupCountsChanged?.Invoke(this, EventArgs.Empty);
}
#endregion
#region Filter
@ -195,12 +177,7 @@ namespace LibationWinForms
#endregion
#region DataGridView Macro
private GridEntry getGridEntry(Func<GridEntry, bool> predicate)
=> ((SortableBindingList<GridEntry>)gridEntryBindingSource.DataSource).FirstOrDefault(predicate);
private GridEntry getGridEntry(int rowIndex) => _dataGridView.GetBoundItem<GridEntry>(rowIndex);
#endregion
}
}