Move source code into "Source" folder
This commit is contained in:
parent
1ee73fa1a7
commit
389fbb2371
287 changed files with 26 additions and 8 deletions
14
Source/LibationWinForms/grid/AsyncNotifyPropertyChanged.cs
Normal file
14
Source/LibationWinForms/grid/AsyncNotifyPropertyChanged.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
using Dinah.Core.Threading;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace LibationWinForms
|
||||
{
|
||||
public abstract class AsyncNotifyPropertyChanged : SynchronizeInvoker, INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
|
||||
=> this.UIThreadAsync(() => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)));
|
||||
}
|
||||
}
|
||||
18
Source/LibationWinForms/grid/DataGridViewImageButtonCell.cs
Normal file
18
Source/LibationWinForms/grid/DataGridViewImageButtonCell.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace LibationWinForms
|
||||
{
|
||||
public class DataGridViewImageButtonCell : DataGridViewButtonCell
|
||||
{
|
||||
protected void DrawButtonImage(Graphics graphics, Image 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace LibationWinForms
|
||||
{
|
||||
public class EditTagsDataGridViewImageButtonColumn : DataGridViewButtonColumn
|
||||
{
|
||||
public EditTagsDataGridViewImageButtonColumn()
|
||||
{
|
||||
CellTemplate = new EditTagsDataGridViewImageButtonCell();
|
||||
}
|
||||
}
|
||||
|
||||
internal class EditTagsDataGridViewImageButtonCell : DataGridViewImageButtonCell
|
||||
{
|
||||
private static Image ButtonImage { get; } = Properties.Resources.edit_25x25;
|
||||
private static Color HiddenForeColor { get; } = Color.LightGray;
|
||||
|
||||
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)
|
||||
{
|
||||
var tagsString = (string)value;
|
||||
|
||||
var foreColor = tagsString?.Contains("hidden") == true ? HiddenForeColor : DataGridView.DefaultCellStyle.ForeColor;
|
||||
|
||||
if (DataGridView.Rows[rowIndex].DefaultCellStyle.ForeColor != foreColor)
|
||||
{
|
||||
DataGridView.Rows[rowIndex].DefaultCellStyle.ForeColor = foreColor;
|
||||
}
|
||||
|
||||
if (tagsString?.Length == 0)
|
||||
{
|
||||
base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, null, null, null, cellStyle, advancedBorderStyle, paintParts);
|
||||
DrawButtonImage(graphics, ButtonImage, cellBounds);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
310
Source/LibationWinForms/grid/GridEntry.cs
Normal file
310
Source/LibationWinForms/grid/GridEntry.cs
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using ApplicationServices;
|
||||
using DataLayer;
|
||||
using Dinah.Core.DataBinding;
|
||||
using Dinah.Core;
|
||||
using Dinah.Core.Drawing;
|
||||
using LibationFileManager;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibationWinForms
|
||||
{
|
||||
/// <summary>
|
||||
/// The View Model for a LibraryBook
|
||||
/// </summary>
|
||||
internal class GridEntry : AsyncNotifyPropertyChanged, IMemberComparable
|
||||
{
|
||||
#region implementation properties NOT exposed to the view
|
||||
// hide from public fields from Data Source GUI with [Browsable(false)]
|
||||
|
||||
[Browsable(false)]
|
||||
public string AudibleProductId => Book.AudibleProductId;
|
||||
[Browsable(false)]
|
||||
public LibraryBook LibraryBook { get; private set; }
|
||||
#endregion
|
||||
|
||||
#region Model properties exposed to the view
|
||||
private Image _cover;
|
||||
public Image Cover
|
||||
{
|
||||
get => _cover;
|
||||
private set
|
||||
{
|
||||
_cover = value;
|
||||
NotifyPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public bool DownloadInProgress { get; private set; }
|
||||
public string ProductRating { get; private set; }
|
||||
public string PurchaseDate { get; private set; }
|
||||
public string MyRating { get; private set; }
|
||||
public string Series { get; private set; }
|
||||
public string Title { get; private set; }
|
||||
public string Length { get; private set; }
|
||||
public string Authors { get; private set; }
|
||||
public string Narrators { get; private set; }
|
||||
public string Category { get; private set; }
|
||||
public string Misc { get; private set; }
|
||||
public string Description { get; private set; }
|
||||
public string DisplayTags
|
||||
{
|
||||
get => string.Join("\r\n", Book.UserDefinedItem.TagsEnumerated);
|
||||
set => Book.UserDefinedItem.Tags = value;
|
||||
}
|
||||
|
||||
// these 2 values being in 1 field is the trick behind getting the liberated+pdf 'stoplight' icon to draw. See: LiberateDataGridViewImageButtonCell.Paint
|
||||
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
|
||||
|
||||
public event EventHandler<string> LibraryBookUpdated;
|
||||
public event EventHandler Committed;
|
||||
|
||||
// alias
|
||||
private Book Book => LibraryBook.Book;
|
||||
|
||||
public GridEntry(LibraryBook libraryBook) => setLibraryBook(libraryBook);
|
||||
|
||||
public async Task DownloadBook()
|
||||
{
|
||||
if (!DownloadInProgress)
|
||||
{
|
||||
try
|
||||
{
|
||||
DownloadInProgress = true;
|
||||
await BookLiberation.ProcessorAutomationController.BackupSingleBookAsync(LibraryBook);
|
||||
}
|
||||
finally
|
||||
{
|
||||
DownloadInProgress = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateLibraryBook(LibraryBook libraryBook)
|
||||
{
|
||||
if (AudibleProductId != libraryBook.Book.AudibleProductId)
|
||||
throw new Exception("Invalid grid entry update. IDs must match");
|
||||
|
||||
setLibraryBook(libraryBook);
|
||||
}
|
||||
|
||||
private void setLibraryBook(LibraryBook libraryBook)
|
||||
{
|
||||
LibraryBook = libraryBook;
|
||||
_memberValues = CreateMemberValueDictionary();
|
||||
|
||||
// Get cover art. If it's default, subscribe to PictureCached
|
||||
{
|
||||
(bool isDefault, byte[] picture) = PictureStorage.GetPicture(new PictureDefinition(Book.PictureId, PictureSize._80x80));
|
||||
|
||||
if (isDefault)
|
||||
PictureStorage.PictureCached += PictureStorage_PictureCached;
|
||||
|
||||
// Mutable property. Set the field so PropertyChanged isn't fired.
|
||||
_cover = ImageReader.ToImage(picture);
|
||||
}
|
||||
|
||||
// Immutable properties
|
||||
{
|
||||
Title = Book.Title;
|
||||
Series = Book.SeriesNames;
|
||||
Length = Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min";
|
||||
MyRating = Book.UserDefinedItem.Rating?.ToStarString()?.DefaultIfNullOrWhiteSpace("");
|
||||
PurchaseDate = libraryBook.DateAdded.ToString("d");
|
||||
ProductRating = Book.Rating?.ToStarString()?.DefaultIfNullOrWhiteSpace("");
|
||||
Authors = Book.AuthorNames;
|
||||
Narrators = Book.NarratorNames;
|
||||
Category = string.Join(" > ", Book.CategoriesNames);
|
||||
Misc = GetMiscDisplay(libraryBook);
|
||||
Description = GetDescriptionDisplay(Book);
|
||||
}
|
||||
|
||||
UserDefinedItem.ItemChanged += UserDefinedItem_ItemChanged;
|
||||
|
||||
// this will never have a value when triggered by ctor b/c nothing can subscribe to the event until after ctor is complete
|
||||
LibraryBookUpdated?.Invoke(this, AudibleProductId);
|
||||
}
|
||||
|
||||
private void PictureStorage_PictureCached(object sender, PictureCachedEventArgs e)
|
||||
{
|
||||
if (e.Definition.PictureId == Book.PictureId)
|
||||
{
|
||||
Cover = ImageReader.ToImage(e.Picture);
|
||||
PictureStorage.PictureCached -= PictureStorage_PictureCached;
|
||||
}
|
||||
}
|
||||
|
||||
#region detect changes to the model, update the view, and save to database.
|
||||
|
||||
/// <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 != Book.AudibleProductId)
|
||||
return;
|
||||
|
||||
switch (itemName)
|
||||
{
|
||||
case nameof(udi.Tags):
|
||||
Book.UserDefinedItem.Tags = udi.Tags;
|
||||
NotifyPropertyChanged(nameof(DisplayTags));
|
||||
break;
|
||||
case nameof(udi.BookStatus):
|
||||
Book.UserDefinedItem.BookStatus = udi.BookStatus;
|
||||
NotifyPropertyChanged(nameof(Liberate));
|
||||
break;
|
||||
case nameof(udi.PdfStatus):
|
||||
Book.UserDefinedItem.PdfStatus = udi.PdfStatus;
|
||||
NotifyPropertyChanged(nameof(Liberate));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!suspendCommit)
|
||||
Commit();
|
||||
}
|
||||
private bool suspendCommit = false;
|
||||
|
||||
/// <summary>
|
||||
/// Begin editing the model, suspending commits until <see cref="EndEdit"/> is called.
|
||||
/// </summary>
|
||||
public void BeginEdit() => suspendCommit = true;
|
||||
|
||||
/// <summary>
|
||||
/// Save all edits to the database.
|
||||
/// </summary>
|
||||
public void EndEdit()
|
||||
{
|
||||
Commit();
|
||||
suspendCommit = false;
|
||||
}
|
||||
|
||||
private void Commit()
|
||||
{
|
||||
// We don't want LiberatedStatus.PartialDownload to be a persistent status.
|
||||
// If display/icon status is PartialDownload then save NotLiberated to db then restore PartialDownload for display
|
||||
var displayStatus = Book.UserDefinedItem.BookStatus;
|
||||
var saveStatus = displayStatus == LiberatedStatus.PartialDownload ? LiberatedStatus.NotLiberated : displayStatus;
|
||||
Book.UserDefinedItem.BookStatus = saveStatus;
|
||||
|
||||
LibraryCommands.UpdateUserDefinedItem(Book);
|
||||
|
||||
Book.UserDefinedItem.BookStatus = displayStatus;
|
||||
|
||||
Committed?.Invoke(this, null);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Data Sorting
|
||||
// These methods are implementation of Dinah.Core.DataBinding.IMemberComparable
|
||||
// Used by Dinah.Core.DataBinding.SortableBindingList<T> for all sorting
|
||||
public virtual object GetMemberValue(string memberName) => _memberValues[memberName]();
|
||||
public virtual IComparer GetMemberComparer(Type memberType) => _memberTypeComparers[memberType];
|
||||
|
||||
private Dictionary<string, Func<object>> _memberValues { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create getters for all member object values by name
|
||||
/// </summary>
|
||||
private Dictionary<string, Func<object>> CreateMemberValueDictionary() => new()
|
||||
{
|
||||
{ nameof(Title), () => Book.TitleSortable },
|
||||
{ nameof(Series), () => Book.SeriesSortable },
|
||||
{ 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.BookStatus }
|
||||
};
|
||||
|
||||
// Instantiate comparers for every exposed member object type.
|
||||
private static readonly Dictionary<Type, IComparer> _memberTypeComparers = new()
|
||||
{
|
||||
{ typeof(string), new ObjectComparer<string>() },
|
||||
{ typeof(int), new ObjectComparer<int>() },
|
||||
{ typeof(float), new ObjectComparer<float>() },
|
||||
{ typeof(DateTime), new ObjectComparer<DateTime>() },
|
||||
{ typeof(LiberatedStatus), new ObjectComparer<LiberatedStatus>() },
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region Static library display functions
|
||||
|
||||
/// <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.
|
||||
/// Maximum of 5 text rows will fit in 80-pixel row height.
|
||||
/// </summary>
|
||||
private static string GetMiscDisplay(LibraryBook libraryBook)
|
||||
{
|
||||
var details = new List<string>();
|
||||
|
||||
var locale = libraryBook.Book.Locale.DefaultIfNullOrWhiteSpace("[unknown]");
|
||||
var acct = libraryBook.Account.DefaultIfNullOrWhiteSpace("[unknown]");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
~GridEntry()
|
||||
{
|
||||
UserDefinedItem.ItemChanged -= UserDefinedItem_ItemChanged;
|
||||
PictureStorage.PictureCached -= PictureStorage_PictureCached;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
using System.Linq;
|
||||
using DataLayer;
|
||||
|
||||
namespace LibationWinForms
|
||||
{
|
||||
public class LiberateDataGridViewImageButtonColumn : DataGridViewButtonColumn
|
||||
{
|
||||
public LiberateDataGridViewImageButtonColumn()
|
||||
{
|
||||
CellTemplate = 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 (LiberatedStatus, LiberatedStatus) or (LiberatedStatus, null))
|
||||
{
|
||||
var (bookState, pdfState) = ((LiberatedStatus bookState, LiberatedStatus? pdfState))value;
|
||||
|
||||
(string mouseoverText, Bitmap buttonImage) = GetLiberateDisplay(bookState, pdfState);
|
||||
|
||||
DrawButtonImage(graphics, buttonImage, cellBounds);
|
||||
|
||||
ToolTipText = mouseoverText;
|
||||
}
|
||||
}
|
||||
|
||||
private static (string mouseoverText, Bitmap buttonImage) GetLiberateDisplay(LiberatedStatus liberatedStatus, LiberatedStatus? pdfStatus)
|
||||
{
|
||||
if (liberatedStatus == LiberatedStatus.Error)
|
||||
return ("Book downloaded ERROR", SystemIcons.Error.ToBitmap());
|
||||
|
||||
(string libState, string image_lib) = liberatedStatus switch
|
||||
{
|
||||
LiberatedStatus.Liberated => ("Liberated", "green"),
|
||||
LiberatedStatus.PartialDownload => ("File has been at least\r\npartially downloaded", "yellow"),
|
||||
LiberatedStatus.NotLiberated => ("Book NOT downloaded", "red"),
|
||||
_ => throw new Exception("Unexpected liberation state")
|
||||
};
|
||||
|
||||
(string pdfState, string image_pdf) = pdfStatus switch
|
||||
{
|
||||
LiberatedStatus.Liberated => ("\r\nPDF downloaded", "_pdf_yes"),
|
||||
LiberatedStatus.NotLiberated => ("\r\nPDF NOT downloaded", "_pdf_no"),
|
||||
LiberatedStatus.Error => ("\r\nPDF downloaded ERROR", "_pdf_no"),
|
||||
null => ("", ""),
|
||||
_ => throw new Exception("Unexpected PDF state")
|
||||
};
|
||||
|
||||
var mouseoverText = libState + pdfState;
|
||||
|
||||
if (liberatedStatus == LiberatedStatus.NotLiberated ||
|
||||
liberatedStatus == LiberatedStatus.PartialDownload ||
|
||||
pdfStatus == LiberatedStatus.NotLiberated)
|
||||
mouseoverText += "\r\nClick to complete";
|
||||
|
||||
var buttonImage = (Bitmap)Properties.Resources.ResourceManager.GetObject($"liberate_{image_lib}{image_pdf}");
|
||||
|
||||
return (mouseoverText, buttonImage);
|
||||
}
|
||||
}
|
||||
}
|
||||
242
Source/LibationWinForms/grid/ProductsGrid.Designer.cs
generated
Normal file
242
Source/LibationWinForms/grid/ProductsGrid.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
namespace LibationWinForms
|
||||
{
|
||||
partial class ProductsGrid
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Component Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
|
||||
this.gridEntryBindingSource = new LibationWinForms.SyncBindingSource(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.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();
|
||||
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.AllowUserToResizeRows = 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.dataGridViewTextBoxColumn8,
|
||||
this.dataGridViewTextBoxColumn9,
|
||||
this.dataGridViewTextBoxColumn10,
|
||||
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(1510, 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 = 75;
|
||||
//
|
||||
// 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.Width = 200;
|
||||
//
|
||||
// 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;
|
||||
this.dataGridViewTextBoxColumn4.ToolTipText = "Recording Length";
|
||||
//
|
||||
// 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;
|
||||
//
|
||||
// ProductRating
|
||||
//
|
||||
this.dataGridViewTextBoxColumn8.DataPropertyName = "ProductRating";
|
||||
this.dataGridViewTextBoxColumn8.HeaderText = "Product Rating";
|
||||
this.dataGridViewTextBoxColumn8.Name = "ProductRating";
|
||||
this.dataGridViewTextBoxColumn8.ReadOnly = true;
|
||||
this.dataGridViewTextBoxColumn8.Width = 108;
|
||||
//
|
||||
// PurchaseDate
|
||||
//
|
||||
this.dataGridViewTextBoxColumn9.DataPropertyName = "PurchaseDate";
|
||||
this.dataGridViewTextBoxColumn9.HeaderText = "Purchase Date";
|
||||
this.dataGridViewTextBoxColumn9.Name = "PurchaseDate";
|
||||
this.dataGridViewTextBoxColumn9.ReadOnly = true;
|
||||
//
|
||||
// MyRating
|
||||
//
|
||||
this.dataGridViewTextBoxColumn10.DataPropertyName = "MyRating";
|
||||
this.dataGridViewTextBoxColumn10.HeaderText = "My Rating";
|
||||
this.dataGridViewTextBoxColumn10.Name = "MyRating";
|
||||
this.dataGridViewTextBoxColumn10.ReadOnly = true;
|
||||
this.dataGridViewTextBoxColumn10.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 = "Tags and Details";
|
||||
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(1510, 380);
|
||||
((System.ComponentModel.ISupportInitialize)(this.gridEntryBindingSource)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.gridEntryDataGridView)).EndInit();
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private LibationWinForms.SyncBindingSource gridEntryBindingSource;
|
||||
private System.Windows.Forms.DataGridView gridEntryDataGridView;
|
||||
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 dataGridViewTextBoxColumn8;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn9;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn10;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn11;
|
||||
private EditTagsDataGridViewImageButtonColumn dataGridViewImageButtonBoxColumn2;
|
||||
}
|
||||
}
|
||||
229
Source/LibationWinForms/grid/ProductsGrid.cs
Normal file
229
Source/LibationWinForms/grid/ProductsGrid.cs
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using ApplicationServices;
|
||||
using Dinah.Core;
|
||||
using Dinah.Core.DataBinding;
|
||||
using Dinah.Core.Threading;
|
||||
using Dinah.Core.Windows.Forms;
|
||||
using LibationWinForms.Dialogs;
|
||||
|
||||
namespace LibationWinForms
|
||||
{
|
||||
// INSTRUCTIONS TO UPDATE DATA_GRID_VIEW
|
||||
// - delete current DataGridView
|
||||
// - view > other windows > data sources
|
||||
// - refresh
|
||||
// OR
|
||||
// - Add New Data Source
|
||||
// Object. Next
|
||||
// LibationWinForms
|
||||
// AudibleDTO
|
||||
// GridEntry
|
||||
// - go to Design view
|
||||
// - click on Data Sources > ProductItem. dropdown: DataGridView
|
||||
// - drag/drop ProductItem on design surface
|
||||
// AS OF AUGUST 2021 THIS DOES NOT WORK IN VS2019 WITH .NET-5 PROJECTS
|
||||
|
||||
public partial class ProductsGrid : UserControl
|
||||
{
|
||||
public event EventHandler<int> VisibleCountChanged;
|
||||
|
||||
// alias
|
||||
private DataGridView _dataGridView => gridEntryDataGridView;
|
||||
|
||||
public ProductsGrid()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// sorting breaks filters. must reapply filters after sorting
|
||||
_dataGridView.Sorted += Filter;
|
||||
_dataGridView.CellContentClick += DataGridView_CellContentClick;
|
||||
|
||||
EnableDoubleBuffering();
|
||||
}
|
||||
private void EnableDoubleBuffering()
|
||||
{
|
||||
var propertyInfo = _dataGridView.GetType().GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
|
||||
|
||||
propertyInfo.SetValue(_dataGridView, true, null);
|
||||
}
|
||||
|
||||
#region Button controls
|
||||
|
||||
private async void DataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
case nameof(liveGridEntry.Liberate):
|
||||
await Liberate_Click(liveGridEntry);
|
||||
break;
|
||||
case nameof(liveGridEntry.DisplayTags):
|
||||
Details_Click(liveGridEntry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task Liberate_Click(GridEntry liveGridEntry)
|
||||
{
|
||||
var libraryBook = liveGridEntry.LibraryBook;
|
||||
|
||||
// liberated: open explorer to file
|
||||
if (libraryBook.Book.Audio_Exists)
|
||||
{
|
||||
var filePath = LibationFileManager.AudibleFileStorage.Audio.GetPath(libraryBook.Book.AudibleProductId);
|
||||
if (!Go.To.File(filePath))
|
||||
{
|
||||
var suffix = string.IsNullOrWhiteSpace(filePath) ? "" : $":\r\n{filePath}";
|
||||
MessageBox.Show($"File not found" + suffix);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// else: liberate
|
||||
await liveGridEntry.DownloadBook();
|
||||
}
|
||||
|
||||
private static void Details_Click(GridEntry liveGridEntry)
|
||||
{
|
||||
var bookDetailsForm = new BookDetailsDialog(liveGridEntry.LibraryBook);
|
||||
if (bookDetailsForm.ShowDialog() != DialogResult.OK)
|
||||
return;
|
||||
|
||||
liveGridEntry.BeginEdit();
|
||||
|
||||
liveGridEntry.DisplayTags = bookDetailsForm.NewTags;
|
||||
liveGridEntry.Liberate = (bookDetailsForm.BookLiberatedStatus, bookDetailsForm.PdfLiberatedStatus);
|
||||
|
||||
liveGridEntry.EndEdit();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region UI display functions
|
||||
|
||||
public int Count { get; private set; }
|
||||
|
||||
private SortableBindingList<GridEntry> bindingList;
|
||||
|
||||
public void Display()
|
||||
{
|
||||
var lib = DbContexts.GetLibrary_Flat_NoTracking();
|
||||
|
||||
Count = lib.Count;
|
||||
|
||||
// if no data. hide all columns. return
|
||||
if (!lib.Any())
|
||||
{
|
||||
for (var i = _dataGridView.ColumnCount - 1; i >= 0; i--)
|
||||
_dataGridView.Columns.RemoveAt(i);
|
||||
return;
|
||||
}
|
||||
|
||||
var orderedBooks = lib
|
||||
// default load order
|
||||
.OrderByDescending(lb => lb.DateAdded)
|
||||
//// more advanced example: sort by author, then series, then title
|
||||
//.OrderBy(lb => lb.Book.AuthorNames)
|
||||
// .ThenBy(lb => lb.Book.SeriesSortable)
|
||||
// .ThenBy(lb => lb.Book.TitleSortable)
|
||||
.ToList();
|
||||
|
||||
// BIND
|
||||
if (bindingList is null)
|
||||
bindToGrid(orderedBooks);
|
||||
else
|
||||
updateGrid(orderedBooks);
|
||||
|
||||
// FILTER
|
||||
Filter();
|
||||
}
|
||||
|
||||
private void bindToGrid(List<DataLayer.LibraryBook> orderedBooks)
|
||||
{
|
||||
bindingList = new SortableBindingList<GridEntry>(orderedBooks.Select(lb => toGridEntry(lb)));
|
||||
gridEntryBindingSource.DataSource = bindingList;
|
||||
}
|
||||
|
||||
private void updateGrid(List<DataLayer.LibraryBook> orderedBooks)
|
||||
{
|
||||
for (var i = orderedBooks.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var libraryBook = orderedBooks[i];
|
||||
var existingItem = bindingList.FirstOrDefault(i => i.AudibleProductId == libraryBook.Book.AudibleProductId);
|
||||
|
||||
// add new to top
|
||||
if (existingItem is null)
|
||||
bindingList.Insert(0, toGridEntry(libraryBook));
|
||||
// update existing
|
||||
else
|
||||
existingItem.UpdateLibraryBook(libraryBook);
|
||||
}
|
||||
|
||||
// remove deleted from grid. note: actual deletion from db must still occur via the RemoveBook feature. deleting from audible will not trigger this
|
||||
var oldIds = bindingList.Select(ge => ge.AudibleProductId).ToList();
|
||||
var newIds = orderedBooks.Select(lb => lb.Book.AudibleProductId).ToList();
|
||||
var remove = oldIds.Except(newIds).ToList();
|
||||
foreach (var id in remove)
|
||||
{
|
||||
var oldItem = bindingList.FirstOrDefault(ge => ge.AudibleProductId == id);
|
||||
if (oldItem is not null)
|
||||
bindingList.Remove(oldItem);
|
||||
}
|
||||
}
|
||||
|
||||
private GridEntry toGridEntry(DataLayer.LibraryBook libraryBook)
|
||||
{
|
||||
var entry = new GridEntry(libraryBook);
|
||||
entry.Committed += Filter;
|
||||
entry.LibraryBookUpdated += (sender, productId) => _dataGridView.InvalidateRow(_dataGridView.GetRowIdOfBoundItem((GridEntry)sender));
|
||||
return entry;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Filter
|
||||
|
||||
private string _filterSearchString;
|
||||
private void Filter(object _ = null, EventArgs __ = null) => Filter(_filterSearchString);
|
||||
public void Filter(string searchString)
|
||||
{
|
||||
_filterSearchString = searchString;
|
||||
|
||||
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 bindingContext = BindingContext[_dataGridView.DataSource];
|
||||
bindingContext.SuspendBinding();
|
||||
{
|
||||
this.UIThreadSync(() =>
|
||||
{
|
||||
for (var r = _dataGridView.RowCount - 1; r >= 0; r--)
|
||||
_dataGridView.Rows[r].Visible = productIds.Contains(getGridEntry(r).AudibleProductId);
|
||||
});
|
||||
}
|
||||
|
||||
// Causes repainting of the DataGridView
|
||||
bindingContext.ResumeBinding();
|
||||
VisibleCountChanged?.Invoke(this, _dataGridView.AsEnumerable().Count(r => r.Visible));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DataGridView Macro
|
||||
private GridEntry getGridEntry(int rowIndex) => _dataGridView.GetBoundItem<GridEntry>(rowIndex);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
64
Source/LibationWinForms/grid/ProductsGrid.resx
Normal file
64
Source/LibationWinForms/grid/ProductsGrid.resx
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<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:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="gridEntryBindingSource.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
</root>
|
||||
Loading…
Add table
Add a link
Reference in a new issue