Merge pull request #549 from Mbucari/master
Lots of Bug Fixes and 2 New Features.
BIN
Images/Plus Minus.psd
Normal file
@ -12,6 +12,7 @@ using DtoImporterService;
|
|||||||
using FileManager;
|
using FileManager;
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
using NPOI.OpenXmlFormats.Spreadsheet;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using static DtoImporterService.PerfLogger;
|
using static DtoImporterService.PerfLogger;
|
||||||
|
|
||||||
@ -171,11 +172,64 @@ namespace ApplicationServices
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<List<ImportItem>> scanAccountsAsync(Func<Account, Task<ApiExtended>> apiExtendedfunc, Account[] accounts, LibraryOptions libraryOptions)
|
public static async Task<int> ImportSingleToDbAsync(AudibleApi.Common.Item item, string accountId, string localeName)
|
||||||
|
{
|
||||||
|
ArgumentValidator.EnsureNotNull(item, "item");
|
||||||
|
ArgumentValidator.EnsureNotNull(accountId, "accountId");
|
||||||
|
ArgumentValidator.EnsureNotNull(localeName, "localeName");
|
||||||
|
|
||||||
|
var importItem = new ImportItem
|
||||||
|
{
|
||||||
|
DtoItem = item,
|
||||||
|
AccountId = accountId,
|
||||||
|
LocaleName = localeName
|
||||||
|
};
|
||||||
|
|
||||||
|
var importItems = new List<ImportItem> { importItem };
|
||||||
|
var validator = new LibraryValidator();
|
||||||
|
var exceptions = validator.Validate(importItems.Select(i => i.DtoItem));
|
||||||
|
|
||||||
|
if (exceptions?.Any() ?? false)
|
||||||
|
{
|
||||||
|
Log.Logger.Error(new AggregateException(exceptions), "Error validating library book. {@DebugInfo}", new { item, accountId, localeName });
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var context = DbContexts.GetContext();
|
||||||
|
|
||||||
|
var bookImporter = new BookImporter(context);
|
||||||
|
await Task.Run(() => bookImporter.Import(importItems));
|
||||||
|
var book = await Task.Run(() => context.LibraryBooks.FirstOrDefault(lb => lb.Book.AudibleProductId == importItem.DtoItem.ProductId));
|
||||||
|
|
||||||
|
if (book is null)
|
||||||
|
{
|
||||||
|
book = new LibraryBook(bookImporter.Cache[importItem.DtoItem.ProductId], importItem.DtoItem.DateAdded, importItem.AccountId);
|
||||||
|
context.LibraryBooks.Add(book);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
book.AbsentFromLastScan = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int qtyChanged = await Task.Run(() => SaveContext(context));
|
||||||
|
if (qtyChanged > 0)
|
||||||
|
await Task.Run(finalizeLibrarySizeChange);
|
||||||
|
return qtyChanged;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Logger.Error(ex, "Error adding single library book to DB. {@DebugInfo}", new { item, accountId, localeName });
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<List<ImportItem>> scanAccountsAsync(Func<Account, Task<ApiExtended>> apiExtendedfunc, Account[] accounts, LibraryOptions libraryOptions)
|
||||||
{
|
{
|
||||||
var tasks = new List<Task<List<ImportItem>>>();
|
var tasks = new List<Task<List<ImportItem>>>();
|
||||||
|
|
||||||
await using LogArchiver archiver
|
await using LogArchiver archiver
|
||||||
= Log.Logger.IsDebugEnabled()
|
= Log.Logger.IsDebugEnabled()
|
||||||
? new LogArchiver(System.IO.Path.Combine(Configuration.Instance.LibationFiles, "LibraryScans.zip"))
|
? new LogArchiver(System.IO.Path.Combine(Configuration.Instance.LibationFiles, "LibraryScans.zip"))
|
||||||
: default;
|
: default;
|
||||||
@ -184,16 +238,24 @@ namespace ApplicationServices
|
|||||||
|
|
||||||
foreach (var account in accounts)
|
foreach (var account in accounts)
|
||||||
{
|
{
|
||||||
// get APIs in serial b/c of logins. do NOT move inside of parallel (Task.WhenAll)
|
try
|
||||||
var apiExtended = await apiExtendedfunc(account);
|
{
|
||||||
|
// get APIs in serial b/c of logins. do NOT move inside of parallel (Task.WhenAll)
|
||||||
|
var apiExtended = await apiExtendedfunc(account);
|
||||||
|
|
||||||
// add scanAccountAsync as a TASK: do not await
|
// add scanAccountAsync as a TASK: do not await
|
||||||
tasks.Add(scanAccountAsync(apiExtended, account, libraryOptions, archiver));
|
tasks.Add(scanAccountAsync(apiExtended, account, libraryOptions, archiver));
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
//Catch to allow other accounts to continue scanning.
|
||||||
|
Log.Logger.Error(ex, "Failed to scan account");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// import library in parallel
|
// import library in parallel
|
||||||
var arrayOfLists = await Task.WhenAll(tasks);
|
var arrayOfLists = await Task.WhenAll(tasks);
|
||||||
var importItems = arrayOfLists.SelectMany(a => a).ToList();
|
var importItems = arrayOfLists.SelectMany(a => a).ToList();
|
||||||
return importItems;
|
return importItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,26 +270,43 @@ namespace ApplicationServices
|
|||||||
|
|
||||||
logTime($"pre scanAccountAsync {account.AccountName}");
|
logTime($"pre scanAccountAsync {account.AccountName}");
|
||||||
|
|
||||||
var dtoItems = await apiExtended.GetLibraryValidatedAsync(libraryOptions, Configuration.Instance.ImportEpisodes);
|
try
|
||||||
|
|
||||||
if (archiver is not null)
|
|
||||||
{
|
{
|
||||||
var fileName = $"{DateTime.Now:u} {account.MaskedLogEntry}.json";
|
var dtoItems = await apiExtended.GetLibraryValidatedAsync(libraryOptions, Configuration.Instance.ImportEpisodes);
|
||||||
var items = await Task.Run(() => JArray.FromObject(dtoItems.Select(i => i.SourceJson)));
|
|
||||||
|
|
||||||
var scanFile = new JObject
|
logTime($"post scanAccountAsync {account.AccountName} qty: {dtoItems.Count}");
|
||||||
{
|
|
||||||
{ "Account", account.MaskedLogEntry },
|
|
||||||
{ "ScannedDateTime", DateTime.Now.ToString("u") },
|
|
||||||
{ "Items", items}
|
|
||||||
};
|
|
||||||
|
|
||||||
await archiver.AddFileAsync(fileName, scanFile);
|
await logDtoItemsAsync(dtoItems);
|
||||||
|
|
||||||
|
return dtoItems.Select(d => new ImportItem { DtoItem = d, AccountId = account.AccountId, LocaleName = account.Locale?.Name }).ToList();
|
||||||
|
}
|
||||||
|
catch(ImportValidationException ex)
|
||||||
|
{
|
||||||
|
await logDtoItemsAsync(ex.Items, ex.InnerExceptions.ToArray());
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task logDtoItemsAsync(IEnumerable<AudibleApi.Common.Item> dtoItems, IEnumerable<Exception> exceptions = null)
|
||||||
|
{
|
||||||
|
if (archiver is not null)
|
||||||
|
{
|
||||||
|
var fileName = $"{DateTime.Now:u} {account.MaskedLogEntry}.json";
|
||||||
|
var items = await Task.Run(() => JArray.FromObject(dtoItems.Select(i => i.SourceJson)));
|
||||||
|
|
||||||
|
var scanFile = new JObject
|
||||||
|
{
|
||||||
|
{ "Account", account.MaskedLogEntry },
|
||||||
|
{ "ScannedDateTime", DateTime.Now.ToString("u") },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (exceptions?.Any() is true)
|
||||||
|
scanFile.Add("Exceptions", JArray.FromObject(exceptions));
|
||||||
|
|
||||||
|
scanFile.Add("Items", items);
|
||||||
|
|
||||||
|
await archiver.AddFileAsync(fileName, scanFile);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logTime($"post scanAccountAsync {account.AccountName} qty: {dtoItems.Count}");
|
|
||||||
|
|
||||||
return dtoItems.Select(d => new ImportItem { DtoItem = d, AccountId = account.AccountId, LocaleName = account.Locale?.Name }).ToList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<int> importIntoDbAsync(List<ImportItem> importItems)
|
private static async Task<int> importIntoDbAsync(List<ImportItem> importItems)
|
||||||
|
|||||||
@ -149,7 +149,7 @@ namespace AudibleUtilities
|
|||||||
foreach (var parent in items.Where(i => i.IsSeriesParent))
|
foreach (var parent in items.Where(i => i.IsSeriesParent))
|
||||||
{
|
{
|
||||||
var children = items.Where(i => i.IsEpisodes && i.Relationships.Any(r => r.Asin == parent.Asin));
|
var children = items.Where(i => i.IsEpisodes && i.Relationships.Any(r => r.Asin == parent.Asin));
|
||||||
setSeries(parent, children);
|
SetSeries(parent, children);
|
||||||
}
|
}
|
||||||
|
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
@ -157,27 +157,13 @@ namespace AudibleUtilities
|
|||||||
Serilog.Log.Logger.Information("Completed indexing series episodes after {elappsed_ms} ms.", sw.ElapsedMilliseconds);
|
Serilog.Log.Logger.Information("Completed indexing series episodes after {elappsed_ms} ms.", sw.ElapsedMilliseconds);
|
||||||
Serilog.Log.Logger.Information($"Completed library scan in {totalTime.TotalMilliseconds:F0} ms.");
|
Serilog.Log.Logger.Information($"Completed library scan in {totalTime.TotalMilliseconds:F0} ms.");
|
||||||
|
|
||||||
var validators = new List<IValidator>();
|
var allExceptions = IValidator.GetAllValidators().SelectMany(v => v.Validate(items));
|
||||||
validators.AddRange(getValidators());
|
if (allExceptions?.Any() is true)
|
||||||
foreach (var v in validators)
|
throw new ImportValidationException(items, allExceptions);
|
||||||
{
|
|
||||||
var exceptions = v.Validate(items);
|
|
||||||
if (exceptions is not null && exceptions.Any())
|
|
||||||
throw new AggregateException(exceptions);
|
|
||||||
}
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<IValidator> getValidators()
|
|
||||||
{
|
|
||||||
var type = typeof(IValidator);
|
|
||||||
var types = AppDomain.CurrentDomain.GetAssemblies()
|
|
||||||
.SelectMany(s => s.GetTypes())
|
|
||||||
.Where(p => type.IsAssignableFrom(p) && !p.IsInterface);
|
|
||||||
|
|
||||||
return types.Select(t => Activator.CreateInstance(t) as IValidator).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
#region episodes and podcasts
|
#region episodes and podcasts
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -232,8 +218,11 @@ namespace AudibleUtilities
|
|||||||
finally { semaphore.Release(); }
|
finally { semaphore.Release(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setSeries(Item parent, IEnumerable<Item> children)
|
public static void SetSeries(Item parent, IEnumerable<Item> children)
|
||||||
{
|
{
|
||||||
|
ArgumentValidator.EnsureNotNull(parent, nameof(parent));
|
||||||
|
ArgumentValidator.EnsureNotNull(children, nameof(children));
|
||||||
|
|
||||||
//A series parent will always have exactly 1 Series
|
//A series parent will always have exactly 1 Series
|
||||||
parent.Series = new[]
|
parent.Series = new[]
|
||||||
{
|
{
|
||||||
@ -246,7 +235,15 @@ namespace AudibleUtilities
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (parent.PurchaseDate == default)
|
if (parent.PurchaseDate == default)
|
||||||
parent.PurchaseDate = children.Select(c => c.PurchaseDate).Order().First();
|
{
|
||||||
|
parent.PurchaseDate = children.Select(c => c.PurchaseDate).Order().FirstOrDefault(d => d != default);
|
||||||
|
|
||||||
|
if (parent.PurchaseDate == default)
|
||||||
|
{
|
||||||
|
Serilog.Log.Logger.Warning("{series} doesn't have a purchase date. Using UtcNow", parent);
|
||||||
|
parent.PurchaseDate = DateTimeOffset.UtcNow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var child in children)
|
foreach (var child in children)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -8,7 +8,18 @@ namespace AudibleUtilities
|
|||||||
public interface IValidator
|
public interface IValidator
|
||||||
{
|
{
|
||||||
IEnumerable<Exception> Validate(IEnumerable<Item> items);
|
IEnumerable<Exception> Validate(IEnumerable<Item> items);
|
||||||
|
|
||||||
|
public static IValidator[] GetAllValidators()
|
||||||
|
=> new IValidator[]
|
||||||
|
{
|
||||||
|
new LibraryValidator(),
|
||||||
|
new BookValidator(),
|
||||||
|
new CategoryValidator(),
|
||||||
|
new ContributorValidator(),
|
||||||
|
new SeriesValidator(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public class LibraryValidator : IValidator
|
public class LibraryValidator : IValidator
|
||||||
{
|
{
|
||||||
public IEnumerable<Exception> Validate(IEnumerable<Item> items)
|
public IEnumerable<Exception> Validate(IEnumerable<Item> items)
|
||||||
|
|||||||
15
Source/AudibleUtilities/ImportValidationException.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using AudibleApi.Common;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace AudibleUtilities
|
||||||
|
{
|
||||||
|
public class ImportValidationException : AggregateException
|
||||||
|
{
|
||||||
|
public List<Item> Items { get; }
|
||||||
|
public ImportValidationException(List<Item> items, IEnumerable<Exception> exceptions) : base(exceptions)
|
||||||
|
{
|
||||||
|
Items = items;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -25,6 +25,8 @@ namespace DataLayer
|
|||||||
Account = account;
|
Account = account;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString() => $"{DateAdded:d} {Book}";
|
public void SetAccount(string account) => Account = account;
|
||||||
|
|
||||||
|
public override string ToString() => $"{DateAdded:d} {Book}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,9 +18,9 @@ namespace DataLayer
|
|||||||
/// <summary>True if exists and IsLiberated. Else false</summary>
|
/// <summary>True if exists and IsLiberated. Else false</summary>
|
||||||
public static bool PDF_Exists(this Book book) => book.UserDefinedItem.PdfStatus == LiberatedStatus.Liberated;
|
public static bool PDF_Exists(this Book book) => book.UserDefinedItem.PdfStatus == LiberatedStatus.Liberated;
|
||||||
|
|
||||||
public static string SeriesSortable(this Book book) => Formatters.GetSortName(book.SeriesNames());
|
public static string SeriesSortable(this Book book) => Formatters.GetSortName(book.SeriesNames(true));
|
||||||
public static bool HasPdf(this Book book) => book.Supplements.Any();
|
public static bool HasPdf(this Book book) => book.Supplements.Any();
|
||||||
public static string SeriesNames(this Book book)
|
public static string SeriesNames(this Book book, bool includeIndex = false)
|
||||||
{
|
{
|
||||||
if (book.SeriesLink is null)
|
if (book.SeriesLink is null)
|
||||||
return "";
|
return "";
|
||||||
@ -28,7 +28,7 @@ namespace DataLayer
|
|||||||
// first: alphabetical by name
|
// first: alphabetical by name
|
||||||
var withNames = book.SeriesLink
|
var withNames = book.SeriesLink
|
||||||
.Where(s => !string.IsNullOrWhiteSpace(s.Series.Name))
|
.Where(s => !string.IsNullOrWhiteSpace(s.Series.Name))
|
||||||
.Select(s => s.Series.Name)
|
.Select(getSeriesNameString)
|
||||||
.OrderBy(a => a)
|
.OrderBy(a => a)
|
||||||
.ToList();
|
.ToList();
|
||||||
// then un-named are alpha by series id
|
// then un-named are alpha by series id
|
||||||
@ -40,7 +40,12 @@ namespace DataLayer
|
|||||||
|
|
||||||
var all = withNames.Union(nullNames).ToList();
|
var all = withNames.Union(nullNames).ToList();
|
||||||
return string.Join(", ", all);
|
return string.Join(", ", all);
|
||||||
}
|
|
||||||
|
string getSeriesNameString(SeriesBook sb)
|
||||||
|
=> includeIndex && !string.IsNullOrWhiteSpace(sb.Order) && sb.Order != "-1"
|
||||||
|
? $"{sb.Series.Name} (#{sb.Order})"
|
||||||
|
: sb.Series.Name;
|
||||||
|
}
|
||||||
public static string[] CategoriesNames(this Book book)
|
public static string[] CategoriesNames(this Book book)
|
||||||
=> book.Category is null ? new string[0]
|
=> book.Category is null ? new string[0]
|
||||||
: book.Category.ParentCategory is null ? new[] { book.Category.Name }
|
: book.Category.ParentCategory is null ? new[] { book.Category.Name }
|
||||||
|
|||||||
@ -41,32 +41,41 @@ namespace DtoImporterService
|
|||||||
//
|
//
|
||||||
// CURRENT SOLUTION: don't re-insert
|
// CURRENT SOLUTION: don't re-insert
|
||||||
|
|
||||||
var newItems = importItems
|
var existingEntries = DbContext.LibraryBooks.AsEnumerable().Where(l => l.Book is not null).ToDictionary(l => l.Book.AudibleProductId);
|
||||||
.ExceptBy(DbContext.LibraryBooks.Select(lb => lb.Book.AudibleProductId), imp => imp.DtoItem.ProductId)
|
var hash = ToDictionarySafe(importItems, dto => dto.DtoItem.ProductId, tieBreak);
|
||||||
.ToList();
|
int qtyNew = 0;
|
||||||
|
|
||||||
// if 2 accounts try to import the same book in the same transaction: error since we're only tracking and pulling by asin.
|
foreach (var item in hash.Values)
|
||||||
// just use the first
|
|
||||||
var hash = newItems.ToDictionarySafe(dto => dto.DtoItem.ProductId);
|
|
||||||
foreach (var kvp in hash)
|
|
||||||
{
|
{
|
||||||
var newItem = kvp.Value;
|
if (existingEntries.TryGetValue(item.DtoItem.ProductId, out LibraryBook existing))
|
||||||
|
|
||||||
var libraryBook = new LibraryBook(
|
|
||||||
bookImporter.Cache[newItem.DtoItem.ProductId],
|
|
||||||
newItem.DtoItem.DateAdded,
|
|
||||||
newItem.AccountId)
|
|
||||||
{
|
{
|
||||||
AbsentFromLastScan = isPlusTitleUnavailable(newItem)
|
if (existing.Account != item.AccountId)
|
||||||
};
|
{
|
||||||
|
//Book is absent from the existing LibraryBook's account. Use the alternate account.
|
||||||
|
existing.SetAccount(item.AccountId);
|
||||||
|
}
|
||||||
|
|
||||||
try
|
existing.AbsentFromLastScan = isPlusTitleUnavailable(item);
|
||||||
{
|
|
||||||
DbContext.LibraryBooks.Add(libraryBook);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
else
|
||||||
{
|
{
|
||||||
Serilog.Log.Logger.Error(ex, "Error adding library book. {@DebugInfo}", new { libraryBook.Book, libraryBook.Account });
|
var libraryBook = new LibraryBook(
|
||||||
|
bookImporter.Cache[item.DtoItem.ProductId],
|
||||||
|
item.DtoItem.DateAdded,
|
||||||
|
item.AccountId)
|
||||||
|
{
|
||||||
|
AbsentFromLastScan = isPlusTitleUnavailable(item)
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DbContext.LibraryBooks.Add(libraryBook);
|
||||||
|
qtyNew++;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Serilog.Log.Logger.Error(ex, "Error adding library book. {@DebugInfo}", new { libraryBook.Book, libraryBook.Account });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,20 +86,28 @@ namespace DtoImporterService
|
|||||||
foreach (var nullBook in DbContext.LibraryBooks.AsEnumerable().Where(lb => lb.Book is null && lb.Account.In(scannedAccounts)))
|
foreach (var nullBook in DbContext.LibraryBooks.AsEnumerable().Where(lb => lb.Book is null && lb.Account.In(scannedAccounts)))
|
||||||
nullBook.AbsentFromLastScan = true;
|
nullBook.AbsentFromLastScan = true;
|
||||||
|
|
||||||
//Join importItems on LibraryBooks before iterating over LibraryBooks to avoid
|
|
||||||
//quadratic complexity caused by searching all of importItems for each LibraryBook.
|
|
||||||
//Join uses hashing, so complexity should approach O(N) instead of O(N^2).
|
|
||||||
var items_lbs
|
|
||||||
= importItems
|
|
||||||
.Join(DbContext.LibraryBooks, o => (o.AccountId, o.DtoItem.ProductId), i => (i.Account, i.Book?.AudibleProductId), (o, i) => (o, i));
|
|
||||||
|
|
||||||
foreach ((ImportItem item, LibraryBook lb) in items_lbs)
|
|
||||||
lb.AbsentFromLastScan = isPlusTitleUnavailable(item);
|
|
||||||
|
|
||||||
var qtyNew = hash.Count;
|
|
||||||
return qtyNew;
|
return qtyNew;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Dictionary<TKey, TSource> ToDictionarySafe<TKey, TSource>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TSource, TSource> tieBreaker)
|
||||||
|
{
|
||||||
|
var dictionary = new Dictionary<TKey, TSource>();
|
||||||
|
|
||||||
|
foreach (TSource newItem in source)
|
||||||
|
{
|
||||||
|
TKey key = keySelector(newItem);
|
||||||
|
|
||||||
|
dictionary[key]
|
||||||
|
= dictionary.TryGetValue(key, out TSource existingItem)
|
||||||
|
? tieBreaker(existingItem, newItem)
|
||||||
|
: newItem;
|
||||||
|
}
|
||||||
|
return dictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ImportItem tieBreak(ImportItem item1, ImportItem item2)
|
||||||
|
=> isPlusTitleUnavailable(item1) && !isPlusTitleUnavailable(item2) ? item2 : item1;
|
||||||
|
|
||||||
private static bool isPlusTitleUnavailable(ImportItem item)
|
private static bool isPlusTitleUnavailable(ImportItem item)
|
||||||
=> item.DtoItem.IsAyce is true
|
=> item.DtoItem.IsAyce is true
|
||||||
&& item.DtoItem.Plans?.Any(p => p.IsAyce) is not true;
|
&& item.DtoItem.Plans?.Any(p => p.IsAyce) is not true;
|
||||||
|
|||||||
@ -7,6 +7,6 @@
|
|||||||
</Application.DataTemplates>
|
</Application.DataTemplates>
|
||||||
|
|
||||||
<Application.Styles>
|
<Application.Styles>
|
||||||
<FluentTheme Mode="Light"/>
|
<FluentTheme/>
|
||||||
</Application.Styles>
|
</Application.Styles>
|
||||||
</Application>
|
</Application>
|
||||||
|
|||||||
@ -66,13 +66,13 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
||||||
<PackageReference Include="Avalonia" Version="11.0.0-preview4" />
|
<PackageReference Include="Avalonia" Version="11.0.0-preview6" />
|
||||||
<PackageReference Include="Avalonia.Desktop" Version="11.0.0-preview4" />
|
<PackageReference Include="Avalonia.Desktop" Version="11.0.0-preview6" />
|
||||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.0-preview4" />
|
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.0-preview6" />
|
||||||
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-preview4" />
|
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-preview6" />
|
||||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.0-preview4" />
|
<PackageReference Include="Avalonia.Controls.ItemsRepeater" Version="11.0.0-preview6" />
|
||||||
<PackageReference Include="XamlNameReferenceGenerator" Version="1.6.1" />
|
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.0-preview6" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\HangoverBase\HangoverBase.csproj" />
|
<ProjectReference Include="..\HangoverBase\HangoverBase.csproj" />
|
||||||
|
|||||||
@ -7,7 +7,7 @@ namespace HangoverAvalonia
|
|||||||
{
|
{
|
||||||
public class ViewLocator : IDataTemplate
|
public class ViewLocator : IDataTemplate
|
||||||
{
|
{
|
||||||
public IControl Build(object data)
|
public Control Build(object data)
|
||||||
{
|
{
|
||||||
var name = data.GetType().FullName!.Replace("ViewModel", "View");
|
var name = data.GetType().FullName!.Replace("ViewModel", "View");
|
||||||
var type = Type.GetType(name);
|
var type = Type.GetType(name);
|
||||||
|
|||||||
@ -64,7 +64,8 @@ namespace HangoverBase
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var sql = _commands.SqlInput().Trim();
|
var sql = _commands.SqlInput()?.Trim();
|
||||||
|
if (sql is null) return;
|
||||||
|
|
||||||
#region // explanation
|
#region // explanation
|
||||||
// Routing statements to non-query is a convenience.
|
// Routing statements to non-query is a convenience.
|
||||||
|
|||||||
@ -7,11 +7,66 @@
|
|||||||
<local:ViewLocator/>
|
<local:ViewLocator/>
|
||||||
</Application.DataTemplates>
|
</Application.DataTemplates>
|
||||||
|
|
||||||
<Application.Styles>
|
<Application.Resources>
|
||||||
<FluentTheme Mode="Light"/>
|
|
||||||
<StyleInclude Source="avares://Avalonia.Themes.Fluent/FluentLight.xaml"/>
|
<ResourceDictionary>
|
||||||
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Accents/BaseLight.xaml"/>
|
<ResourceDictionary.ThemeDictionaries>
|
||||||
<StyleInclude Source="/Assets/DataGridTheme.xaml"/>
|
<ResourceDictionary x:Key="Light">
|
||||||
<StyleInclude Source="/Assets/LibationStyles.xaml"/>
|
<SolidColorBrush x:Key="SeriesEntryGridBackgroundBrush" Opacity="0.3" Color="#abffab" />
|
||||||
|
<SolidColorBrush x:Key="ProcessQueueBookFailedBrush" Color="LightCoral" />
|
||||||
|
<SolidColorBrush x:Key="ProcessQueueBookCompletedBrush" Color="PaleGreen" />
|
||||||
|
<SolidColorBrush x:Key="ProcessQueueBookCancelledBrush" Color="Khaki" />
|
||||||
|
<SolidColorBrush x:Key="HyperlinkNew" Color="Blue" />
|
||||||
|
<SolidColorBrush x:Key="HyperlinkVisited" Color="Purple" />
|
||||||
|
<SolidColorBrush x:Key="ProcessQueueBookDefaultBrush" Color="White" />
|
||||||
|
<SolidColorBrush x:Key="SystemOpaqueBase" Color="White" />
|
||||||
|
|
||||||
|
<SolidColorBrush x:Key="CancelRed" Color="FireBrick" />
|
||||||
|
<SolidColorBrush x:Key="IconFill" Color="#231F20" />
|
||||||
|
<SolidColorBrush x:Key="StoplightRed" Color="#F06060" />
|
||||||
|
<SolidColorBrush x:Key="StoplightYellow" Color="#F0E160" />
|
||||||
|
<SolidColorBrush x:Key="StoplightGreen" Color="#70FA70" />
|
||||||
|
|
||||||
|
<SolidColorBrush x:Key="DisabledGrayBrush" Opacity="0.4" Color="{StaticResource SystemChromeMediumColor}" />
|
||||||
|
|
||||||
|
</ResourceDictionary>
|
||||||
|
<ResourceDictionary x:Key="Dark">
|
||||||
|
<SolidColorBrush x:Key="SeriesEntryGridBackgroundBrush" Opacity="0.3" Color="#bed2fa" />
|
||||||
|
<SolidColorBrush x:Key="ProcessQueueBookFailedBrush" Color="#502727" />
|
||||||
|
<SolidColorBrush x:Key="ProcessQueueBookCompletedBrush" Color="#1c3e20" />
|
||||||
|
<SolidColorBrush x:Key="ProcessQueueBookCancelledBrush" Color="#4e4b15" />
|
||||||
|
<SolidColorBrush x:Key="HyperlinkNew" Color="CornflowerBlue" />
|
||||||
|
<SolidColorBrush x:Key="HyperlinkVisited" Color="Orchid" />
|
||||||
|
<SolidColorBrush x:Key="ProcessQueueBookDefaultBrush" Color="Black" />
|
||||||
|
<SolidColorBrush x:Key="SystemOpaqueBase" Color="Black" />
|
||||||
|
|
||||||
|
<SolidColorBrush x:Key="CancelRed" Color="#802727" />
|
||||||
|
<SolidColorBrush x:Key="IconFill" Color="#DCE0DF" />
|
||||||
|
<SolidColorBrush x:Key="StoplightRed" Color="#5F0707" />
|
||||||
|
<SolidColorBrush x:Key="StoplightYellow" Color="#5F5B1A" />
|
||||||
|
<SolidColorBrush x:Key="StoplightGreen" Color="#174E15" />
|
||||||
|
|
||||||
|
|
||||||
|
<SolidColorBrush x:Key="DisabledGrayBrush" Opacity="0.4" Color="{StaticResource SystemChromeMediumColor}" />
|
||||||
|
|
||||||
|
</ResourceDictionary>
|
||||||
|
</ResourceDictionary.ThemeDictionaries>
|
||||||
|
|
||||||
|
</ResourceDictionary>
|
||||||
|
</Application.Resources>
|
||||||
|
|
||||||
|
<Application.Styles>
|
||||||
|
<FluentTheme />
|
||||||
|
<StyleInclude Source="avares://Avalonia.Themes.Fluent/FluentTheme.xaml"/>
|
||||||
|
<StyleInclude Source="/Assets/DataGridFluentTheme.xaml"/>
|
||||||
|
<StyleInclude Source="/Assets/LibationVectorIcons.xaml"/>
|
||||||
|
|
||||||
|
<Style Selector="TextBox[IsReadOnly=true]">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource SystemControlBackgroundBaseLowBrush}" />
|
||||||
|
<Setter Property="CaretBrush" Value="{DynamicResource SystemControlTransparentBrush}" />
|
||||||
|
<Style Selector="^ /template/ Border#PART_BorderElement">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource SystemControlBackgroundBaseLowBrush}" />
|
||||||
|
</Style>
|
||||||
|
</Style>
|
||||||
</Application.Styles>
|
</Application.Styles>
|
||||||
</Application>
|
</Application>
|
||||||
@ -12,6 +12,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using ApplicationServices;
|
using ApplicationServices;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Styling;
|
||||||
|
|
||||||
namespace LibationAvalonia
|
namespace LibationAvalonia
|
||||||
{
|
{
|
||||||
@ -23,6 +24,7 @@ namespace LibationAvalonia
|
|||||||
public static IBrush ProcessQueueBookCancelledBrush { get; private set; }
|
public static IBrush ProcessQueueBookCancelledBrush { get; private set; }
|
||||||
public static IBrush ProcessQueueBookDefaultBrush { get; private set; }
|
public static IBrush ProcessQueueBookDefaultBrush { get; private set; }
|
||||||
public static IBrush SeriesEntryGridBackgroundBrush { get; private set; }
|
public static IBrush SeriesEntryGridBackgroundBrush { get; private set; }
|
||||||
|
public static IBrush HyperlinkVisited { get; private set; }
|
||||||
|
|
||||||
public static IAssetLoader AssetLoader { get; private set; }
|
public static IAssetLoader AssetLoader { get; private set; }
|
||||||
|
|
||||||
@ -214,6 +216,10 @@ namespace LibationAvalonia
|
|||||||
|
|
||||||
private static void ShowMainWindow(IClassicDesktopStyleApplicationLifetime desktop)
|
private static void ShowMainWindow(IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
{
|
{
|
||||||
|
Current.RequestedThemeVariant = Configuration.Instance.GetString(propertyName: nameof(ThemeVariant)) is "Dark" ? ThemeVariant.Dark : ThemeVariant.Light;
|
||||||
|
|
||||||
|
//Reload colors for current theme
|
||||||
|
LoadStyles();
|
||||||
var mainWindow = new MainWindow();
|
var mainWindow = new MainWindow();
|
||||||
desktop.MainWindow = MainWindow = mainWindow;
|
desktop.MainWindow = MainWindow = mainWindow;
|
||||||
mainWindow.RestoreSizeAndLocation(Configuration.Instance);
|
mainWindow.RestoreSizeAndLocation(Configuration.Instance);
|
||||||
@ -227,8 +233,9 @@ namespace LibationAvalonia
|
|||||||
ProcessQueueBookFailedBrush = AvaloniaUtils.GetBrushFromResources("ProcessQueueBookFailedBrush");
|
ProcessQueueBookFailedBrush = AvaloniaUtils.GetBrushFromResources("ProcessQueueBookFailedBrush");
|
||||||
ProcessQueueBookCompletedBrush = AvaloniaUtils.GetBrushFromResources("ProcessQueueBookCompletedBrush");
|
ProcessQueueBookCompletedBrush = AvaloniaUtils.GetBrushFromResources("ProcessQueueBookCompletedBrush");
|
||||||
ProcessQueueBookCancelledBrush = AvaloniaUtils.GetBrushFromResources("ProcessQueueBookCancelledBrush");
|
ProcessQueueBookCancelledBrush = AvaloniaUtils.GetBrushFromResources("ProcessQueueBookCancelledBrush");
|
||||||
ProcessQueueBookDefaultBrush = AvaloniaUtils.GetBrushFromResources("ProcessQueueBookDefaultBrush");
|
|
||||||
SeriesEntryGridBackgroundBrush = AvaloniaUtils.GetBrushFromResources("SeriesEntryGridBackgroundBrush");
|
SeriesEntryGridBackgroundBrush = AvaloniaUtils.GetBrushFromResources("SeriesEntryGridBackgroundBrush");
|
||||||
|
ProcessQueueBookDefaultBrush = AvaloniaUtils.GetBrushFromResources("ProcessQueueBookDefaultBrush");
|
||||||
|
HyperlinkVisited = AvaloniaUtils.GetBrushFromResources(nameof(HyperlinkVisited));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 469 B |
|
Before Width: | Height: | Size: 455 B |
588
Source/LibationAvalonia/Assets/DataGridFluentTheme.xaml
Normal file
@ -0,0 +1,588 @@
|
|||||||
|
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:collections="using:Avalonia.Collections">
|
||||||
|
<Styles.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<ResourceDictionary.ThemeDictionaries>
|
||||||
|
<ResourceDictionary x:Key="Dark">
|
||||||
|
<SolidColorBrush x:Key="DataGridColumnHeaderForegroundBrush" Color="{DynamicResource SystemBaseMediumColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridColumnHeaderBackgroundBrush" Color="{DynamicResource SystemAltHighColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridColumnHeaderHoveredBackgroundBrush" Color="{DynamicResource SystemListLowColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridColumnHeaderPressedBackgroundBrush" Color="{DynamicResource SystemListMediumColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridColumnHeaderDraggedBackgroundBrush" Color="{DynamicResource SystemChromeMediumLowColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridRowGroupHeaderBackgroundBrush" Color="{DynamicResource SystemChromeMediumColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridRowGroupHeaderPressedBackgroundBrush" Color="{DynamicResource SystemListMediumColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridRowGroupHeaderForegroundBrush" Color="{DynamicResource SystemBaseHighColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridRowGroupHeaderHoveredBackgroundBrush" Color="{DynamicResource SystemListLowColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridRowHoveredBackgroundColor" Color="{DynamicResource SystemListLowColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridRowInvalidBrush" Color="{DynamicResource SystemErrorTextColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridCellFocusVisualPrimaryBrush" Color="{DynamicResource SystemBaseHighColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridCellFocusVisualSecondaryBrush" Color="{DynamicResource SystemAltMediumColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridCellInvalidBrush" Color="{DynamicResource SystemErrorTextColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridGridLinesBrush" Opacity="0.4" Color="{DynamicResource SystemBaseMediumLowColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridDetailsPresenterBackgroundBrush" Color="{DynamicResource SystemChromeMediumLowColor}" />
|
||||||
|
|
||||||
|
</ResourceDictionary>
|
||||||
|
<ResourceDictionary x:Key="Default">
|
||||||
|
<SolidColorBrush x:Key="DataGridColumnHeaderForegroundBrush" Color="{DynamicResource SystemBaseMediumColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridColumnHeaderBackgroundBrush" Color="{DynamicResource SystemAltHighColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridColumnHeaderHoveredBackgroundBrush" Color="{DynamicResource SystemListLowColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridColumnHeaderPressedBackgroundBrush" Color="{DynamicResource SystemListMediumColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridColumnHeaderDraggedBackgroundBrush" Color="{DynamicResource SystemChromeMediumLowColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridRowGroupHeaderBackgroundBrush" Color="{DynamicResource SystemChromeMediumColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridRowGroupHeaderPressedBackgroundBrush" Color="{DynamicResource SystemListMediumColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridRowGroupHeaderForegroundBrush" Color="{DynamicResource SystemBaseHighColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridRowGroupHeaderHoveredBackgroundBrush" Color="{DynamicResource SystemListLowColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridRowHoveredBackgroundColor" Color="{DynamicResource SystemListLowColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridRowInvalidBrush" Color="{DynamicResource SystemErrorTextColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridCellFocusVisualPrimaryBrush" Color="{DynamicResource SystemBaseHighColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridCellFocusVisualSecondaryBrush" Color="{DynamicResource SystemAltMediumColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridCellInvalidBrush" Color="{DynamicResource SystemErrorTextColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridGridLinesBrush" Opacity="0.4" Color="{DynamicResource SystemBaseMediumLowColor}" />
|
||||||
|
<SolidColorBrush x:Key="DataGridDetailsPresenterBackgroundBrush" Color="{DynamicResource SystemChromeMediumLowColor}" />
|
||||||
|
|
||||||
|
</ResourceDictionary>
|
||||||
|
</ResourceDictionary.ThemeDictionaries>
|
||||||
|
|
||||||
|
<x:Double x:Key="ListAccentLowOpacity">0.6</x:Double>
|
||||||
|
<x:Double x:Key="ListAccentMediumOpacity">0.8</x:Double>
|
||||||
|
|
||||||
|
<StreamGeometry x:Key="DataGridSortIconDescendingPath">M1875 1011l-787 787v-1798h-128v1798l-787 -787l-90 90l941 941l941 -941z</StreamGeometry>
|
||||||
|
<StreamGeometry x:Key="DataGridSortIconAscendingPath">M1965 947l-941 -941l-941 941l90 90l787 -787v1798h128v-1798l787 787z</StreamGeometry>
|
||||||
|
<StreamGeometry x:Key="DataGridRowGroupHeaderIconClosedPath">M515 93l930 931l-930 931l90 90l1022 -1021l-1022 -1021z</StreamGeometry>
|
||||||
|
<StreamGeometry x:Key="DataGridRowGroupHeaderIconOpenedPath">M109 486 19 576 1024 1581 2029 576 1939 486 1024 1401z</StreamGeometry>
|
||||||
|
|
||||||
|
<StaticResource x:Key="DataGridRowBackgroundBrush" ResourceKey="SystemControlTransparentBrush" />
|
||||||
|
<SolidColorBrush x:Key="DataGridRowSelectedBackgroundBrush" Color="{DynamicResource SystemAccentColor}" />
|
||||||
|
<StaticResource x:Key="DataGridRowSelectedBackgroundOpacity" ResourceKey="ListAccentLowOpacity" />
|
||||||
|
<SolidColorBrush x:Key="DataGridRowSelectedHoveredBackgroundBrush" Color="{DynamicResource SystemAccentColor}" />
|
||||||
|
<StaticResource x:Key="DataGridRowSelectedHoveredBackgroundOpacity" ResourceKey="ListAccentMediumOpacity" />
|
||||||
|
<SolidColorBrush x:Key="DataGridRowSelectedUnfocusedBackgroundBrush" Color="{DynamicResource SystemAccentColor}" />
|
||||||
|
<StaticResource x:Key="DataGridRowSelectedUnfocusedBackgroundOpacity" ResourceKey="ListAccentLowOpacity" />
|
||||||
|
<SolidColorBrush x:Key="DataGridRowSelectedHoveredUnfocusedBackgroundBrush" Color="{DynamicResource SystemAccentColor}" />
|
||||||
|
<StaticResource x:Key="DataGridRowSelectedHoveredUnfocusedBackgroundOpacity" ResourceKey="ListAccentMediumOpacity" />
|
||||||
|
<StaticResource x:Key="DataGridCellBackgroundBrush" ResourceKey="SystemControlTransparentBrush" />
|
||||||
|
<StaticResource x:Key="DataGridCurrencyVisualPrimaryBrush" ResourceKey="SystemControlTransparentBrush" />
|
||||||
|
<StaticResource x:Key="DataGridFillerColumnGridLinesBrush" ResourceKey="SystemControlTransparentBrush" />
|
||||||
|
|
||||||
|
<ControlTheme x:Key="DataGridCellTextBlockTheme" TargetType="TextBlock">
|
||||||
|
<Setter Property="Margin" Value="12,0,12,0" />
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center" />
|
||||||
|
</ControlTheme>
|
||||||
|
<ControlTheme x:Key="DataGridCellTextBoxTheme" TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
|
||||||
|
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||||
|
<Setter Property="Background" Value="Transparent" />
|
||||||
|
<Style Selector="^ /template/ DataValidationErrors">
|
||||||
|
<Setter Property="Theme" Value="{StaticResource TooltipDataValidationErrors}" />
|
||||||
|
</Style>
|
||||||
|
</ControlTheme>
|
||||||
|
|
||||||
|
<ControlTheme x:Key="{x:Type DataGridCell}" TargetType="DataGridCell">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource DataGridCellBackgroundBrush}" />
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||||
|
<Setter Property="VerticalContentAlignment" Value="Stretch" />
|
||||||
|
<Setter Property="FontSize" Value="15" />
|
||||||
|
<Setter Property="MinHeight" Value="32" />
|
||||||
|
<Setter Property="Focusable" Value="False" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Border x:Name="CellBorder"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}">
|
||||||
|
<Grid x:Name="PART_CellRoot" ColumnDefinitions="*,Auto">
|
||||||
|
|
||||||
|
<Rectangle x:Name="CurrencyVisual"
|
||||||
|
IsVisible="False"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
Fill="Transparent"
|
||||||
|
IsHitTestVisible="False"
|
||||||
|
Stroke="{DynamicResource DataGridCurrencyVisualPrimaryBrush}"
|
||||||
|
StrokeThickness="1" />
|
||||||
|
<Grid Grid.Column="0" x:Name="FocusVisual" IsHitTestVisible="False"
|
||||||
|
IsVisible="False">
|
||||||
|
<Rectangle HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
Fill="Transparent"
|
||||||
|
IsHitTestVisible="False"
|
||||||
|
Stroke="{DynamicResource DataGridCellFocusVisualPrimaryBrush}"
|
||||||
|
StrokeThickness="2" />
|
||||||
|
<Rectangle Margin="2"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
Fill="Transparent"
|
||||||
|
IsHitTestVisible="False"
|
||||||
|
Stroke="{DynamicResource DataGridCellFocusVisualSecondaryBrush}"
|
||||||
|
StrokeThickness="1" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<ContentPresenter Grid.Column="0" Margin="{TemplateBinding Padding}"
|
||||||
|
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||||
|
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||||
|
Content="{TemplateBinding Content}"
|
||||||
|
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||||
|
Foreground="{TemplateBinding Foreground}" />
|
||||||
|
|
||||||
|
<Rectangle Grid.Column="0" x:Name="InvalidVisualElement"
|
||||||
|
IsVisible="False"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
IsHitTestVisible="False"
|
||||||
|
Stroke="{DynamicResource DataGridCellInvalidBrush}"
|
||||||
|
StrokeThickness="1" />
|
||||||
|
|
||||||
|
<Rectangle Name="PART_RightGridLine"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="1"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
Fill="{DynamicResource DataGridFillerColumnGridLinesBrush}" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
<Style Selector="^:current /template/ Rectangle#CurrencyVisual">
|
||||||
|
<Setter Property="IsVisible" Value="True" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:focus /template/ Grid#FocusVisual">
|
||||||
|
<Setter Property="IsVisible" Value="True" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:invalid /template/ Rectangle#InvalidVisualElement">
|
||||||
|
<Setter Property="IsVisible" Value="True" />
|
||||||
|
</Style>
|
||||||
|
</ControlTheme>
|
||||||
|
|
||||||
|
<ControlTheme x:Key="{x:Type DataGridColumnHeader}" TargetType="DataGridColumnHeader">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource DataGridColumnHeaderForegroundBrush}" />
|
||||||
|
<Setter Property="Background" Value="{DynamicResource DataGridColumnHeaderBackgroundBrush}" />
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||||
|
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||||
|
<Setter Property="Focusable" Value="False" />
|
||||||
|
<Setter Property="SeparatorBrush" Value="{DynamicResource DataGridGridLinesBrush}" />
|
||||||
|
<Setter Property="Padding" Value="8,0,0,0" />
|
||||||
|
<Setter Property="FontSize" Value="12" />
|
||||||
|
<Setter Property="MinHeight" Value="32" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Border x:Name="HeaderBorder"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}">
|
||||||
|
<Grid Name="PART_ColumnHeaderRoot" ColumnDefinitions="*,Auto">
|
||||||
|
|
||||||
|
<Grid Margin="{TemplateBinding Padding}"
|
||||||
|
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||||
|
VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="16" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<ContentPresenter Content="{TemplateBinding Content}"
|
||||||
|
ContentTemplate="{TemplateBinding ContentTemplate}" />
|
||||||
|
|
||||||
|
<Path Name="SortIcon"
|
||||||
|
IsVisible="False"
|
||||||
|
Grid.Column="1"
|
||||||
|
Height="12"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Fill="{TemplateBinding Foreground}"
|
||||||
|
Stretch="Uniform" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Rectangle Name="VerticalSeparator"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="1"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
Fill="{TemplateBinding SeparatorBrush}"
|
||||||
|
IsVisible="{TemplateBinding AreSeparatorsVisible}" />
|
||||||
|
|
||||||
|
<Grid x:Name="FocusVisual" IsHitTestVisible="False"
|
||||||
|
IsVisible="False">
|
||||||
|
<Rectangle x:Name="FocusVisualPrimary"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
Fill="Transparent"
|
||||||
|
IsHitTestVisible="False"
|
||||||
|
Stroke="{DynamicResource DataGridCellFocusVisualPrimaryBrush}"
|
||||||
|
StrokeThickness="2" />
|
||||||
|
<Rectangle x:Name="FocusVisualSecondary"
|
||||||
|
Margin="2"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
Fill="Transparent"
|
||||||
|
IsHitTestVisible="False"
|
||||||
|
Stroke="{DynamicResource DataGridCellFocusVisualSecondaryBrush}"
|
||||||
|
StrokeThickness="1" />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
|
||||||
|
<Style Selector="^:focus-visible /template/ Grid#FocusVisual">
|
||||||
|
<Setter Property="IsVisible" Value="True" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="^:pointerover /template/ Grid#PART_ColumnHeaderRoot">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource DataGridColumnHeaderHoveredBackgroundBrush}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="^:pressed /template/ Grid#PART_ColumnHeaderRoot">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource DataGridColumnHeaderPressedBackgroundBrush}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="^:dragIndicator">
|
||||||
|
<Setter Property="Opacity" Value="0.5" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="^:sortascending /template/ Path#SortIcon">
|
||||||
|
<Setter Property="IsVisible" Value="True" />
|
||||||
|
<Setter Property="Data" Value="{StaticResource DataGridSortIconAscendingPath}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="^:sortdescending /template/ Path#SortIcon">
|
||||||
|
<Setter Property="IsVisible" Value="True" />
|
||||||
|
<Setter Property="Data" Value="{StaticResource DataGridSortIconDescendingPath}" />
|
||||||
|
</Style>
|
||||||
|
</ControlTheme>
|
||||||
|
|
||||||
|
<ControlTheme x:Key="DataGridTopLeftColumnHeader" TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Grid x:Name="TopLeftHeaderRoot"
|
||||||
|
RowDefinitions="*,*,Auto">
|
||||||
|
<Border Grid.RowSpan="2"
|
||||||
|
BorderThickness="0,0,1,0"
|
||||||
|
BorderBrush="{DynamicResource DataGridGridLinesBrush}" />
|
||||||
|
<Rectangle Grid.Row="0" Grid.RowSpan="2"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
StrokeThickness="1"
|
||||||
|
Height="1"
|
||||||
|
Fill="{DynamicResource DataGridGridLinesBrush}" />
|
||||||
|
</Grid>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</ControlTheme>
|
||||||
|
|
||||||
|
<ControlTheme x:Key="{x:Type DataGridRowHeader}" TargetType="DataGridRowHeader">
|
||||||
|
<Setter Property="Focusable" Value="False" />
|
||||||
|
<Setter Property="SeparatorBrush" Value="{DynamicResource DataGridGridLinesBrush}" />
|
||||||
|
<Setter Property="AreSeparatorsVisible" Value="False" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Grid x:Name="PART_Root"
|
||||||
|
RowDefinitions="*,*,Auto"
|
||||||
|
ColumnDefinitions="Auto,*">
|
||||||
|
<Border Grid.RowSpan="3"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
BorderBrush="{TemplateBinding SeparatorBrush}"
|
||||||
|
BorderThickness="0,0,1,0">
|
||||||
|
<Grid Background="{TemplateBinding Background}">
|
||||||
|
<Rectangle x:Name="RowInvalidVisualElement"
|
||||||
|
Opacity="0"
|
||||||
|
Fill="{DynamicResource DataGridRowInvalidBrush}"
|
||||||
|
Stretch="Fill" />
|
||||||
|
<Rectangle x:Name="BackgroundRectangle"
|
||||||
|
Fill="{DynamicResource DataGridRowBackgroundBrush}"
|
||||||
|
Stretch="Fill" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
<Rectangle x:Name="HorizontalSeparator"
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Height="1"
|
||||||
|
Margin="1,0,1,0"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Fill="{TemplateBinding SeparatorBrush}"
|
||||||
|
IsVisible="{TemplateBinding AreSeparatorsVisible}" />
|
||||||
|
|
||||||
|
<ContentPresenter Grid.RowSpan="2"
|
||||||
|
Grid.Column="1"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Content="{TemplateBinding Content}" />
|
||||||
|
</Grid>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</ControlTheme>
|
||||||
|
|
||||||
|
<ControlTheme x:Key="{x:Type DataGridRow}" TargetType="DataGridRow">
|
||||||
|
<Setter Property="Focusable" Value="False" />
|
||||||
|
<Setter Property="Background" Value="{Binding $parent[DataGrid].RowBackground}" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Border x:Name="RowBorder"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}">
|
||||||
|
<DataGridFrozenGrid Name="PART_Root"
|
||||||
|
ColumnDefinitions="Auto,*"
|
||||||
|
RowDefinitions="*,Auto,Auto">
|
||||||
|
|
||||||
|
<Rectangle Name="BackgroundRectangle"
|
||||||
|
Fill="{DynamicResource DataGridRowBackgroundBrush}"
|
||||||
|
Grid.RowSpan="2"
|
||||||
|
Grid.ColumnSpan="2" />
|
||||||
|
<Rectangle x:Name="InvalidVisualElement"
|
||||||
|
Opacity="0"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Fill="{DynamicResource DataGridRowInvalidBrush}" />
|
||||||
|
|
||||||
|
<DataGridRowHeader Name="PART_RowHeader"
|
||||||
|
Grid.RowSpan="3"
|
||||||
|
DataGridFrozenGrid.IsFrozen="True" />
|
||||||
|
<DataGridCellsPresenter Name="PART_CellsPresenter"
|
||||||
|
Grid.Column="1"
|
||||||
|
DataGridFrozenGrid.IsFrozen="True" />
|
||||||
|
<DataGridDetailsPresenter Name="PART_DetailsPresenter"
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="1"
|
||||||
|
Background="{DynamicResource DataGridDetailsPresenterBackgroundBrush}" />
|
||||||
|
<Rectangle Name="PART_BottomGridLine"
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.Column="1"
|
||||||
|
Height="1"
|
||||||
|
HorizontalAlignment="Stretch" />
|
||||||
|
|
||||||
|
</DataGridFrozenGrid>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
|
||||||
|
<Style Selector="^:invalid">
|
||||||
|
<Style Selector="^ /template/ Rectangle#InvalidVisualElement">
|
||||||
|
<Setter Property="Opacity" Value="0.4" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^ /template/ Rectangle#BackgroundRectangle">
|
||||||
|
<Setter Property="Opacity" Value="0" />
|
||||||
|
</Style>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="^:pointerover /template/ Rectangle#BackgroundRectangle">
|
||||||
|
<Setter Property="Fill" Value="{DynamicResource DataGridRowHoveredBackgroundColor}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="^:selected">
|
||||||
|
<Style Selector="^ /template/ Rectangle#BackgroundRectangle">
|
||||||
|
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedUnfocusedBackgroundBrush}" />
|
||||||
|
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedUnfocusedBackgroundOpacity}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:pointerover /template/ Rectangle#BackgroundRectangle">
|
||||||
|
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedHoveredUnfocusedBackgroundBrush}" />
|
||||||
|
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedHoveredUnfocusedBackgroundOpacity}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:focus /template/ Rectangle#BackgroundRectangle">
|
||||||
|
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedBackgroundBrush}" />
|
||||||
|
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedBackgroundOpacity}" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^:pointerover:focus /template/ Rectangle#BackgroundRectangle">
|
||||||
|
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedHoveredBackgroundBrush}" />
|
||||||
|
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedHoveredBackgroundOpacity}" />
|
||||||
|
</Style>
|
||||||
|
</Style>
|
||||||
|
</ControlTheme>
|
||||||
|
|
||||||
|
<ControlTheme x:Key="FluentDataGridRowGroupExpanderButtonTheme" TargetType="ToggleButton">
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Border Width="12"
|
||||||
|
Height="12"
|
||||||
|
Background="Transparent"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<Path Fill="{TemplateBinding Foreground}"
|
||||||
|
Data="{StaticResource DataGridRowGroupHeaderIconClosedPath}"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Stretch="Uniform" />
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
<Style Selector="^:checked /template/ Path">
|
||||||
|
<Setter Property="Data" Value="{StaticResource DataGridRowGroupHeaderIconOpenedPath}" />
|
||||||
|
</Style>
|
||||||
|
</ControlTheme>
|
||||||
|
|
||||||
|
<ControlTheme x:Key="{x:Type DataGridRowGroupHeader}" TargetType="DataGridRowGroupHeader">
|
||||||
|
<Setter Property="Focusable" Value="False" />
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource DataGridRowGroupHeaderForegroundBrush}" />
|
||||||
|
<Setter Property="Background" Value="{DynamicResource DataGridRowGroupHeaderBackgroundBrush}" />
|
||||||
|
<Setter Property="FontSize" Value="15" />
|
||||||
|
<Setter Property="MinHeight" Value="32" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate x:DataType="collections:DataGridCollectionViewGroup">
|
||||||
|
<DataGridFrozenGrid Name="PART_Root"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
MinHeight="{TemplateBinding MinHeight}"
|
||||||
|
ColumnDefinitions="Auto,Auto,Auto,Auto,*"
|
||||||
|
RowDefinitions="*,Auto">
|
||||||
|
|
||||||
|
<Rectangle Name="PART_IndentSpacer"
|
||||||
|
Grid.Column="1" />
|
||||||
|
<ToggleButton Name="PART_ExpanderButton"
|
||||||
|
Grid.Column="2"
|
||||||
|
Width="12"
|
||||||
|
Height="12"
|
||||||
|
Margin="12,0,0,0"
|
||||||
|
Theme="{StaticResource FluentDataGridRowGroupExpanderButtonTheme}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}"
|
||||||
|
Focusable="False"
|
||||||
|
Foreground="{TemplateBinding Foreground}" />
|
||||||
|
|
||||||
|
<StackPanel Grid.Column="3"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Margin="12,0,0,0">
|
||||||
|
<TextBlock Name="PART_PropertyNameElement"
|
||||||
|
Margin="4,0,0,0"
|
||||||
|
IsVisible="{TemplateBinding IsPropertyNameVisible}"
|
||||||
|
Foreground="{TemplateBinding Foreground}" />
|
||||||
|
<TextBlock Margin="4,0,0,0"
|
||||||
|
Text="{Binding Key}"
|
||||||
|
Foreground="{TemplateBinding Foreground}" />
|
||||||
|
<TextBlock Name="PART_ItemCountElement"
|
||||||
|
Margin="4,0,0,0"
|
||||||
|
IsVisible="{TemplateBinding IsItemCountVisible}"
|
||||||
|
Foreground="{TemplateBinding Foreground}" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<Rectangle x:Name="CurrencyVisual"
|
||||||
|
Grid.ColumnSpan="5"
|
||||||
|
IsVisible="False"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
Fill="Transparent"
|
||||||
|
IsHitTestVisible="False"
|
||||||
|
Stroke="{DynamicResource DataGridCurrencyVisualPrimaryBrush}"
|
||||||
|
StrokeThickness="1" />
|
||||||
|
<Grid x:Name="FocusVisual"
|
||||||
|
Grid.ColumnSpan="5"
|
||||||
|
IsVisible="False"
|
||||||
|
IsHitTestVisible="False">
|
||||||
|
<Rectangle HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
Fill="Transparent"
|
||||||
|
IsHitTestVisible="False"
|
||||||
|
Stroke="{DynamicResource DataGridCellFocusVisualPrimaryBrush}"
|
||||||
|
StrokeThickness="2" />
|
||||||
|
<Rectangle Margin="2"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
Fill="Transparent"
|
||||||
|
IsHitTestVisible="False"
|
||||||
|
Stroke="{DynamicResource DataGridCellFocusVisualSecondaryBrush}"
|
||||||
|
StrokeThickness="1" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<DataGridRowHeader Name="PART_RowHeader"
|
||||||
|
Grid.RowSpan="2"
|
||||||
|
DataGridFrozenGrid.IsFrozen="True" />
|
||||||
|
|
||||||
|
<Rectangle x:Name="PART_BottomGridLine"
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.ColumnSpan="5"
|
||||||
|
Height="1" />
|
||||||
|
</DataGridFrozenGrid>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</ControlTheme>
|
||||||
|
|
||||||
|
<ControlTheme x:Key="{x:Type DataGrid}" TargetType="DataGrid">
|
||||||
|
<Setter Property="RowBackground" Value="Transparent" />
|
||||||
|
<Setter Property="HeadersVisibility" Value="Column" />
|
||||||
|
<Setter Property="HorizontalScrollBarVisibility" Value="Auto" />
|
||||||
|
<Setter Property="VerticalScrollBarVisibility" Value="Auto" />
|
||||||
|
<Setter Property="SelectionMode" Value="Extended" />
|
||||||
|
<Setter Property="GridLinesVisibility" Value="None" />
|
||||||
|
<Setter Property="HorizontalGridLinesBrush" Value="{DynamicResource DataGridGridLinesBrush}" />
|
||||||
|
<Setter Property="VerticalGridLinesBrush" Value="{DynamicResource DataGridGridLinesBrush}" />
|
||||||
|
<Setter Property="DropLocationIndicatorTemplate">
|
||||||
|
<Template>
|
||||||
|
<Rectangle Fill="{DynamicResource DataGridDropLocationIndicatorBackground}"
|
||||||
|
Width="2" />
|
||||||
|
</Template>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Border x:Name="DataGridBorder"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
CornerRadius="{TemplateBinding CornerRadius}">
|
||||||
|
<Grid ColumnDefinitions="Auto,*,Auto"
|
||||||
|
RowDefinitions="Auto,*,Auto,Auto"
|
||||||
|
ClipToBounds="True">
|
||||||
|
<DataGridColumnHeader Name="PART_TopLeftCornerHeader"
|
||||||
|
Theme="{StaticResource DataGridTopLeftColumnHeader}" />
|
||||||
|
<DataGridColumnHeadersPresenter Name="PART_ColumnHeadersPresenter"
|
||||||
|
Grid.Column="1"
|
||||||
|
Grid.Row="0" Grid.ColumnSpan="2" />
|
||||||
|
<Rectangle Name="PART_ColumnHeadersAndRowsSeparator"
|
||||||
|
Grid.Row="0" Grid.ColumnSpan="3" Grid.Column="0"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
Height="1"
|
||||||
|
Fill="{DynamicResource DataGridGridLinesBrush}" />
|
||||||
|
|
||||||
|
<DataGridRowsPresenter Name="PART_RowsPresenter"
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.RowSpan="2"
|
||||||
|
Grid.ColumnSpan="3" Grid.Column="0">
|
||||||
|
<DataGridRowsPresenter.GestureRecognizers>
|
||||||
|
<ScrollGestureRecognizer CanHorizontallyScroll="True" CanVerticallyScroll="True" />
|
||||||
|
</DataGridRowsPresenter.GestureRecognizers>
|
||||||
|
</DataGridRowsPresenter>
|
||||||
|
<Rectangle Name="PART_BottomRightCorner"
|
||||||
|
Fill="{DynamicResource DataGridScrollBarsSeparatorBackground}"
|
||||||
|
Grid.Column="2"
|
||||||
|
Grid.Row="2" />
|
||||||
|
<ScrollBar Name="PART_VerticalScrollbar"
|
||||||
|
Orientation="Vertical"
|
||||||
|
Grid.Column="2"
|
||||||
|
Grid.Row="1"
|
||||||
|
Width="{DynamicResource ScrollBarSize}" />
|
||||||
|
|
||||||
|
<Grid Grid.Column="1"
|
||||||
|
Grid.Row="2"
|
||||||
|
ColumnDefinitions="Auto,*">
|
||||||
|
<Rectangle Name="PART_FrozenColumnScrollBarSpacer" />
|
||||||
|
<ScrollBar Name="PART_HorizontalScrollbar"
|
||||||
|
Grid.Column="1"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
Height="{DynamicResource ScrollBarSize}" />
|
||||||
|
</Grid>
|
||||||
|
<Border x:Name="PART_DisabledVisualElement"
|
||||||
|
Grid.ColumnSpan="3" Grid.Column="0"
|
||||||
|
Grid.Row="0" Grid.RowSpan="4"
|
||||||
|
IsHitTestVisible="False"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
CornerRadius="2"
|
||||||
|
Background="{DynamicResource DataGridDisabledVisualElementBackground}"
|
||||||
|
IsVisible="{Binding !$parent[DataGrid].IsEnabled}" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
|
||||||
|
<Style Selector="^:empty-columns">
|
||||||
|
<Style Selector="^ /template/ DataGridColumnHeader#PART_TopLeftCornerHeader">
|
||||||
|
<Setter Property="IsVisible" Value="False" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^ /template/ DataGridColumnHeadersPresenter#PART_ColumnHeadersPresenter">
|
||||||
|
<Setter Property="IsVisible" Value="False" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^ /template/ Rectangle#PART_ColumnHeadersAndRowsSeparator">
|
||||||
|
<Setter Property="IsVisible" Value="False" />
|
||||||
|
</Style>
|
||||||
|
</Style>
|
||||||
|
</ControlTheme>
|
||||||
|
</ResourceDictionary>
|
||||||
|
</Styles.Resources>
|
||||||
|
</Styles>
|
||||||
@ -1,658 +0,0 @@
|
|||||||
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
|
||||||
<Styles.Resources>
|
|
||||||
<x:Double x:Key="ListAccentLowOpacity">0.6</x:Double>
|
|
||||||
<x:Double x:Key="ListAccentMediumOpacity">0.8</x:Double>
|
|
||||||
<Thickness x:Key="DataGridTextColumnCellTextBlockMargin">12,0,12,0</Thickness>
|
|
||||||
|
|
||||||
<StreamGeometry x:Key="DataGridSortIconDescendingPath">M1875 1011l-787 787v-1798h-128v1798l-787 -787l-90 90l941 941l941 -941z</StreamGeometry>
|
|
||||||
<StreamGeometry x:Key="DataGridRowGroupHeaderIconClosedPath">M515 93l930 931l-930 931l90 90l1022 -1021l-1022 -1021z</StreamGeometry>
|
|
||||||
<StreamGeometry x:Key="DataGridRowGroupHeaderIconOpenedPath">M1939 1581l90 -90l-1005 -1005l-1005 1005l90 90l915 -915z</StreamGeometry>
|
|
||||||
|
|
||||||
<SolidColorBrush x:Key="DataGridColumnHeaderForegroundBrush" Color="{DynamicResource SystemBaseMediumColor}" />
|
|
||||||
<SolidColorBrush x:Key="DataGridColumnHeaderBackgroundBrush" Color="{DynamicResource SystemAltHighColor}" />
|
|
||||||
<SolidColorBrush x:Key="DataGridColumnHeaderHoveredBackgroundBrush" Color="{DynamicResource SystemListLowColor}" />
|
|
||||||
<SolidColorBrush x:Key="DataGridColumnHeaderPressedBackgroundBrush" Color="{DynamicResource SystemListMediumColor}" />
|
|
||||||
<SolidColorBrush x:Key="DataGridColumnHeaderDraggedBackgroundBrush" Color="{DynamicResource SystemChromeMediumLowColor}" />
|
|
||||||
|
|
||||||
<SolidColorBrush x:Key="DataGridRowGroupHeaderBackgroundBrush" Color="{DynamicResource SystemChromeMediumColor}" />
|
|
||||||
<SolidColorBrush x:Key="DataGridRowGroupHeaderPressedBackgroundBrush" Color="{DynamicResource SystemListMediumColor}" />
|
|
||||||
<SolidColorBrush x:Key="DataGridRowGroupHeaderForegroundBrush" Color="{DynamicResource SystemBaseHighColor}" />
|
|
||||||
<SolidColorBrush x:Key="DataGridRowGroupHeaderHoveredBackgroundBrush" Color="{DynamicResource SystemListLowColor}" />
|
|
||||||
|
|
||||||
<StaticResource x:Key="DataGridRowBackgroundBrush" ResourceKey="SystemControlTransparentBrush" />
|
|
||||||
<SolidColorBrush x:Key="DataGridRowSelectedBackgroundBrush" Color="{DynamicResource SystemAccentColor}" />
|
|
||||||
<StaticResource x:Key="DataGridRowSelectedBackgroundOpacity" ResourceKey="ListAccentLowOpacity" />
|
|
||||||
<SolidColorBrush x:Key="DataGridRowSelectedHoveredBackgroundBrush" Color="{DynamicResource SystemAccentColor}" />
|
|
||||||
<StaticResource x:Key="DataGridRowSelectedHoveredBackgroundOpacity" ResourceKey="ListAccentMediumOpacity" />
|
|
||||||
<SolidColorBrush x:Key="DataGridRowSelectedUnfocusedBackgroundBrush" Color="{DynamicResource SystemAccentColor}" />
|
|
||||||
<StaticResource x:Key="DataGridRowSelectedUnfocusedBackgroundOpacity" ResourceKey="ListAccentLowOpacity" />
|
|
||||||
<SolidColorBrush x:Key="DataGridRowSelectedHoveredUnfocusedBackgroundBrush" Color="{DynamicResource SystemAccentColor}" />
|
|
||||||
<StaticResource x:Key="DataGridRowSelectedHoveredUnfocusedBackgroundOpacity" ResourceKey="ListAccentMediumOpacity" />
|
|
||||||
<SolidColorBrush x:Key="DataGridRowHoveredBackgroundColor" Color="{DynamicResource SystemListLowColor}" />
|
|
||||||
<SolidColorBrush x:Key="DataGridRowInvalidBrush" Color="{DynamicResource SystemErrorTextColor}" />
|
|
||||||
|
|
||||||
<SolidColorBrush x:Key="DataGridRowHeaderForegroundBrush" Color="{DynamicResource SystemBaseMediumColor}" />
|
|
||||||
<SolidColorBrush x:Key="DataGridRowHeaderBackgroundBrush" Color="{DynamicResource SystemAltHighColor}" />
|
|
||||||
|
|
||||||
<StaticResource x:Key="DataGridCellBackgroundBrush" ResourceKey="SystemControlTransparentBrush" />
|
|
||||||
<SolidColorBrush x:Key="DataGridCellFocusVisualPrimaryBrush" Color="{DynamicResource SystemBaseHighColor}" />
|
|
||||||
<SolidColorBrush x:Key="DataGridCellFocusVisualSecondaryBrush" Color="{DynamicResource SystemAltMediumColor}" />
|
|
||||||
<SolidColorBrush x:Key="DataGridCellInvalidBrush" Color="{DynamicResource SystemErrorTextColor}" />
|
|
||||||
|
|
||||||
<SolidColorBrush x:Key="DataGridGridLinesBrush"
|
|
||||||
Opacity="0.4"
|
|
||||||
Color="{DynamicResource SystemBaseMediumLowColor}" />
|
|
||||||
<StaticResource x:Key="DataGridCurrencyVisualPrimaryBrush" ResourceKey="SystemControlTransparentBrush" />
|
|
||||||
<SolidColorBrush x:Key="DataGridDetailsPresenterBackgroundBrush" Color="{DynamicResource SystemChromeMediumLowColor}" />
|
|
||||||
<StaticResource x:Key="DataGridFillerColumnGridLinesBrush" ResourceKey="SystemControlTransparentBrush" />
|
|
||||||
</Styles.Resources>
|
|
||||||
|
|
||||||
<Style Selector="DataGridCell">
|
|
||||||
<Setter Property="Background" Value="{DynamicResource DataGridCellBackgroundBrush}" />
|
|
||||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
|
||||||
<Setter Property="VerticalContentAlignment" Value="Stretch" />
|
|
||||||
<Setter Property="FontSize" Value="12" />
|
|
||||||
<Setter Property="MinHeight" Value="32" />
|
|
||||||
<Setter Property="Focusable" Value="False" />
|
|
||||||
<Setter Property="Template">
|
|
||||||
<ControlTemplate>
|
|
||||||
<Border x:Name="CellBorder"
|
|
||||||
Background="{TemplateBinding Background}"
|
|
||||||
BorderBrush="{TemplateBinding BorderBrush}"
|
|
||||||
BorderThickness="{TemplateBinding BorderThickness}"
|
|
||||||
CornerRadius="{TemplateBinding CornerRadius}">
|
|
||||||
<Grid x:Name="PART_CellRoot" ColumnDefinitions="*,Auto">
|
|
||||||
|
|
||||||
<Rectangle x:Name="CurrencyVisual"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
Fill="Transparent"
|
|
||||||
IsHitTestVisible="False"
|
|
||||||
Stroke="{DynamicResource DataGridCurrencyVisualPrimaryBrush}"
|
|
||||||
StrokeThickness="1" />
|
|
||||||
<Grid x:Name="FocusVisual" IsHitTestVisible="False">
|
|
||||||
<Rectangle HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
Fill="Transparent"
|
|
||||||
IsHitTestVisible="False"
|
|
||||||
Stroke="{DynamicResource DataGridCellFocusVisualPrimaryBrush}"
|
|
||||||
StrokeThickness="2" />
|
|
||||||
<Rectangle Margin="2"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
Fill="Transparent"
|
|
||||||
IsHitTestVisible="False"
|
|
||||||
Stroke="{DynamicResource DataGridCellFocusVisualSecondaryBrush}"
|
|
||||||
StrokeThickness="1" />
|
|
||||||
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<ContentPresenter Margin="{TemplateBinding Padding}"
|
|
||||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
|
||||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
|
||||||
Content="{TemplateBinding Content}"
|
|
||||||
ContentTemplate="{TemplateBinding ContentTemplate}"/>
|
|
||||||
|
|
||||||
<Rectangle x:Name="InvalidVisualElement"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
IsHitTestVisible="False"
|
|
||||||
Stroke="{DynamicResource DataGridCellInvalidBrush}"
|
|
||||||
StrokeThickness="1" />
|
|
||||||
|
|
||||||
<Rectangle Name="PART_RightGridLine"
|
|
||||||
Grid.Column="1"
|
|
||||||
Width="1"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
Fill="{DynamicResource DataGridFillerColumnGridLinesBrush}" />
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="DataGridCell > TextBlock#CellTextBlock">
|
|
||||||
<Setter Property="Margin" Value="{DynamicResource DataGridTextColumnCellTextBlockMargin}" />
|
|
||||||
<Setter Property="VerticalAlignment" Value="Center" />
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="DataGridCell /template/ Rectangle#CurrencyVisual">
|
|
||||||
<Setter Property="IsVisible" Value="False" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridCell /template/ Grid#FocusVisual">
|
|
||||||
<Setter Property="IsVisible" Value="False" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridCell:current /template/ Rectangle#CurrencyVisual">
|
|
||||||
<Setter Property="IsVisible" Value="True" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGrid:focus DataGridCell:current /template/ Grid#FocusVisual">
|
|
||||||
<Setter Property="IsVisible" Value="True" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridCell /template/ Rectangle#InvalidVisualElement">
|
|
||||||
<Setter Property="IsVisible" Value="False" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridCell:invalid /template/ Rectangle#InvalidVisualElement">
|
|
||||||
<Setter Property="IsVisible" Value="True" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridCell > TextBox DataValidationErrors">
|
|
||||||
<Setter Property="Template" Value="{DynamicResource TooltipDataValidationContentTemplate}" />
|
|
||||||
<Setter Property="ErrorTemplate" Value="{DynamicResource TooltipDataValidationErrorTemplate}" />
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="DataGridColumnHeader">
|
|
||||||
<Setter Property="Foreground" Value="{DynamicResource DataGridColumnHeaderForegroundBrush}" />
|
|
||||||
<Setter Property="Background" Value="{DynamicResource DataGridColumnHeaderBackgroundBrush}" />
|
|
||||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
|
||||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
|
||||||
<Setter Property="Focusable" Value="False" />
|
|
||||||
<Setter Property="SeparatorBrush" Value="{DynamicResource DataGridGridLinesBrush}" />
|
|
||||||
<Setter Property="Padding" Value="6,0,0,0" />
|
|
||||||
<Setter Property="FontSize" Value="12" />
|
|
||||||
<Setter Property="MinHeight" Value="40" />
|
|
||||||
<Setter Property="Template">
|
|
||||||
<ControlTemplate>
|
|
||||||
<Border x:Name="HeaderBorder"
|
|
||||||
Background="{TemplateBinding Background}"
|
|
||||||
BorderBrush="{TemplateBinding BorderBrush}"
|
|
||||||
BorderThickness="{TemplateBinding BorderThickness}"
|
|
||||||
CornerRadius="{TemplateBinding CornerRadius}">
|
|
||||||
<Grid Name="PART_ColumnHeaderRoot" ColumnDefinitions="*,Auto">
|
|
||||||
|
|
||||||
<Grid Grid.Column="0" Margin="{TemplateBinding Padding}"
|
|
||||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
|
||||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" ColumnDefinitions="*,12">
|
|
||||||
|
|
||||||
<ContentPresenter Grid.Column="0"
|
|
||||||
HorizontalAlignment="Left"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Content="{TemplateBinding Content}"
|
|
||||||
ContentTemplate="{TemplateBinding ContentTemplate}" />
|
|
||||||
|
|
||||||
<Path Name="SortIcon"
|
|
||||||
Grid.Column="1"
|
|
||||||
Height="12"
|
|
||||||
Width="8"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Fill="{TemplateBinding Foreground}"
|
|
||||||
Stretch="Uniform"
|
|
||||||
Margin="0,0,4,0"
|
|
||||||
Data="F1 M -5.215,6.099L 5.215,6.099L 0,0L -5.215,6.099 Z "/>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Rectangle Name="VerticalSeparator"
|
|
||||||
Grid.Column="1"
|
|
||||||
Width="1"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
Fill="{TemplateBinding SeparatorBrush}"
|
|
||||||
IsVisible="{TemplateBinding AreSeparatorsVisible}" />
|
|
||||||
|
|
||||||
<Grid x:Name="FocusVisual" IsHitTestVisible="False">
|
|
||||||
<Rectangle x:Name="FocusVisualPrimary"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
Fill="Transparent"
|
|
||||||
IsHitTestVisible="False"
|
|
||||||
Stroke="{DynamicResource DataGridCellFocusVisualPrimaryBrush}"
|
|
||||||
StrokeThickness="2" />
|
|
||||||
<Rectangle x:Name="FocusVisualSecondary"
|
|
||||||
Margin="2"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
Fill="Transparent"
|
|
||||||
IsHitTestVisible="False"
|
|
||||||
Stroke="{DynamicResource DataGridCellFocusVisualSecondaryBrush}"
|
|
||||||
StrokeThickness="1" />
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="DataGridColumnHeader /template/ Grid#FocusVisual">
|
|
||||||
<Setter Property="IsVisible" Value="False" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridColumnHeader:focus-visible /template/ Grid#FocusVisual">
|
|
||||||
<Setter Property="IsVisible" Value="True" />
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="DataGridColumnHeader:pointerover /template/ Grid#PART_ColumnHeaderRoot">
|
|
||||||
<Setter Property="Background" Value="{DynamicResource DataGridColumnHeaderHoveredBackgroundBrush}" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridColumnHeader:pressed /template/ Grid#PART_ColumnHeaderRoot">
|
|
||||||
<Setter Property="Background" Value="{DynamicResource DataGridColumnHeaderPressedBackgroundBrush}" />
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="DataGridColumnHeader:dragIndicator">
|
|
||||||
<Setter Property="Opacity" Value="0.5" />
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="DataGridColumnHeader /template/ Path#SortIcon">
|
|
||||||
<Setter Property="IsVisible" Value="False" />
|
|
||||||
<Setter Property="RenderTransform">
|
|
||||||
<Setter.Value>
|
|
||||||
<ScaleTransform ScaleX="0.9" ScaleY="0.9" />
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="DataGridColumnHeader:sortascending /template/ Path#SortIcon">
|
|
||||||
<Setter Property="IsVisible" Value="True" />
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="DataGridColumnHeader:sortdescending /template/ Path#SortIcon">
|
|
||||||
<Setter Property="IsVisible" Value="True" />
|
|
||||||
<Setter Property="RenderTransform">
|
|
||||||
<Setter.Value>
|
|
||||||
<ScaleTransform ScaleX="0.9" ScaleY="-0.9" />
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="DataGridRow">
|
|
||||||
<Setter Property="Focusable" Value="False" />
|
|
||||||
<Setter Property="Template">
|
|
||||||
<ControlTemplate>
|
|
||||||
<Border x:Name="RowBorder"
|
|
||||||
Background="{TemplateBinding Background}"
|
|
||||||
BorderBrush="{TemplateBinding BorderBrush}"
|
|
||||||
BorderThickness="{TemplateBinding BorderThickness}"
|
|
||||||
CornerRadius="{TemplateBinding CornerRadius}">
|
|
||||||
<DataGridFrozenGrid Name="PART_Root"
|
|
||||||
ColumnDefinitions="Auto,*"
|
|
||||||
RowDefinitions="*,Auto,Auto">
|
|
||||||
|
|
||||||
<Rectangle Name="BackgroundRectangle"
|
|
||||||
Grid.RowSpan="2"
|
|
||||||
Grid.ColumnSpan="2" />
|
|
||||||
<Rectangle x:Name="InvalidVisualElement"
|
|
||||||
Grid.ColumnSpan="2"
|
|
||||||
Fill="{DynamicResource DataGridRowInvalidBrush}" />
|
|
||||||
|
|
||||||
<DataGridRowHeader Name="PART_RowHeader"
|
|
||||||
Grid.RowSpan="3"
|
|
||||||
DataGridFrozenGrid.IsFrozen="True" />
|
|
||||||
<DataGridCellsPresenter Name="PART_CellsPresenter"
|
|
||||||
Grid.Column="1"
|
|
||||||
DataGridFrozenGrid.IsFrozen="True" />
|
|
||||||
<DataGridDetailsPresenter Name="PART_DetailsPresenter"
|
|
||||||
Grid.Row="1"
|
|
||||||
Grid.Column="1"
|
|
||||||
Background="{DynamicResource DataGridDetailsPresenterBackgroundBrush}" />
|
|
||||||
<Rectangle Name="PART_BottomGridLine"
|
|
||||||
Grid.Row="2"
|
|
||||||
Grid.Column="1"
|
|
||||||
Height="1"
|
|
||||||
HorizontalAlignment="Stretch" />
|
|
||||||
|
|
||||||
</DataGridFrozenGrid>
|
|
||||||
</Border>
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="DataGridRow">
|
|
||||||
<Setter Property="Background" Value="{Binding $parent[DataGrid].RowBackground}" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridRow:nth-child(even)">
|
|
||||||
<Setter Property="Background" Value="{Binding $parent[DataGrid].AlternatingRowBackground}" />
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="DataGridRow /template/ Rectangle#InvalidVisualElement">
|
|
||||||
<Setter Property="Opacity" Value="0" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridRow:invalid /template/ Rectangle#InvalidVisualElement">
|
|
||||||
<Setter Property="Opacity" Value="0.4" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridRow:invalid /template/ Rectangle#BackgroundRectangle">
|
|
||||||
<Setter Property="Opacity" Value="0" />
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="DataGridRow /template/ Rectangle#BackgroundRectangle">
|
|
||||||
<Setter Property="Fill" Value="{DynamicResource DataGridRowBackgroundBrush}" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridRow:pointerover /template/ Rectangle#BackgroundRectangle">
|
|
||||||
<Setter Property="Fill" Value="{DynamicResource DataGridRowHoveredBackgroundColor}" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridRow:selected /template/ Rectangle#BackgroundRectangle">
|
|
||||||
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedUnfocusedBackgroundBrush}" />
|
|
||||||
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedUnfocusedBackgroundOpacity}" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridRow:selected:pointerover /template/ Rectangle#BackgroundRectangle">
|
|
||||||
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedHoveredUnfocusedBackgroundBrush}" />
|
|
||||||
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedHoveredUnfocusedBackgroundOpacity}" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridRow:selected:focus /template/ Rectangle#BackgroundRectangle">
|
|
||||||
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedBackgroundBrush}" />
|
|
||||||
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedBackgroundOpacity}" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridRow:selected:pointerover:focus /template/ Rectangle#BackgroundRectangle">
|
|
||||||
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedHoveredBackgroundBrush}" />
|
|
||||||
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedHoveredBackgroundOpacity}" />
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="DataGridRowHeader">
|
|
||||||
<Setter Property="Foreground" Value="{DynamicResource DataGridRowHeaderForegroundBrush}" />
|
|
||||||
<Setter Property="Background" Value="{DynamicResource DataGridRowHeaderBackgroundBrush}" />
|
|
||||||
<Setter Property="Focusable" Value="False" />
|
|
||||||
<Setter Property="SeparatorBrush" Value="{DynamicResource DataGridGridLinesBrush}" />
|
|
||||||
<Setter Property="AreSeparatorsVisible" Value="False" />
|
|
||||||
<Setter Property="Template">
|
|
||||||
<ControlTemplate>
|
|
||||||
<Grid x:Name="PART_Root"
|
|
||||||
RowDefinitions="*,*,Auto"
|
|
||||||
ColumnDefinitions="Auto,*">
|
|
||||||
<Border Grid.RowSpan="3"
|
|
||||||
Grid.ColumnSpan="2"
|
|
||||||
BorderBrush="{TemplateBinding SeparatorBrush}"
|
|
||||||
BorderThickness="0,0,1,0">
|
|
||||||
<Grid Background="{TemplateBinding Background}">
|
|
||||||
<Rectangle x:Name="RowInvalidVisualElement"
|
|
||||||
Fill="{DynamicResource DataGridRowInvalidBrush}"
|
|
||||||
Stretch="Fill" />
|
|
||||||
<Rectangle x:Name="BackgroundRectangle"
|
|
||||||
Stretch="Fill" />
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
<Rectangle x:Name="HorizontalSeparator"
|
|
||||||
Grid.Row="2"
|
|
||||||
Grid.ColumnSpan="2"
|
|
||||||
Height="1"
|
|
||||||
Margin="1,0,1,0"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
Fill="{TemplateBinding SeparatorBrush}"
|
|
||||||
IsVisible="{TemplateBinding AreSeparatorsVisible}" />
|
|
||||||
|
|
||||||
<ContentPresenter Grid.RowSpan="2"
|
|
||||||
Grid.Column="1"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Content="{TemplateBinding Content}" />
|
|
||||||
</Grid>
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="DataGridRowHeader /template/ Rectangle#RowInvalidVisualElement">
|
|
||||||
<Setter Property="Opacity" Value="0" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridRowHeader:invalid /template/ Rectangle#RowInvalidVisualElement">
|
|
||||||
<Setter Property="Opacity" Value="0.4" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridRowHeader:invalid /template/ Rectangle#BackgroundRectangle">
|
|
||||||
<Setter Property="Opacity" Value="0" />
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="DataGridRowHeader /template/ Rectangle#BackgroundRectangle">
|
|
||||||
<Setter Property="Fill" Value="{DynamicResource DataGridRowBackgroundBrush}" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridRow:pointerover DataGridRowHeader /template/ Rectangle#BackgroundRectangle">
|
|
||||||
<Setter Property="Fill" Value="{DynamicResource DataGridRowHoveredBackgroundColor}" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridRowHeader:selected /template/ Rectangle#BackgroundRectangle">
|
|
||||||
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedUnfocusedBackgroundBrush}" />
|
|
||||||
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedUnfocusedBackgroundOpacity}" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridRow:pointerover DataGridRowHeader:selected /template/ Rectangle#BackgroundRectangle">
|
|
||||||
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedHoveredUnfocusedBackgroundBrush}" />
|
|
||||||
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedHoveredUnfocusedBackgroundOpacity}" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridRowHeader:selected:focus /template/ Rectangle#BackgroundRectangle">
|
|
||||||
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedBackgroundBrush}" />
|
|
||||||
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedBackgroundOpacity}" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridRow:pointerover DataGridRowHeader:selected:focus /template/ Rectangle#BackgroundRectangle">
|
|
||||||
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedHoveredBackgroundBrush}" />
|
|
||||||
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedHoveredBackgroundOpacity}" />
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="DataGridRowGroupHeader">
|
|
||||||
<Setter Property="Focusable" Value="False" />
|
|
||||||
<Setter Property="Foreground" Value="{DynamicResource DataGridRowGroupHeaderForegroundBrush}" />
|
|
||||||
<Setter Property="Background" Value="{DynamicResource DataGridRowGroupHeaderBackgroundBrush}" />
|
|
||||||
<Setter Property="FontSize" Value="15" />
|
|
||||||
<Setter Property="MinHeight" Value="32" />
|
|
||||||
<Setter Property="Template">
|
|
||||||
<ControlTemplate>
|
|
||||||
<DataGridFrozenGrid Name="PART_Root"
|
|
||||||
MinHeight="{TemplateBinding MinHeight}"
|
|
||||||
ColumnDefinitions="Auto,Auto,Auto,Auto,*"
|
|
||||||
RowDefinitions="*,Auto">
|
|
||||||
|
|
||||||
<Rectangle Name="IndentSpacer"
|
|
||||||
Grid.Column="1" />
|
|
||||||
<ToggleButton Name="ExpanderButton"
|
|
||||||
Grid.Column="2"
|
|
||||||
Width="12"
|
|
||||||
Height="12"
|
|
||||||
Margin="12,0,0,0"
|
|
||||||
BorderBrush="{TemplateBinding BorderBrush}"
|
|
||||||
BorderThickness="{TemplateBinding BorderThickness}"
|
|
||||||
Background="{TemplateBinding Background}"
|
|
||||||
CornerRadius="{TemplateBinding CornerRadius}"
|
|
||||||
Focusable="False"
|
|
||||||
Foreground="{TemplateBinding Foreground}" />
|
|
||||||
|
|
||||||
<StackPanel Grid.Column="3"
|
|
||||||
Orientation="Horizontal"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="12,0,0,0">
|
|
||||||
<TextBlock Name="PropertyNameElement"
|
|
||||||
Margin="4,0,0,0"
|
|
||||||
IsVisible="{TemplateBinding IsPropertyNameVisible}"
|
|
||||||
Foreground="{TemplateBinding Foreground}" />
|
|
||||||
<TextBlock Margin="4,0,0,0"
|
|
||||||
Text="{Binding Key}"
|
|
||||||
Foreground="{TemplateBinding Foreground}" />
|
|
||||||
<TextBlock Name="ItemCountElement"
|
|
||||||
Margin="4,0,0,0"
|
|
||||||
IsVisible="{TemplateBinding IsItemCountVisible}"
|
|
||||||
Foreground="{TemplateBinding Foreground}" />
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<Rectangle x:Name="CurrencyVisual"
|
|
||||||
Grid.ColumnSpan="5"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
Fill="Transparent"
|
|
||||||
IsHitTestVisible="False"
|
|
||||||
Stroke="{DynamicResource DataGridCurrencyVisualPrimaryBrush}"
|
|
||||||
StrokeThickness="1" />
|
|
||||||
<Grid x:Name="FocusVisual"
|
|
||||||
Grid.ColumnSpan="5"
|
|
||||||
IsHitTestVisible="False">
|
|
||||||
<Rectangle HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
Fill="Transparent"
|
|
||||||
IsHitTestVisible="False"
|
|
||||||
Stroke="{DynamicResource DataGridCellFocusVisualPrimaryBrush}"
|
|
||||||
StrokeThickness="2" />
|
|
||||||
<Rectangle Margin="2"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
Fill="Transparent"
|
|
||||||
IsHitTestVisible="False"
|
|
||||||
Stroke="{DynamicResource DataGridCellFocusVisualSecondaryBrush}"
|
|
||||||
StrokeThickness="1" />
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<DataGridRowHeader Name="PART_RowHeader"
|
|
||||||
Grid.RowSpan="2"
|
|
||||||
DataGridFrozenGrid.IsFrozen="True" />
|
|
||||||
|
|
||||||
<Rectangle x:Name="PART_BottomGridLine"
|
|
||||||
Grid.Row="1"
|
|
||||||
Grid.ColumnSpan="5"
|
|
||||||
Height="1" />
|
|
||||||
</DataGridFrozenGrid>
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="DataGridRowGroupHeader /template/ ToggleButton#ExpanderButton">
|
|
||||||
<Setter Property="Template">
|
|
||||||
<ControlTemplate>
|
|
||||||
<Border Grid.Column="0"
|
|
||||||
Width="12"
|
|
||||||
Height="12"
|
|
||||||
Background="Transparent"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
VerticalAlignment="Center">
|
|
||||||
<Path Fill="{TemplateBinding Foreground}"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Stretch="Uniform" />
|
|
||||||
</Border>
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="DataGridRowGroupHeader /template/ ToggleButton#ExpanderButton /template/ Path">
|
|
||||||
<Setter Property="Data" Value="{StaticResource DataGridRowGroupHeaderIconOpenedPath}" />
|
|
||||||
<Setter Property="Stretch" Value="Uniform" />
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="DataGridRowGroupHeader /template/ ToggleButton#ExpanderButton:checked /template/ Path">
|
|
||||||
<Setter Property="Data" Value="{StaticResource DataGridRowGroupHeaderIconClosedPath}" />
|
|
||||||
<Setter Property="Stretch" Value="UniformToFill" />
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="DataGridRowGroupHeader /template/ DataGridFrozenGrid#PART_Root">
|
|
||||||
<Setter Property="Background" Value="{Binding $parent[DataGridRowGroupHeader].Background}" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridRowGroupHeader:pointerover /template/ DataGridFrozenGrid#PART_Root">
|
|
||||||
<Setter Property="Background" Value="{DynamicResource DataGridRowGroupHeaderHoveredBackgroundBrush}" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridRowGroupHeader:pressed /template/ DataGridFrozenGrid#PART_Root">
|
|
||||||
<Setter Property="Background" Value="{DynamicResource DataGridRowGroupHeaderPressedBackgroundBrush}" />
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="DataGridRowGroupHeader /template/ Rectangle#CurrencyVisual">
|
|
||||||
<Setter Property="IsVisible" Value="False" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridRowGroupHeader /template/ Grid#FocusVisual">
|
|
||||||
<Setter Property="IsVisible" Value="False" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGridRowGroupHeader:current /template/ Rectangle#CurrencyVisual">
|
|
||||||
<Setter Property="IsVisible" Value="True" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGrid:focus DataGridRowGroupHeader:current /template/ Grid#FocusVisual">
|
|
||||||
<Setter Property="IsVisible" Value="True" />
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="DataGrid">
|
|
||||||
<Setter Property="RowBackground" Value="Transparent" />
|
|
||||||
<Setter Property="AlternatingRowBackground" Value="Transparent" />
|
|
||||||
<Setter Property="HeadersVisibility" Value="Column" />
|
|
||||||
<Setter Property="HorizontalScrollBarVisibility" Value="Auto" />
|
|
||||||
<Setter Property="VerticalScrollBarVisibility" Value="Auto" />
|
|
||||||
<Setter Property="SelectionMode" Value="Extended" />
|
|
||||||
<Setter Property="GridLinesVisibility" Value="None" />
|
|
||||||
<Setter Property="HorizontalGridLinesBrush" Value="{DynamicResource DataGridGridLinesBrush}" />
|
|
||||||
<Setter Property="VerticalGridLinesBrush" Value="{DynamicResource DataGridGridLinesBrush}" />
|
|
||||||
<Setter Property="DropLocationIndicatorTemplate">
|
|
||||||
<Template>
|
|
||||||
<Rectangle Fill="{DynamicResource DataGridDropLocationIndicatorBackground}"
|
|
||||||
Width="2" />
|
|
||||||
</Template>
|
|
||||||
</Setter>
|
|
||||||
<Setter Property="Template">
|
|
||||||
<ControlTemplate>
|
|
||||||
<Border x:Name="DataGridBorder"
|
|
||||||
Background="{TemplateBinding Background}"
|
|
||||||
BorderBrush="{TemplateBinding BorderBrush}"
|
|
||||||
BorderThickness="{TemplateBinding BorderThickness}"
|
|
||||||
CornerRadius="{TemplateBinding CornerRadius}">
|
|
||||||
<Grid ColumnDefinitions="Auto,*,Auto" RowDefinitions="Auto,*,Auto,Auto">
|
|
||||||
<Grid.Resources>
|
|
||||||
<ControlTemplate x:Key="TopLeftHeaderTemplate"
|
|
||||||
TargetType="DataGridColumnHeader">
|
|
||||||
<Grid x:Name="TopLeftHeaderRoot"
|
|
||||||
RowDefinitions="*,*,Auto">
|
|
||||||
<Border Grid.RowSpan="2"
|
|
||||||
BorderThickness="0,0,1,0"
|
|
||||||
BorderBrush="{DynamicResource DataGridGridLinesBrush}" />
|
|
||||||
<Rectangle Grid.RowSpan="2"
|
|
||||||
VerticalAlignment="Bottom"
|
|
||||||
StrokeThickness="1"
|
|
||||||
Height="1"
|
|
||||||
Fill="{DynamicResource DataGridGridLinesBrush}" />
|
|
||||||
</Grid>
|
|
||||||
</ControlTemplate>
|
|
||||||
<ControlTemplate x:Key="TopRightHeaderTemplate"
|
|
||||||
TargetType="DataGridColumnHeader">
|
|
||||||
<Grid x:Name="RootElement" />
|
|
||||||
</ControlTemplate>
|
|
||||||
</Grid.Resources>
|
|
||||||
|
|
||||||
<DataGridColumnHeader Name="PART_TopLeftCornerHeader"
|
|
||||||
Template="{StaticResource TopLeftHeaderTemplate}" />
|
|
||||||
<DataGridColumnHeadersPresenter Name="PART_ColumnHeadersPresenter"
|
|
||||||
Grid.Column="1"
|
|
||||||
Grid.ColumnSpan="2" />
|
|
||||||
<!--<DataGridColumnHeader Name="PART_TopRightCornerHeader"
|
|
||||||
Grid.Column="2"
|
|
||||||
Template="{StaticResource TopRightHeaderTemplate}" />-->
|
|
||||||
<Rectangle Name="PART_ColumnHeadersAndRowsSeparator"
|
|
||||||
Grid.ColumnSpan="3"
|
|
||||||
VerticalAlignment="Bottom"
|
|
||||||
Height="1"
|
|
||||||
Fill="{DynamicResource DataGridGridLinesBrush}" />
|
|
||||||
|
|
||||||
<DataGridRowsPresenter Name="PART_RowsPresenter"
|
|
||||||
Grid.Row="1"
|
|
||||||
Grid.RowSpan="2"
|
|
||||||
Grid.ColumnSpan="3">
|
|
||||||
<DataGridRowsPresenter.GestureRecognizers>
|
|
||||||
<ScrollGestureRecognizer CanHorizontallyScroll="True" CanVerticallyScroll="True" />
|
|
||||||
</DataGridRowsPresenter.GestureRecognizers>
|
|
||||||
</DataGridRowsPresenter>
|
|
||||||
<Rectangle Name="PART_BottomRightCorner"
|
|
||||||
Fill="{DynamicResource DataGridScrollBarsSeparatorBackground}"
|
|
||||||
Grid.Column="2"
|
|
||||||
Grid.Row="2" />
|
|
||||||
<!--<Rectangle Name="BottomLeftCorner"
|
|
||||||
Fill="{DynamicResource DataGridScrollBarsSeparatorBackground}"
|
|
||||||
Grid.Row="2"
|
|
||||||
Grid.ColumnSpan="2" />-->
|
|
||||||
<ScrollBar Name="PART_VerticalScrollbar"
|
|
||||||
Orientation="Vertical"
|
|
||||||
Grid.Column="2"
|
|
||||||
Grid.Row="1"
|
|
||||||
Width="{DynamicResource ScrollBarSize}" />
|
|
||||||
|
|
||||||
<Grid Grid.Column="1"
|
|
||||||
Grid.Row="2"
|
|
||||||
ColumnDefinitions="Auto,*">
|
|
||||||
<Rectangle Name="PART_FrozenColumnScrollBarSpacer" />
|
|
||||||
<ScrollBar Name="PART_HorizontalScrollbar"
|
|
||||||
Grid.Column="1"
|
|
||||||
Orientation="Horizontal"
|
|
||||||
Height="{DynamicResource ScrollBarSize}" />
|
|
||||||
</Grid>
|
|
||||||
<Border x:Name="PART_DisabledVisualElement"
|
|
||||||
Grid.ColumnSpan="3"
|
|
||||||
Grid.RowSpan="4"
|
|
||||||
IsHitTestVisible="False"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Stretch"
|
|
||||||
CornerRadius="2"
|
|
||||||
Background="{DynamicResource DataGridDisabledVisualElementBackground}"
|
|
||||||
IsVisible="{Binding !$parent[DataGrid].IsEnabled}" />
|
|
||||||
</Grid>
|
|
||||||
</Border>
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
|
|
||||||
<Style Selector="DataGrid:empty-columns /template/ DataGridColumnHeader#PART_TopLeftCornerHeader">
|
|
||||||
<Setter Property="IsVisible" Value="False" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGrid:empty-columns /template/ DataGridColumnHeadersPresenter#PART_ColumnHeadersPresenter">
|
|
||||||
<Setter Property="IsVisible" Value="False" />
|
|
||||||
</Style>
|
|
||||||
<Style Selector="DataGrid:empty-columns /template/ Rectangle#PART_ColumnHeadersAndRowsSeparator">
|
|
||||||
<Setter Property="IsVisible" Value="False" />
|
|
||||||
</Style>
|
|
||||||
</Styles>
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
|
||||||
<Styles.Resources>
|
|
||||||
<Color x:Key="SeriesEntryGridBackgroundColor">#cdffcd</Color>
|
|
||||||
|
|
||||||
<SolidColorBrush x:Key="SeriesEntryGridBackgroundBrush" Opacity="0.5" Color="{StaticResource SeriesEntryGridBackgroundColor}" />
|
|
||||||
<SolidColorBrush x:Key="ProcessQueueBookFailedBrush" Color="LightCoral" />
|
|
||||||
<SolidColorBrush x:Key="ProcessQueueBookCompletedBrush" Color="PaleGreen" />
|
|
||||||
<SolidColorBrush x:Key="ProcessQueueBookCancelledBrush" Color="Khaki" />
|
|
||||||
<SolidColorBrush x:Key="ProcessQueueBookDefaultBrush" Color="{StaticResource SystemAltHighColor}" />
|
|
||||||
<SolidColorBrush x:Key="ProcessQueueBookBorderBrush" Color="Gray" />
|
|
||||||
<SolidColorBrush x:Key="DisabledGrayBrush" Color="#60D3D3D3" />
|
|
||||||
|
|
||||||
</Styles.Resources>
|
|
||||||
<Style Selector="TextBox[IsReadOnly=true]">
|
|
||||||
<Setter Property="Background" Value="LightGray" />
|
|
||||||
<Setter Property="CaretBrush" Value="#00000000" />
|
|
||||||
</Style>
|
|
||||||
</Styles>
|
|
||||||
69
Source/LibationAvalonia/Assets/LibationVectorIcons.xaml
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:collections="using:Avalonia.Collections">
|
||||||
|
<Styles.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<StreamGeometry x:Key="LeftArrows">M30,0 H60 L30,50 L60,100 H30 L0,50 M85,0 H115 L85,50 L115,100 H85 L55,50 M140,0 H170 L140,50 L170,100 H140 L110,50</StreamGeometry>
|
||||||
|
<StreamGeometry x:Key="FirstButtonIcon">M0,0 H100 V12 H53 L100,46 H0 L47,12 H0</StreamGeometry>
|
||||||
|
<StreamGeometry x:Key="UpButtonIcon">M0,36.66 L50,0 L100,36.66</StreamGeometry>
|
||||||
|
<StreamGeometry x:Key="DownButtonIcon">M0,0 L100,0 L50,36.66</StreamGeometry>
|
||||||
|
<StreamGeometry x:Key="LastButtonIcon">M0,0 H100 L53,34 H100 V46 H0 V34 H47</StreamGeometry>
|
||||||
|
<StreamGeometry x:Key="CancelButtonIcon">M30,0 H50 V30 H80 V50 H50 V80 H30 V50 H0 V30 H30</StreamGeometry>
|
||||||
|
<StreamGeometry x:Key="QueuedIcon">M31,0 H49 V100 H31 M58,0 H76 V100 H58 M85,0 H103 V100 H85 M8,85 V122 H129 V85 H117 V109 H20 V85 H8 M0,36 V66 L24,51 M114,36 V66 L138,51</StreamGeometry>
|
||||||
|
<StreamGeometry x:Key="QueueCompletedIcon">M0,0 H100 V100 H0 V0 M2,50 L36,82 L 93,27 L81,15 L36,59 L14,38</StreamGeometry>
|
||||||
|
<StreamGeometry x:Key="QueueErrorIcon">M0,0 H100 V100 H0 V0 M15,71 L29,85 L50,64 L71,85 L85,71 L64,50 L85,29 L71,15 L50,36 L29,15 L15,29 L36,50</StreamGeometry>
|
||||||
|
<StreamGeometry x:Key="BookErrorIcon">M32,0 a 32,32 0 0 1 0,64 a 32,32 0 0 1 0,-64 m 0,4 a 28,28 0 0 1 0,56 a 28,28 0 0 1 0,-56 m-21,24 h42 a 1,1 0 0 1 1,1 v6 a 1,1 0 0 1 -1,1 h-42 a 1,1 0 0 1 -1,-1 v-6 a 1,1 0 0 1 1,-1</StreamGeometry>
|
||||||
|
|
||||||
|
<RotateTransform x:Key="Rotate45Transform" Angle="45" />
|
||||||
|
|
||||||
|
<StreamGeometry x:Key="EditTagsIcon">
|
||||||
|
M39,35 L50,24 H11
|
||||||
|
A 11,11 0 0 0 0,35 V89 A 11,11 0 0 0 11,100 H64 A 11,11 0 0 0 75,89 V52 L64,63 V89 H11 V35
|
||||||
|
M 51,65 H36 V50
|
||||||
|
M 90.5,26.5 L55,62 L 39,45 L74,10
|
||||||
|
M 78,6 L81.5,2.5 A 8,8 0 0 1 91.5,2 L98.5,9 A 8,8 0 0 1 97.5,19.5 L94,23
|
||||||
|
</StreamGeometry>
|
||||||
|
|
||||||
|
<StreamGeometry x:Key="CollapseIcon">
|
||||||
|
M0,2 A 2,2 0 0 1 2,0 H62 A2,2 0 0 1 64,2 V62 A 2,2 0 0 1 62,64 H 2 A 2,2 0 0 1 0,62 V2
|
||||||
|
M 2,2 H62 V62 H2 V2
|
||||||
|
M11,28 h42 a 1,1 0 0 1 1,1 v6 a 1,1 0 0 1 -1,1 h-42 a 1,1 0 0 1 -1,-1 v-6 a 1,1 0 0 1 1,-1
|
||||||
|
</StreamGeometry>
|
||||||
|
<StreamGeometry x:Key="VerticalBarIcon">M28,53 v-42 a 1,1 0 0 1 1,-1 h6 a 1,1 0 0 1 1,1 v42 a 1,1 0 0 1 -1,1 h-6 a 1,1 0 0 1 -1,-1</StreamGeometry>
|
||||||
|
|
||||||
|
|
||||||
|
<CombinedGeometry x:Key="ExpandIcon" Geometry1="{StaticResource CollapseIcon}" Geometry2="{StaticResource VerticalBarIcon}" />
|
||||||
|
|
||||||
|
<StreamGeometry x:Key="StoplightBodyIcon">
|
||||||
|
M0,12 A 12,12 0 0 1 12,0 H34 A 12,12 0 0 1 46,12 V88 A 12,12 0 0 1 34,100 H12 A 12,12 0 0 1 0,88 V12
|
||||||
|
M20,8 H26 A 12,12 0 0 1 26,32 H20 A 12,12 0 0 1 20,8
|
||||||
|
M20,38 H26 A 12,12 0 0 1 26,62 H20 A 12,12 0 0 1 20,38
|
||||||
|
M20,68 H26 A 12,12 0 0 1 26,92 H20 A 12,12 0 0 1 20,68
|
||||||
|
</StreamGeometry>
|
||||||
|
|
||||||
|
<StreamGeometry x:Key="PdfDownloadedIcon">
|
||||||
|
M4,38.5 H3 A 3,3 0 0 1 0,35.5 V21.4 A 3,3 0 0 1 3,18.4 H4 V2 A 2,2 0 0 1 6,0 H30.5 L41,12 V18.4 A 3,3 0 0 1 45,21.4 V35.5 A 3,3 0 0 1 42,38.5 H41 V48.5 A 2,2 0 0 1 39,50.5 H6 A 2,2 0 0 1 4,48.5
|
||||||
|
M6,38.5 H39 V48.5 H6 V38.5
|
||||||
|
M6,18.4 V2 H29 V12 A 1,1 0 0 0 30,13 H39 V18.4
|
||||||
|
M 4.3179,36 c 0,0 0.122,-14.969 0.122,-14.969 1.469,-0.194 2.939,-0.388 4.5,-0.362 1.561,0.026 3.214,0.27 4.357,0.944 1.143,0.674 1.775,1.776 2.015,2.959 0.24,1.184 0.087,2.449 -0.5,3.52 -0.587,1.071 -1.607,1.949 -2.816,2.352 -1.209,0.403 -2.607,0.332 -4.005,0.26 0,0 -0.031,5.265 -0.031,5.265 0,0 -3.673,0.122 -3.673,0.122 0,0 0.031,-0.092 0.031,-0.092
|
||||||
|
m 3.643,-12.428 c 0,0 0.031,4.286 0.031,4.286 0.735,0.051 1.47,0.102 2.107,-0.056 0.638,-0.158 1.178,-0.526 1.459,-1.122 0.281,-0.597 0.301,-1.423 0.01,-2.005 -0.291,-0.582 -0.893,-0.918 -1.546,-1.061 -0.653,-0.143 -1.357,-0.092 -1.709,-0.066 -0.352,0.026 -0.352,0.026 -0.352,0.026
|
||||||
|
m 9.428,12.428 c 2.265,0.245 4.531,0.49 6.674,0.066 2.143,-0.424 4.163,-1.515 5.285,-3.081 1.122,-1.566 1.347,-3.607 1.27,-5.306 -0.076,-1.699 -0.454,-3.056 -1.454,-4.219 -1,-1.163 -2.622,-2.133 -4.704,-2.505 -2.082,-0.373 -4.623,-0.148 -7.164,0.076 0,0 0.092,14.969 0.092,14.969
|
||||||
|
m 3.49,-12.398 c 0,0 0,9.673 0,9.673 0.888,0.02 1.776,0.041 2.653,-0.179 0.877,-0.219 1.745,-0.679 2.367,-1.541 0.622,-0.862 1,-2.127 0.98,-3.403 -0.02,-1.275 -0.439,-2.561 -1.193,-3.337 -0.755,-0.776 -1.847,-1.041 -2.704,-1.158 -0.857,-0.117 -1.48,-0.087 -2.102,-0.056
|
||||||
|
m 11.908,12.245 v-14.785 h8.969 v2.51 h-5.786 v3.612 h5.388 v2.51 h-5.449 v6.092
|
||||||
|
</StreamGeometry>
|
||||||
|
|
||||||
|
<StreamGeometry x:Key="PdfDownArrowIcon">
|
||||||
|
M29,44 V58.7498 H35.0491 A 1.5,1.5 0 0 1 36.1342,61.2861 L23.5607,73.8595 A 1.5,1.5 0 0 1 21.4393,73.8595 L8.8658,61.2861 A 1.5,1.5 0 0 1 9.9509,58.7498 H16 V44 A 1.5,1.5 0 0 1 17.5,42.5 H27.5 A 1.5,1.5 0 0 1 29,44
|
||||||
|
</StreamGeometry>
|
||||||
|
|
||||||
|
<CombinedGeometry x:Key="PdfNotDownloadedIcon" Geometry1="{StaticResource PdfDownloadedIcon}" Geometry2="{StaticResource PdfDownArrowIcon}" />
|
||||||
|
|
||||||
|
<StreamGeometry x:Key="ImportIcon">
|
||||||
|
M5.65,4.3 h-2.75 a2.9,2.25 0 0 0 -2.9,2.25 v7.2
|
||||||
|
a2.9,2.25 0 0 0 2.9,2.25 h10.2 a2.9,2.25 0 0 0 2.9,-2.25 v-7.2 a2.9,2.25 0 0 0 -2.9,-2.25
|
||||||
|
h-2.75 v1.6 h2.75 a1.3,0.65 0 0 1 1.3,0.65 v7.2 a1.3,0.65 0 0 1 -1.3,0.65 h-10.2 a1.3,0.65 0 0 1 -1.3,-0.65 v-7.2 a1.3,0.65 0 0 1 1.3,-0.65 h2.75 v-1.6
|
||||||
|
M7.2,0.8 a 0.8,0.8 0 0 1 1.6,0 v8 l0.9929,-0.9929 a 0.8,0.8 0 0 1 1.1314,1.1314 l-2.3586,2.3586
|
||||||
|
a 0.8,0.8 0 0 1 -1.1314,0 l-2.3586,-2.3586 a 0.8,0.8 0 0 1 1.1314,-1.1314 l0.9929,0.9929 v8
|
||||||
|
</StreamGeometry>
|
||||||
|
</ResourceDictionary>
|
||||||
|
</Styles.Resources>
|
||||||
|
</Styles>
|
||||||
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 314 B |
|
Before Width: | Height: | Size: 573 B |
|
Before Width: | Height: | Size: 747 B |
|
Before Width: | Height: | Size: 813 B |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 482 B |
|
Before Width: | Height: | Size: 383 B |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
@ -1,6 +1,7 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Avalonia.Media.Imaging;
|
using Avalonia.Media.Imaging;
|
||||||
|
using Avalonia.VisualTree;
|
||||||
using LibationAvalonia.Dialogs;
|
using LibationAvalonia.Dialogs;
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -13,7 +14,7 @@ namespace LibationAvalonia
|
|||||||
=> GetBrushFromResources(name, Brushes.Transparent);
|
=> GetBrushFromResources(name, Brushes.Transparent);
|
||||||
public static IBrush GetBrushFromResources(string name, IBrush defaultBrush)
|
public static IBrush GetBrushFromResources(string name, IBrush defaultBrush)
|
||||||
{
|
{
|
||||||
if (App.Current.Styles.TryGetResource(name, out var value) && value is IBrush brush)
|
if (App.Current.TryGetResource(name, App.Current.ActualThemeVariant, out var value) && value is IBrush brush)
|
||||||
return brush;
|
return brush;
|
||||||
return defaultBrush;
|
return defaultBrush;
|
||||||
}
|
}
|
||||||
@ -21,7 +22,7 @@ namespace LibationAvalonia
|
|||||||
public static Task<DialogResult> ShowDialogAsync(this DialogWindow dialogWindow, Window owner = null)
|
public static Task<DialogResult> ShowDialogAsync(this DialogWindow dialogWindow, Window owner = null)
|
||||||
=> dialogWindow.ShowDialog<DialogResult>(owner ?? App.MainWindow);
|
=> dialogWindow.ShowDialog<DialogResult>(owner ?? App.MainWindow);
|
||||||
|
|
||||||
public static Window GetParentWindow(this IControl control) => control.VisualRoot as Window;
|
public static Window GetParentWindow(this Control control) => control.GetVisualRoot() as Window;
|
||||||
|
|
||||||
|
|
||||||
private static Bitmap defaultImage;
|
private static Bitmap defaultImage;
|
||||||
|
|||||||
@ -5,7 +5,7 @@ namespace LibationAvalonia.Controls
|
|||||||
{
|
{
|
||||||
public class DataGridCheckBoxColumnExt : DataGridCheckBoxColumn
|
public class DataGridCheckBoxColumnExt : DataGridCheckBoxColumn
|
||||||
{
|
{
|
||||||
protected override IControl GenerateEditingElementDirect(DataGridCell cell, object dataItem)
|
protected override Control GenerateEditingElementDirect(DataGridCell cell, object dataItem)
|
||||||
{
|
{
|
||||||
//Only SeriesEntry types have three-state checks, individual LibraryEntry books are binary.
|
//Only SeriesEntry types have three-state checks, individual LibraryEntry books are binary.
|
||||||
var ele = base.GenerateEditingElementDirect(cell, dataItem) as CheckBox;
|
var ele = base.GenerateEditingElementDirect(cell, dataItem) as CheckBox;
|
||||||
|
|||||||
@ -18,7 +18,7 @@ namespace LibationAvalonia.Controls
|
|||||||
BindingTarget = MyRatingCellEditor.RatingProperty;
|
BindingTarget = MyRatingCellEditor.RatingProperty;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IControl GenerateElement(DataGridCell cell, object dataItem)
|
protected override Control GenerateElement(DataGridCell cell, object dataItem)
|
||||||
{
|
{
|
||||||
var myRatingElement = new MyRatingCellEditor
|
var myRatingElement = new MyRatingCellEditor
|
||||||
{
|
{
|
||||||
@ -41,7 +41,7 @@ namespace LibationAvalonia.Controls
|
|||||||
return myRatingElement;
|
return myRatingElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IControl GenerateEditingElementDirect(DataGridCell cell, object dataItem)
|
protected override Control GenerateEditingElementDirect(DataGridCell cell, object dataItem)
|
||||||
{
|
{
|
||||||
var myRatingElement = new MyRatingCellEditor
|
var myRatingElement = new MyRatingCellEditor
|
||||||
{
|
{
|
||||||
@ -57,12 +57,12 @@ namespace LibationAvalonia.Controls
|
|||||||
return myRatingElement;
|
return myRatingElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override object PrepareCellForEdit(IControl editingElement, RoutedEventArgs editingEventArgs)
|
protected override object PrepareCellForEdit(Control editingElement, RoutedEventArgs editingEventArgs)
|
||||||
=> editingElement is MyRatingCellEditor myRating
|
=> editingElement is MyRatingCellEditor myRating
|
||||||
? myRating.Rating
|
? myRating.Rating
|
||||||
: DefaultRating;
|
: DefaultRating;
|
||||||
|
|
||||||
protected override void CancelCellEdit(IControl editingElement, object uneditedValue)
|
protected override void CancelCellEdit(Control editingElement, object uneditedValue)
|
||||||
{
|
{
|
||||||
if (editingElement is MyRatingCellEditor myRating)
|
if (editingElement is MyRatingCellEditor myRating)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -6,7 +6,7 @@ namespace LibationAvalonia.Controls
|
|||||||
{
|
{
|
||||||
public partial class DataGridTemplateColumnExt : DataGridTemplateColumn
|
public partial class DataGridTemplateColumnExt : DataGridTemplateColumn
|
||||||
{
|
{
|
||||||
protected override IControl GenerateElement(DataGridCell cell, object dataItem)
|
protected override Control GenerateElement(DataGridCell cell, object dataItem)
|
||||||
{
|
{
|
||||||
cell?.AttachContextMenu();
|
cell?.AttachContextMenu();
|
||||||
return base.GenerateElement(cell, dataItem);
|
return base.GenerateElement(cell, dataItem);
|
||||||
|
|||||||
@ -97,12 +97,7 @@ namespace LibationAvalonia.Controls
|
|||||||
|
|
||||||
var selectedFolders = await (VisualRoot as Window).StorageProvider.OpenFolderPickerAsync(options);
|
var selectedFolders = await (VisualRoot as Window).StorageProvider.OpenFolderPickerAsync(options);
|
||||||
|
|
||||||
customStates.CustomDir =
|
customStates.CustomDir = selectedFolders.SingleOrDefault()?.Path?.LocalPath ?? customStates.CustomDir;
|
||||||
selectedFolders
|
|
||||||
.SingleOrDefault()?.
|
|
||||||
TryGetUri(out var uri) is true
|
|
||||||
? uri.LocalPath
|
|
||||||
: customStates.CustomDir;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckStates_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
private void CheckStates_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||||
@ -180,10 +175,5 @@ namespace LibationAvalonia.Controls
|
|||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -90,10 +90,5 @@ namespace LibationAvalonia.Controls
|
|||||||
get => GetValue(SubDirectoryProperty);
|
get => GetValue(SubDirectoryProperty);
|
||||||
set => SetValue(SubDirectoryProperty, value);
|
set => SetValue(SubDirectoryProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,7 @@
|
|||||||
VerticalAlignment="Top">
|
VerticalAlignment="Top">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Padding="4,0,4,0"
|
Padding="4,0,4,0"
|
||||||
Background="{StaticResource SystemAltHighColor}"
|
Background="{DynamicResource SystemAltHighColor}"
|
||||||
Text="{TemplateBinding Label}"
|
Text="{TemplateBinding Label}"
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@ -29,10 +29,5 @@ namespace LibationAvalonia.Controls
|
|||||||
get { return GetValue(LabelProperty); }
|
get { return GetValue(LabelProperty); }
|
||||||
set { SetValue(LabelProperty, value); }
|
set { SetValue(LabelProperty, value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
x:Class="LibationAvalonia.Controls.LinkLabel">
|
x:Class="LibationAvalonia.Controls.LinkLabel">
|
||||||
<TextBlock.Styles>
|
<TextBlock.Styles>
|
||||||
<Style Selector="TextBlock">
|
<Style Selector="TextBlock">
|
||||||
<Setter Property="Foreground" Value="Blue"/>
|
<Setter Property="Foreground" Value="{DynamicResource HyperlinkNew}"/>
|
||||||
<Setter Property="TextDecorations" Value="Underline"/>
|
<Setter Property="TextDecorations" Value="Underline"/>
|
||||||
</Style>
|
</Style>
|
||||||
</TextBlock.Styles>
|
</TextBlock.Styles>
|
||||||
|
|||||||
@ -2,6 +2,7 @@ using Avalonia;
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.Media;
|
||||||
using Avalonia.Styling;
|
using Avalonia.Styling;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
@ -14,7 +15,14 @@ namespace LibationAvalonia.Controls
|
|||||||
public LinkLabel()
|
public LinkLabel()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
Tapped += LinkLabel_Tapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void LinkLabel_Tapped(object sender, TappedEventArgs e)
|
||||||
|
{
|
||||||
|
Foreground = App.HyperlinkVisited;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnPointerEntered(PointerEventArgs e)
|
protected override void OnPointerEntered(PointerEventArgs e)
|
||||||
{
|
{
|
||||||
this.Cursor = HandCursor;
|
this.Cursor = HandCursor;
|
||||||
@ -25,10 +33,5 @@ namespace LibationAvalonia.Controls
|
|||||||
this.Cursor = Cursor.Default;
|
this.Cursor = Cursor.Default;
|
||||||
base.OnPointerExited(e);
|
base.OnPointerExited(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,10 +26,5 @@ namespace LibationAvalonia.Controls
|
|||||||
|
|
||||||
base.OnPointerWheelChanged(e);
|
base.OnPointerWheelChanged(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,9 +33,9 @@
|
|||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
Width="60"
|
|
||||||
Height="30"
|
|
||||||
Content="X"
|
Content="X"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
HorizontalContentAlignment="Center"
|
HorizontalContentAlignment="Center"
|
||||||
IsEnabled="{Binding !IsDefault}"
|
IsEnabled="{Binding !IsDefault}"
|
||||||
Click="DeleteButton_Clicked" />
|
Click="DeleteButton_Clicked" />
|
||||||
@ -49,9 +49,10 @@
|
|||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
Width="60"
|
|
||||||
Height="30"
|
|
||||||
Content="Export"
|
Content="Export"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
HorizontalContentAlignment="Center"
|
||||||
IsEnabled="{Binding !IsDefault}"
|
IsEnabled="{Binding !IsDefault}"
|
||||||
ToolTip.Tip="Export account authorization to audible-cli"
|
ToolTip.Tip="Export account authorization to audible-cli"
|
||||||
Click="ExportButton_Clicked" />
|
Click="ExportButton_Clicked" />
|
||||||
|
|||||||
@ -129,16 +129,16 @@ namespace LibationAvalonia.Dialogs
|
|||||||
|
|
||||||
string audibleAppDataDir = GetAudibleCliAppDataPath();
|
string audibleAppDataDir = GetAudibleCliAppDataPath();
|
||||||
if (Directory.Exists(audibleAppDataDir))
|
if (Directory.Exists(audibleAppDataDir))
|
||||||
openFileDialogOptions.SuggestedStartLocation = new BclStorageFolder(audibleAppDataDir);
|
openFileDialogOptions.SuggestedStartLocation = await StorageProvider.TryGetFolderFromPathAsync(audibleAppDataDir);
|
||||||
|
|
||||||
var selectedFiles = await StorageProvider.OpenFilePickerAsync(openFileDialogOptions);
|
var selectedFiles = await StorageProvider.OpenFilePickerAsync(openFileDialogOptions);
|
||||||
var selectedFile = selectedFiles.SingleOrDefault();
|
var selectedFile = selectedFiles.SingleOrDefault()?.TryGetLocalPath();
|
||||||
|
|
||||||
if (selectedFile?.TryGetUri(out var uri) is not true) return;
|
if (selectedFile is null) return;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var jsonText = File.ReadAllText(uri.LocalPath);
|
var jsonText = File.ReadAllText(selectedFile);
|
||||||
var mkbAuth = Mkb79Auth.FromJson(jsonText);
|
var mkbAuth = Mkb79Auth.FromJson(jsonText);
|
||||||
var account = await mkbAuth.ToAccountAsync();
|
var account = await mkbAuth.ToAccountAsync();
|
||||||
|
|
||||||
@ -159,7 +159,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
{
|
{
|
||||||
await MessageBox.ShowAdminAlert(
|
await MessageBox.ShowAdminAlert(
|
||||||
this,
|
this,
|
||||||
$"An error occurred while importing an account from:\r\n{uri.LocalPath}\r\n\r\nIs the file encrypted?",
|
$"An error occurred while importing an account from:\r\n{selectedFile}\r\n\r\nIs the file encrypted?",
|
||||||
"Error Importing Account",
|
"Error Importing Account",
|
||||||
ex);
|
ex);
|
||||||
}
|
}
|
||||||
@ -196,12 +196,6 @@ namespace LibationAvalonia.Dialogs
|
|||||||
public async void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public async void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
=> await SaveAndCloseAsync();
|
=> await SaveAndCloseAsync();
|
||||||
|
|
||||||
|
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void persist(AccountsSettings accountsSettings)
|
private void persist(AccountsSettings accountsSettings)
|
||||||
{
|
{
|
||||||
var existingAccounts = accountsSettings.Accounts;
|
var existingAccounts = accountsSettings.Accounts;
|
||||||
@ -293,20 +287,20 @@ namespace LibationAvalonia.Dialogs
|
|||||||
string audibleAppDataDir = GetAudibleCliAppDataPath();
|
string audibleAppDataDir = GetAudibleCliAppDataPath();
|
||||||
|
|
||||||
if (Directory.Exists(audibleAppDataDir))
|
if (Directory.Exists(audibleAppDataDir))
|
||||||
options.SuggestedStartLocation = new BclStorageFolder(audibleAppDataDir);
|
options.SuggestedStartLocation = await StorageProvider.TryGetFolderFromPathAsync(audibleAppDataDir);
|
||||||
|
|
||||||
var selectedFile = await StorageProvider.SaveFilePickerAsync(options);
|
var selectedFile = (await StorageProvider.SaveFilePickerAsync(options))?.TryGetLocalPath();
|
||||||
|
|
||||||
if (selectedFile?.TryGetUri(out var uri) is not true) return;
|
if (selectedFile is null) return;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var mkbAuth = Mkb79Auth.FromAccount(account);
|
var mkbAuth = Mkb79Auth.FromAccount(account);
|
||||||
var jsonText = mkbAuth.ToJson();
|
var jsonText = mkbAuth.ToJson();
|
||||||
|
|
||||||
File.WriteAllText(uri.LocalPath, jsonText);
|
File.WriteAllText(selectedFile, jsonText);
|
||||||
|
|
||||||
await MessageBox.Show(this, $"Successfully exported {account.AccountName} to\r\n\r\n{uri.LocalPath}", "Success!");
|
await MessageBox.Show(this, $"Successfully exported {account.AccountName} to\r\n\r\n{selectedFile}", "Success!");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -63,11 +63,6 @@ namespace LibationAvalonia.Dialogs
|
|||||||
public void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
=> SaveAndClose();
|
=> SaveAndClose();
|
||||||
|
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class BookDetailsDialogViewModel : ViewModelBase
|
private class BookDetailsDialogViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
public class liberatedComboBoxItem
|
public class liberatedComboBoxItem
|
||||||
|
|||||||
@ -172,23 +172,23 @@ namespace LibationAvalonia.Dialogs
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var selectedFile = await StorageProvider.SaveFilePickerAsync(saveFileDialog);
|
var selectedFile = (await StorageProvider.SaveFilePickerAsync(saveFileDialog))?.TryGetLocalPath();
|
||||||
|
|
||||||
if (selectedFile?.TryGetUri(out var uri) is not true) return;
|
if (selectedFile is null) return;
|
||||||
|
|
||||||
var ext = System.IO.Path.GetExtension(uri.LocalPath).ToLowerInvariant();
|
var ext = System.IO.Path.GetExtension(selectedFile).ToLowerInvariant();
|
||||||
|
|
||||||
switch (ext)
|
switch (ext)
|
||||||
{
|
{
|
||||||
case ".xlsx":
|
case ".xlsx":
|
||||||
default:
|
default:
|
||||||
await Task.Run(() => RecordExporter.ToXlsx(uri.LocalPath, records));
|
await Task.Run(() => RecordExporter.ToXlsx(selectedFile, records));
|
||||||
break;
|
break;
|
||||||
case ".csv":
|
case ".csv":
|
||||||
await Task.Run(() => RecordExporter.ToCsv(uri.LocalPath, records));
|
await Task.Run(() => RecordExporter.ToCsv(selectedFile, records));
|
||||||
break;
|
break;
|
||||||
case ".json":
|
case ".json":
|
||||||
await Task.Run(() => RecordExporter.ToJson(uri.LocalPath, libraryBook, records));
|
await Task.Run(() => RecordExporter.ToJson(selectedFile, libraryBook, records));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,11 @@
|
|||||||
SystemDecorations="None"
|
SystemDecorations="None"
|
||||||
Title="DescriptionDisplay">
|
Title="DescriptionDisplay">
|
||||||
|
|
||||||
|
<Window.Styles>
|
||||||
|
<Style Selector="TextBox[IsReadOnly=true] /template/ Border#PART_BorderElement">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource TextControlBackground}" />
|
||||||
|
</Style>
|
||||||
|
</Window.Styles>
|
||||||
<TextBox
|
<TextBox
|
||||||
Text="{Binding DescriptionText}"
|
Text="{Binding DescriptionText}"
|
||||||
IsReadOnly="True"
|
IsReadOnly="True"
|
||||||
|
|||||||
@ -52,11 +52,5 @@ namespace LibationAvalonia.Dialogs
|
|||||||
{
|
{
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,9 +33,10 @@
|
|||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
Width="55"
|
|
||||||
Height="30"
|
|
||||||
Content="X"
|
Content="X"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
HorizontalContentAlignment="Center"
|
||||||
IsEnabled="{Binding !IsDefault}"
|
IsEnabled="{Binding !IsDefault}"
|
||||||
Click="DeleteButton_Clicked" />
|
Click="DeleteButton_Clicked" />
|
||||||
|
|
||||||
@ -49,15 +50,15 @@
|
|||||||
Binding="{Binding FilterString, Mode=TwoWay}"
|
Binding="{Binding FilterString, Mode=TwoWay}"
|
||||||
Header="Filter"/>
|
Header="Filter"/>
|
||||||
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Header="Move
Up">
|
<DataGridTemplateColumn Header="Move
Up">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
Width="50"
|
|
||||||
Height="30"
|
|
||||||
Content="▲"
|
Content="▲"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
HorizontalContentAlignment="Center"
|
||||||
IsEnabled="{Binding !IsDefault}"
|
IsEnabled="{Binding !IsDefault}"
|
||||||
ToolTip.Tip="Export account authorization to audible-cli"
|
ToolTip.Tip="Export account authorization to audible-cli"
|
||||||
Click="MoveUpButton_Clicked" />
|
Click="MoveUpButton_Clicked" />
|
||||||
@ -73,9 +74,10 @@
|
|||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
Width="50"
|
|
||||||
Height="30"
|
|
||||||
Content="▼"
|
Content="▼"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
HorizontalContentAlignment="Center"
|
||||||
IsEnabled="{Binding !IsDefault}"
|
IsEnabled="{Binding !IsDefault}"
|
||||||
ToolTip.Tip="Export account authorization to audible-cli"
|
ToolTip.Tip="Export account authorization to audible-cli"
|
||||||
Click="MoveDownButton_Clicked" />
|
Click="MoveDownButton_Clicked" />
|
||||||
|
|||||||
@ -170,10 +170,5 @@ namespace LibationAvalonia.Dialogs
|
|||||||
public char Character => string.IsNullOrEmpty(_characterToReplace) ? default : _characterToReplace[0];
|
public char Character => string.IsNullOrEmpty(_characterToReplace) ? default : _characterToReplace[0];
|
||||||
public bool IsDefault { get; private set; }
|
public bool IsDefault { get; private set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,7 +23,6 @@
|
|||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Name="userEditTbox"
|
Name="userEditTbox"
|
||||||
FontFamily="{Binding FontFamily}"
|
|
||||||
Text="{Binding UserTemplateText, Mode=TwoWay}" />
|
Text="{Binding UserTemplateText, Mode=TwoWay}" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@ -75,7 +75,6 @@ namespace LibationAvalonia.Dialogs
|
|||||||
private class EditTemplateViewModel : ViewModels.ViewModelBase
|
private class EditTemplateViewModel : ViewModels.ViewModelBase
|
||||||
{
|
{
|
||||||
private readonly Configuration config;
|
private readonly Configuration config;
|
||||||
public FontFamily FontFamily { get; } = FontManager.Current.DefaultFontFamilyName;
|
|
||||||
public InlineCollection Inlines { get; } = new();
|
public InlineCollection Inlines { get; } = new();
|
||||||
public ITemplateEditor TemplateEditor { get; }
|
public ITemplateEditor TemplateEditor { get; }
|
||||||
public EditTemplateViewModel(Configuration configuration, ITemplateEditor templates)
|
public EditTemplateViewModel(Configuration configuration, ITemplateEditor templates)
|
||||||
@ -91,7 +90,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
.Cast<TemplateTags>()
|
.Cast<TemplateTags>()
|
||||||
.Select(
|
.Select(
|
||||||
t => new Tuple<string, string, string>(
|
t => new Tuple<string, string, string>(
|
||||||
$"<{t.TagName.Replace("->", "-\x200C>").Replace("<-", "<\x200C-")}>",
|
$"<{t.TagName}>",
|
||||||
t.Description,
|
t.Description,
|
||||||
t.DefaultValue)
|
t.DefaultValue)
|
||||||
)
|
)
|
||||||
|
|||||||
@ -20,12 +20,6 @@ namespace LibationAvalonia.Dialogs
|
|||||||
DataContext = _bitmapHolder;
|
DataContext = _bitmapHolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetCoverBytes(byte[] cover)
|
public void SetCoverBytes(byte[] cover)
|
||||||
{
|
{
|
||||||
_bitmapHolder.CoverImage = AvaloniaUtils.TryLoadImageOrDefault(cover);
|
_bitmapHolder.CoverImage = AvaloniaUtils.TryLoadImageOrDefault(cover);
|
||||||
@ -36,7 +30,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
var options = new FilePickerSaveOptions
|
var options = new FilePickerSaveOptions
|
||||||
{
|
{
|
||||||
Title = $"Save Sover Image",
|
Title = $"Save Sover Image",
|
||||||
SuggestedStartLocation = new Avalonia.Platform.Storage.FileIO.BclStorageFolder(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)),
|
SuggestedStartLocation = await StorageProvider.TryGetFolderFromPathAsync(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)),
|
||||||
SuggestedFileName = PictureFileName,
|
SuggestedFileName = PictureFileName,
|
||||||
DefaultExtension = "jpg",
|
DefaultExtension = "jpg",
|
||||||
ShowOverwritePrompt = true,
|
ShowOverwritePrompt = true,
|
||||||
@ -50,17 +44,17 @@ namespace LibationAvalonia.Dialogs
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var selectedFile = await StorageProvider.SaveFilePickerAsync(options);
|
var selectedFile = (await StorageProvider.SaveFilePickerAsync(options))?.TryGetLocalPath();
|
||||||
|
|
||||||
if (selectedFile?.TryGetUri(out var uri) is not true) return;
|
if (selectedFile is null) return;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_bitmapHolder.CoverImage.Save(uri.LocalPath);
|
_bitmapHolder.CoverImage.Save(selectedFile);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Serilog.Log.Logger.Error(ex, $"Failed to save picture to {uri.LocalPath}");
|
Serilog.Log.Logger.Error(ex, $"Failed to save picture to {selectedFile}");
|
||||||
await MessageBox.Show(this, $"An error was encountered while trying to save the picture\r\n\r\n{ex.Message}", "Failed to save picture", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
|
await MessageBox.Show(this, $"An error was encountered while trying to save the picture\r\n\r\n{ex.Message}", "Failed to save picture", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,10 +48,5 @@ namespace LibationAvalonia.Dialogs
|
|||||||
DialogResult = DialogResult.OK;
|
DialogResult = DialogResult.OK;
|
||||||
Close(DialogResult);
|
Close(DialogResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,10 +12,6 @@ namespace LibationAvalonia.Dialogs
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
DataContext = this;
|
DataContext = this;
|
||||||
}
|
}
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
=> SaveAndClose();
|
=> SaveAndClose();
|
||||||
|
|||||||
@ -46,10 +46,6 @@ namespace LibationAvalonia.Dialogs
|
|||||||
SelectedItem = BookStatuses[0] as liberatedComboBoxItem;
|
SelectedItem = BookStatuses[0] as liberatedComboBoxItem;
|
||||||
DataContext = this;
|
DataContext = this;
|
||||||
}
|
}
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
=> SaveAndClose();
|
=> SaveAndClose();
|
||||||
|
|||||||
@ -70,12 +70,12 @@ namespace LibationAvalonia.Dialogs
|
|||||||
{
|
{
|
||||||
Title = "Select the folder to search for audiobooks",
|
Title = "Select the folder to search for audiobooks",
|
||||||
AllowMultiple = false,
|
AllowMultiple = false,
|
||||||
SuggestedStartLocation = new BclStorageFolder(Configuration.Instance.Books.PathWithoutPrefix)
|
SuggestedStartLocation = await StorageProvider.TryGetFolderFromPathAsync(Configuration.Instance.Books.PathWithoutPrefix)
|
||||||
};
|
};
|
||||||
|
|
||||||
var selectedFolder = await StorageProvider.OpenFolderPickerAsync(folderPicker);
|
var selectedFolder = (await StorageProvider.OpenFolderPickerAsync(folderPicker))?.SingleOrDefault()?.TryGetLocalPath();
|
||||||
|
|
||||||
if (selectedFolder.FirstOrDefault().TryGetUri(out var uri) is not true || !Directory.Exists(uri.LocalPath))
|
if (selectedFolder is null || !Directory.Exists(selectedFolder))
|
||||||
{
|
{
|
||||||
await CancelAndCloseAsync();
|
await CancelAndCloseAsync();
|
||||||
return;
|
return;
|
||||||
@ -83,7 +83,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
|
|
||||||
using var context = DbContexts.GetContext();
|
using var context = DbContexts.GetContext();
|
||||||
|
|
||||||
await foreach (var book in AudioFileStorage.FindAudiobooksAsync(uri.LocalPath, tokenSource.Token))
|
await foreach (var book in AudioFileStorage.FindAudiobooksAsync(selectedFolder, tokenSource.Token))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@ -12,11 +12,6 @@ namespace LibationAvalonia.Dialogs.Login
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Task SaveAndCloseAsync()
|
protected override Task SaveAndCloseAsync()
|
||||||
{
|
{
|
||||||
Serilog.Log.Logger.Information("Approve button clicked");
|
Serilog.Log.Logger.Information("Approve button clicked");
|
||||||
|
|||||||
@ -25,7 +25,7 @@ namespace LibationAvalonia.Dialogs.Login
|
|||||||
{
|
{
|
||||||
var dialog = new LoginChoiceEagerDialog(_account);
|
var dialog = new LoginChoiceEagerDialog(_account);
|
||||||
|
|
||||||
if (await dialog.ShowDialogAsync() is not DialogResult.OK)
|
if (await dialog.ShowDialogAsync() is not DialogResult.OK || string.IsNullOrWhiteSpace(dialog.Password))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
switch (dialog.LoginMethod)
|
switch (dialog.LoginMethod)
|
||||||
|
|||||||
@ -49,11 +49,6 @@ namespace LibationAvalonia.Dialogs.Login
|
|||||||
Opened += (_, _) => (string.IsNullOrEmpty(password) ? passwordBox : captchaBox).Focus();
|
Opened += (_, _) => (string.IsNullOrEmpty(password) ? passwordBox : captchaBox).Focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task SaveAndCloseAsync()
|
protected override async Task SaveAndCloseAsync()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(_viewModel.Password))
|
if (string.IsNullOrWhiteSpace(_viewModel.Password))
|
||||||
|
|||||||
@ -31,12 +31,6 @@ namespace LibationAvalonia.Dialogs.Login
|
|||||||
DataContext = this;
|
DataContext = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected override Task SaveAndCloseAsync()
|
protected override Task SaveAndCloseAsync()
|
||||||
{
|
{
|
||||||
Serilog.Log.Logger.Information("Submit button clicked: {@DebugInfo}", new { email = Account?.AccountId?.ToMask(), passwordLength = Password?.Length });
|
Serilog.Log.Logger.Information("Submit button clicked: {@DebugInfo}", new { email = Account?.AccountId?.ToMask(), passwordLength = Password?.Length });
|
||||||
|
|||||||
@ -4,6 +4,7 @@ using Avalonia;
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace LibationAvalonia.Dialogs.Login
|
namespace LibationAvalonia.Dialogs.Login
|
||||||
{
|
{
|
||||||
@ -31,15 +32,21 @@ namespace LibationAvalonia.Dialogs.Login
|
|||||||
DataContext = this;
|
DataContext = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override async Task SaveAndCloseAsync()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(Password))
|
||||||
|
{
|
||||||
|
await MessageBox.Show(this, "Please enter your password");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await base.SaveAndCloseAsync();
|
||||||
|
}
|
||||||
|
|
||||||
public async void ExternalLoginLink_Tapped(object sender, Avalonia.Input.TappedEventArgs e)
|
public async void ExternalLoginLink_Tapped(object sender, Avalonia.Input.TappedEventArgs e)
|
||||||
{
|
{
|
||||||
LoginMethod = LoginMethod.External;
|
LoginMethod = LoginMethod.External;
|
||||||
await SaveAndCloseAsync();
|
await SaveAndCloseAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,10 +40,6 @@ namespace LibationAvalonia.Dialogs.Login
|
|||||||
Account = account;
|
Account = account;
|
||||||
DataContext = this;
|
DataContext = this;
|
||||||
}
|
}
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task SaveAndCloseAsync()
|
protected override async Task SaveAndCloseAsync()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -65,11 +65,6 @@ namespace LibationAvalonia.Dialogs.Login
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task SaveAndCloseAsync()
|
protected override async Task SaveAndCloseAsync()
|
||||||
{
|
{
|
||||||
var selected = Values.CheckedButton;
|
var selected = Values.CheckedButton;
|
||||||
|
|||||||
@ -60,11 +60,6 @@ namespace LibationAvalonia.Dialogs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OkButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public void OkButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
SaveAndClose();
|
SaveAndClose();
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
Icon="/Assets/1x1.png">
|
Icon="/Assets/1x1.png">
|
||||||
<Grid ColumnDefinitions="*" RowDefinitions="*,Auto">
|
<Grid ColumnDefinitions="*" RowDefinitions="*,Auto">
|
||||||
|
|
||||||
<DockPanel Margin="5,10,10,20" Grid.Row="0" Background="White">
|
<DockPanel Margin="5,10,10,20" Grid.Row="0">
|
||||||
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal"
|
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal"
|
||||||
VerticalAlignment="Top">
|
VerticalAlignment="Top">
|
||||||
|
|
||||||
@ -26,7 +26,7 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
|
|
||||||
<DockPanel Height="45" Grid.Row="1" Background="WhiteSmoke">
|
<DockPanel Height="45" Grid.Row="1" Background="{DynamicResource SystemChromeMediumLowColor}">
|
||||||
<DockPanel.Styles>
|
<DockPanel.Styles>
|
||||||
<Style Selector="Button:focus">
|
<Style Selector="Button:focus">
|
||||||
<Setter Property="BorderBrush" Value="{DynamicResource SystemAccentColor}" />
|
<Setter Property="BorderBrush" Value="{DynamicResource SystemAccentColor}" />
|
||||||
|
|||||||
@ -17,11 +17,6 @@ namespace LibationAvalonia.Dialogs
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void CancelAndClose() => Close(DialogResult.None);
|
protected override void CancelAndClose() => Close(DialogResult.None);
|
||||||
|
|
||||||
protected override void SaveAndClose() { }
|
protected override void SaveAndClose() { }
|
||||||
|
|||||||
@ -26,6 +26,10 @@ namespace LibationAvalonia.Dialogs
|
|||||||
public ScanAccountsDialog()
|
public ScanAccountsDialog()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
this.HideMinMaxBtns();
|
||||||
|
this.Opened += ScanAccountsDialog_Opened;
|
||||||
|
|
||||||
LoadAccounts();
|
LoadAccounts();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,13 +49,6 @@ namespace LibationAvalonia.Dialogs
|
|||||||
DataContext = this;
|
DataContext = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
this.HideMinMaxBtns();
|
|
||||||
this.Opened += ScanAccountsDialog_Opened;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ScanAccountsDialog_Opened(object sender, System.EventArgs e)
|
private void ScanAccountsDialog_Opened(object sender, System.EventArgs e)
|
||||||
{
|
{
|
||||||
this.FindControl<Button>(nameof(ImportButton)).Focus();
|
this.FindControl<Button>(nameof(ImportButton)).Focus();
|
||||||
|
|||||||
@ -12,6 +12,8 @@ namespace LibationAvalonia.Dialogs
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
this.HideMinMaxBtns();
|
||||||
|
|
||||||
StringFields = @"
|
StringFields = @"
|
||||||
Search for wizard of oz:
|
Search for wizard of oz:
|
||||||
title:oz
|
title:oz
|
||||||
@ -52,12 +54,5 @@ for the ID field
|
|||||||
DataContext = this;
|
DataContext = this;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
|
|
||||||
this.HideMinMaxBtns();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -113,6 +113,32 @@
|
|||||||
|
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
-->
|
-->
|
||||||
|
<Grid
|
||||||
|
Grid.Row="2"
|
||||||
|
ColumnDefinitions="Auto,Auto,*"
|
||||||
|
Margin="10"
|
||||||
|
VerticalAlignment="Bottom">
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="0"
|
||||||
|
FontSize="16"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="Theme: "/>
|
||||||
|
|
||||||
|
<controls:WheelComboBox
|
||||||
|
Grid.Column="1"
|
||||||
|
SelectedItem="{Binding ImportantSettings.ThemeVariant, Mode=TwoWay}"
|
||||||
|
Items="{Binding ImportantSettings.Themes}" />
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="2"
|
||||||
|
FontSize="16"
|
||||||
|
FontWeight="Bold"
|
||||||
|
Margin="10,0,0,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
IsVisible="{Binding ImportantSettings.SelectionChanged}"
|
||||||
|
Text="Theme change takes effect on restart"/>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
using Avalonia;
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -9,7 +7,6 @@ using ReactiveUI;
|
|||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FileManager;
|
using FileManager;
|
||||||
using System.IO;
|
|
||||||
using Avalonia.Collections;
|
using Avalonia.Collections;
|
||||||
using LibationUiBase;
|
using LibationUiBase;
|
||||||
|
|
||||||
@ -29,11 +26,6 @@ namespace LibationAvalonia.Dialogs
|
|||||||
DataContext = settingsDisp = new(config);
|
DataContext = settingsDisp = new(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task SaveAndCloseAsync()
|
protected override async Task SaveAndCloseAsync()
|
||||||
{
|
{
|
||||||
if (!await settingsDisp.SaveSettingsAsync(config))
|
if (!await settingsDisp.SaveSettingsAsync(config))
|
||||||
@ -134,7 +126,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ImportantSettings : ISettingsDisplay
|
public class ImportantSettings : ViewModels.ViewModelBase, ISettingsDisplay
|
||||||
{
|
{
|
||||||
public ImportantSettings(Configuration config)
|
public ImportantSettings(Configuration config)
|
||||||
{
|
{
|
||||||
@ -147,6 +139,10 @@ namespace LibationAvalonia.Dialogs
|
|||||||
SavePodcastsToParentFolder = config.SavePodcastsToParentFolder;
|
SavePodcastsToParentFolder = config.SavePodcastsToParentFolder;
|
||||||
LoggingLevel = config.LogLevel;
|
LoggingLevel = config.LogLevel;
|
||||||
BetaOptIn = config.BetaOptIn;
|
BetaOptIn = config.BetaOptIn;
|
||||||
|
ThemeVariant = InitialThemeVariant
|
||||||
|
= Configuration.Instance.GetString(propertyName: nameof(ThemeVariant)) is nameof(Avalonia.Styling.ThemeVariant.Dark)
|
||||||
|
? nameof(Avalonia.Styling.ThemeVariant.Dark)
|
||||||
|
: nameof(Avalonia.Styling.ThemeVariant.Light);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> SaveSettingsAsync(Configuration config)
|
public async Task<bool> SaveSettingsAsync(Configuration config)
|
||||||
@ -168,6 +164,7 @@ namespace LibationAvalonia.Dialogs
|
|||||||
config.SavePodcastsToParentFolder = SavePodcastsToParentFolder;
|
config.SavePodcastsToParentFolder = SavePodcastsToParentFolder;
|
||||||
config.LogLevel = LoggingLevel;
|
config.LogLevel = LoggingLevel;
|
||||||
config.BetaOptIn = BetaOptIn;
|
config.BetaOptIn = BetaOptIn;
|
||||||
|
Configuration.Instance.SetString(ThemeVariant, nameof(ThemeVariant));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -184,11 +181,27 @@ namespace LibationAvalonia.Dialogs
|
|||||||
public string SavePodcastsToParentFolderText { get; } = Configuration.GetDescription(nameof(Configuration.SavePodcastsToParentFolder));
|
public string SavePodcastsToParentFolderText { get; } = Configuration.GetDescription(nameof(Configuration.SavePodcastsToParentFolder));
|
||||||
public Serilog.Events.LogEventLevel[] LoggingLevels { get; } = Enum.GetValues<Serilog.Events.LogEventLevel>();
|
public Serilog.Events.LogEventLevel[] LoggingLevels { get; } = Enum.GetValues<Serilog.Events.LogEventLevel>();
|
||||||
public string BetaOptInText { get; } = Configuration.GetDescription(nameof(Configuration.BetaOptIn));
|
public string BetaOptInText { get; } = Configuration.GetDescription(nameof(Configuration.BetaOptIn));
|
||||||
|
public string[] Themes { get; } = { nameof(Avalonia.Styling.ThemeVariant.Light), nameof(Avalonia.Styling.ThemeVariant.Dark) };
|
||||||
|
|
||||||
|
|
||||||
public string BooksDirectory { get; set; }
|
public string BooksDirectory { get; set; }
|
||||||
public bool SavePodcastsToParentFolder { get; set; }
|
public bool SavePodcastsToParentFolder { get; set; }
|
||||||
public Serilog.Events.LogEventLevel LoggingLevel { get; set; }
|
public Serilog.Events.LogEventLevel LoggingLevel { get; set; }
|
||||||
public bool BetaOptIn { get; set; }
|
public bool BetaOptIn { get; set; }
|
||||||
|
private string themeVariant;
|
||||||
|
public string ThemeVariant
|
||||||
|
{
|
||||||
|
get => themeVariant;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
this.RaiseAndSetIfChanged(ref themeVariant, value);
|
||||||
|
|
||||||
|
SelectionChanged = ThemeVariant != InitialThemeVariant;
|
||||||
|
this.RaisePropertyChanged(nameof(SelectionChanged));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public string InitialThemeVariant { get; private set; }
|
||||||
|
public bool SelectionChanged { get; private set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ImportSettings : ISettingsDisplay
|
public class ImportSettings : ISettingsDisplay
|
||||||
|
|||||||
@ -30,10 +30,5 @@ namespace LibationAvalonia.Dialogs
|
|||||||
IsReturningUser = true;
|
IsReturningUser = true;
|
||||||
Close(DialogResult.OK);
|
Close(DialogResult.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,11 +15,6 @@ namespace LibationAvalonia.Dialogs
|
|||||||
DataContext = this;
|
DataContext = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeComponent()
|
|
||||||
{
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
public void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
=> SaveAndClose();
|
=> SaveAndClose();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||||
<ApplicationIcon>libation.ico</ApplicationIcon>
|
<ApplicationIcon>Assets/libation.ico</ApplicationIcon>
|
||||||
<AssemblyName>Libation</AssemblyName>
|
<AssemblyName>Libation</AssemblyName>
|
||||||
<IsPublishable>true</IsPublishable>
|
<IsPublishable>true</IsPublishable>
|
||||||
<PublishReadyToRun>true</PublishReadyToRun>
|
<PublishReadyToRun>true</PublishReadyToRun>
|
||||||
@ -31,47 +31,15 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AvaloniaResource Include="Assets\**" />
|
<AvaloniaResource Include="Assets\**" />
|
||||||
<None Remove=".gitignore" />
|
<None Remove=".gitignore" />
|
||||||
<None Remove="Assets\Arrows_left.png" />
|
<None Remove="Assets\DataGridFluentTheme.xaml" />
|
||||||
<None Remove="Assets\Arrows_right.png" />
|
|
||||||
<None Remove="Assets\Asterisk.png" />
|
|
||||||
<None Remove="Assets\cancel.png" />
|
|
||||||
<None Remove="Assets\completed.png" />
|
|
||||||
<None Remove="Assets\down.png" />
|
|
||||||
<None Remove="Assets\download-arrow.png" />
|
|
||||||
<None Remove="Assets\edit-tags-25x25.png" />
|
|
||||||
<None Remove="Assets\edit-tags-50x50.png" />
|
|
||||||
<None Remove="Assets\edit_25x25.png" />
|
|
||||||
<None Remove="Assets\edit_64x64.png" />
|
|
||||||
<None Remove="Assets\error.png" />
|
|
||||||
<None Remove="Assets\errored.png" />
|
|
||||||
<None Remove="Assets\Exclamation.png" />
|
|
||||||
<None Remove="Assets\first.png" />
|
|
||||||
<None Remove="Assets\glass-with-glow_16.png" />
|
|
||||||
<None Remove="Assets\img-coverart-prod-unavailable_300x300.jpg" />
|
<None Remove="Assets\img-coverart-prod-unavailable_300x300.jpg" />
|
||||||
<None Remove="Assets\img-coverart-prod-unavailable_500x500.jpg" />
|
<None Remove="Assets\img-coverart-prod-unavailable_500x500.jpg" />
|
||||||
<None Remove="Assets\img-coverart-prod-unavailable_80x80.jpg" />
|
<None Remove="Assets\img-coverart-prod-unavailable_80x80.jpg" />
|
||||||
<None Remove="Assets\import_16x16.png" />
|
<None Remove="Assets\1x1.png" />
|
||||||
<None Remove="Assets\last.png" />
|
|
||||||
<None Remove="Assets\libation.ico" />
|
|
||||||
<None Remove="Assets\LibationStyles.xaml" />
|
|
||||||
<None Remove="Assets\liberate_green.png" />
|
|
||||||
<None Remove="Assets\liberate_green_pdf_no.png" />
|
|
||||||
<None Remove="Assets\liberate_green_pdf_yes.png" />
|
|
||||||
<None Remove="Assets\liberate_red.png" />
|
|
||||||
<None Remove="Assets\liberate_red_pdf_no.png" />
|
|
||||||
<None Remove="Assets\liberate_red_pdf_yes.png" />
|
|
||||||
<None Remove="Assets\liberate_yellow.png" />
|
|
||||||
<None Remove="Assets\liberate_yellow_pdf_no.png" />
|
|
||||||
<None Remove="Assets\liberate_yellow_pdf_yes.png" />
|
|
||||||
<None Remove="Assets\MBIcons\Asterisk.png" />
|
<None Remove="Assets\MBIcons\Asterisk.png" />
|
||||||
<None Remove="Assets\MBIcons\error.png" />
|
<None Remove="Assets\MBIcons\error.png" />
|
||||||
<None Remove="Assets\MBIcons\Exclamation.png" />
|
<None Remove="Assets\MBIcons\Exclamation.png" />
|
||||||
<None Remove="Assets\MBIcons\Question.png" />
|
<None Remove="Assets\MBIcons\Question.png" />
|
||||||
<None Remove="Assets\minus.png" />
|
|
||||||
<None Remove="Assets\plus.png" />
|
|
||||||
<None Remove="Assets\Question.png" />
|
|
||||||
<None Remove="Assets\queued.png" />
|
|
||||||
<None Remove="Assets\up.png" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -80,6 +48,9 @@
|
|||||||
<AutoGen>True</AutoGen>
|
<AutoGen>True</AutoGen>
|
||||||
<DependentUpon>Resources.resx</DependentUpon>
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Update="Views\LiberateStatusButton.axaml.cs">
|
||||||
|
<DependentUpon>LiberateStatusButton.axaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Update="Views\MainWindow.*.cs">
|
<Compile Update="Views\MainWindow.*.cs">
|
||||||
<DependentUpon>MainWindow.axaml</DependentUpon>
|
<DependentUpon>MainWindow.axaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
@ -97,13 +68,13 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Avalonia" Version="11.0.0-preview4" />
|
<PackageReference Include="Avalonia.Diagnostics" Version="11.0.0-preview6" Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'" />
|
||||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.0.0-preview4" />
|
<PackageReference Include="Avalonia" Version="11.0.0-preview6" />
|
||||||
<PackageReference Include="Avalonia.Desktop" Version="11.0.0-preview4" />
|
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.0.0-preview6" />
|
||||||
<PackageReference Include="Avalonia.Diagnostics" Version="11.0.0-preview4 " />
|
<PackageReference Include="Avalonia.Controls.ItemsRepeater" Version="11.0.0-preview6" />
|
||||||
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-preview4" />
|
<PackageReference Include="Avalonia.Desktop" Version="11.0.0-preview6" />
|
||||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.0-preview4" />
|
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-preview6" />
|
||||||
<PackageReference Include="XamlNameReferenceGenerator" Version="1.6.1" />
|
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.0-preview6" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -7,7 +7,7 @@ namespace LibationAvalonia
|
|||||||
{
|
{
|
||||||
public class ViewLocator : IDataTemplate
|
public class ViewLocator : IDataTemplate
|
||||||
{
|
{
|
||||||
public IControl Build(object data)
|
public Control Build(object data)
|
||||||
{
|
{
|
||||||
var name = data.GetType().FullName!.Replace("ViewModel", "View");
|
var name = data.GetType().FullName!.Replace("ViewModel", "View");
|
||||||
var type = Type.GetType(name);
|
var type = Type.GetType(name);
|
||||||
|
|||||||
@ -16,11 +16,7 @@ namespace LibationAvalonia.ViewModels
|
|||||||
protected override Bitmap LoadImage(byte[] picture)
|
protected override Bitmap LoadImage(byte[] picture)
|
||||||
=> AvaloniaUtils.TryLoadImageOrDefault(picture, LibationFileManager.PictureSize._80x80);
|
=> AvaloniaUtils.TryLoadImageOrDefault(picture, LibationFileManager.PictureSize._80x80);
|
||||||
|
|
||||||
protected override Bitmap GetResourceImage(string rescName)
|
//Button icons are handled by LiberateStatusButton
|
||||||
{
|
protected override Bitmap GetResourceImage(string rescName) => null;
|
||||||
//These images are assest, so assume they will never corrupt.
|
|
||||||
using var stream = App.OpenAsset(rescName + ".png");
|
|
||||||
return new Bitmap(stream);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,27 @@
|
|||||||
|
using ReactiveUI;
|
||||||
|
|
||||||
|
namespace LibationAvalonia.ViewModels
|
||||||
|
{
|
||||||
|
public class LiberateStatusButtonViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
private bool isSeries;
|
||||||
|
private bool isError;
|
||||||
|
private bool isButtonEnabled;
|
||||||
|
private bool expanded;
|
||||||
|
private bool redVisible = true;
|
||||||
|
private bool yellowVisible;
|
||||||
|
private bool greenVisible;
|
||||||
|
private bool pdfNotDownloadedVisible;
|
||||||
|
private bool pdfDownloadedVisible;
|
||||||
|
|
||||||
|
public bool IsError { get => isError; set => this.RaiseAndSetIfChanged(ref isError, value); }
|
||||||
|
public bool IsButtonEnabled { get => isButtonEnabled; set => this.RaiseAndSetIfChanged(ref isButtonEnabled, value); }
|
||||||
|
public bool IsSeries { get => isSeries; set => this.RaiseAndSetIfChanged(ref isSeries, value); }
|
||||||
|
public bool Expanded { get => expanded; set => this.RaiseAndSetIfChanged(ref expanded, value); }
|
||||||
|
public bool RedVisible { get => redVisible; set => this.RaiseAndSetIfChanged(ref redVisible, value); }
|
||||||
|
public bool YellowVisible { get => yellowVisible; set => this.RaiseAndSetIfChanged(ref yellowVisible, value); }
|
||||||
|
public bool GreenVisible { get => greenVisible; set => this.RaiseAndSetIfChanged(ref greenVisible, value); }
|
||||||
|
public bool PdfDownloadedVisible { get => pdfDownloadedVisible; set => this.RaiseAndSetIfChanged(ref pdfDownloadedVisible, value); }
|
||||||
|
public bool PdfNotDownloadedVisible { get => pdfNotDownloadedVisible; set => this.RaiseAndSetIfChanged(ref pdfNotDownloadedVisible, value); }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -134,8 +134,11 @@ namespace LibationAvalonia.ViewModels
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
this.RaiseAndSetIfChanged(ref _queueOpen, value);
|
this.RaiseAndSetIfChanged(ref _queueOpen, value);
|
||||||
|
QueueButtonAngle = value ? 180 : 0;
|
||||||
|
this.RaisePropertyChanged(nameof(QueueButtonAngle));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public double QueueButtonAngle { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
/// <summary> The number of books visible in the Product Display </summary>
|
/// <summary> The number of books visible in the Product Display </summary>
|
||||||
|
|||||||
@ -31,10 +31,14 @@ namespace LibationAvalonia.ViewModels
|
|||||||
public bool RemoveColumnVisivle { get => _removeColumnVisivle; private set => this.RaiseAndSetIfChanged(ref _removeColumnVisivle, value); }
|
public bool RemoveColumnVisivle { get => _removeColumnVisivle; private set => this.RaiseAndSetIfChanged(ref _removeColumnVisivle, value); }
|
||||||
|
|
||||||
public List<LibraryBook> GetVisibleBookEntries()
|
public List<LibraryBook> GetVisibleBookEntries()
|
||||||
=> GridEntries
|
=> FilteredInGridEntries?
|
||||||
.OfType<ILibraryBookEntry>()
|
.OfType<ILibraryBookEntry>()
|
||||||
.Select(lbe => lbe.LibraryBook)
|
.Select(lbe => lbe.LibraryBook)
|
||||||
.ToList();
|
.ToList()
|
||||||
|
?? SOURCE
|
||||||
|
.OfType<ILibraryBookEntry>()
|
||||||
|
.Select(lbe => lbe.LibraryBook)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
private IEnumerable<ILibraryBookEntry> GetAllBookEntries()
|
private IEnumerable<ILibraryBookEntry> GetAllBookEntries()
|
||||||
=> SOURCE
|
=> SOURCE
|
||||||
@ -110,16 +114,31 @@ namespace LibationAvalonia.ViewModels
|
|||||||
seriesEntry.Liberate.Expanded = false;
|
seriesEntry.Liberate.Expanded = false;
|
||||||
|
|
||||||
geList.Add(seriesEntry);
|
geList.Add(seriesEntry);
|
||||||
geList.AddRange(seriesEntry.Children);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Create the filtered-in list before adding entries to avoid a refresh
|
//Create the filtered-in list before adding entries to avoid a refresh
|
||||||
FilteredInGridEntries = QueryResults(geList, FilterString);
|
FilteredInGridEntries = QueryResults(geList, FilterString);
|
||||||
SOURCE.AddRange(geList.OrderByDescending(e => e.DateAdded));
|
SOURCE.AddRange(geList.OrderByDescending(e => e.DateAdded));
|
||||||
GridEntries.CollectionChanged += (_, _)
|
|
||||||
=> VisibleCountChanged?.Invoke(this, GridEntries.OfType<ILibraryBookEntry>().Count());
|
|
||||||
|
|
||||||
VisibleCountChanged?.Invoke(this, GridEntries.OfType<ILibraryBookEntry>().Count());
|
//Add all children beneath their parent
|
||||||
|
foreach (var series in SOURCE.OfType<ISeriesEntry>().ToList())
|
||||||
|
{
|
||||||
|
var seriesIndex = SOURCE.IndexOf(series);
|
||||||
|
foreach (var child in series.Children)
|
||||||
|
SOURCE.Insert(++seriesIndex, child);
|
||||||
|
}
|
||||||
|
|
||||||
|
GridEntries.CollectionChanged += GridEntries_CollectionChanged;
|
||||||
|
GridEntries_CollectionChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GridEntries_CollectionChanged(object sender = null, EventArgs e = null)
|
||||||
|
{
|
||||||
|
var count
|
||||||
|
= FilteredInGridEntries?.OfType<ILibraryBookEntry>().Count()
|
||||||
|
?? SOURCE.OfType<ILibraryBookEntry>().Count();
|
||||||
|
|
||||||
|
VisibleCountChanged?.Invoke(this, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -127,6 +146,8 @@ namespace LibationAvalonia.ViewModels
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal async Task UpdateGridAsync(List<LibraryBook> dbBooks)
|
internal async Task UpdateGridAsync(List<LibraryBook> dbBooks)
|
||||||
{
|
{
|
||||||
|
GridEntries.CollectionChanged -= GridEntries_CollectionChanged;
|
||||||
|
|
||||||
#region Add new or update existing grid entries
|
#region Add new or update existing grid entries
|
||||||
|
|
||||||
//Add absent entries to grid, or update existing entry
|
//Add absent entries to grid, or update existing entry
|
||||||
@ -176,6 +197,9 @@ namespace LibationAvalonia.ViewModels
|
|||||||
|
|
||||||
await Filter(FilterString);
|
await Filter(FilterString);
|
||||||
GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true);
|
GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true);
|
||||||
|
|
||||||
|
GridEntries.CollectionChanged += GridEntries_CollectionChanged;
|
||||||
|
GridEntries_CollectionChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RemoveBooks(IEnumerable<ILibraryBookEntry> removedBooks, IEnumerable<ISeriesEntry> removedSeries)
|
private void RemoveBooks(IEnumerable<ILibraryBookEntry> removedBooks, IEnumerable<ISeriesEntry> removedSeries)
|
||||||
@ -237,13 +261,15 @@ namespace LibationAvalonia.ViewModels
|
|||||||
//Series exists. Create and add episode child then update the SeriesEntry
|
//Series exists. Create and add episode child then update the SeriesEntry
|
||||||
episodeEntry = new LibraryBookEntry<AvaloniaEntryStatus>(episodeBook, seriesEntry);
|
episodeEntry = new LibraryBookEntry<AvaloniaEntryStatus>(episodeBook, seriesEntry);
|
||||||
seriesEntry.Children.Add(episodeEntry);
|
seriesEntry.Children.Add(episodeEntry);
|
||||||
|
seriesEntry.Children.Sort((c1, c2) => c1.SeriesIndex.CompareTo(c2.SeriesIndex));
|
||||||
var seriesBook = dbBooks.Single(lb => lb.Book.AudibleProductId == seriesEntry.LibraryBook.Book.AudibleProductId);
|
var seriesBook = dbBooks.Single(lb => lb.Book.AudibleProductId == seriesEntry.LibraryBook.Book.AudibleProductId);
|
||||||
seriesEntry.UpdateLibraryBook(seriesBook);
|
seriesEntry.UpdateLibraryBook(seriesBook);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Add episode to the grid beneath the parent
|
//Add episode to the grid beneath the parent
|
||||||
int seriesIndex = SOURCE.IndexOf(seriesEntry);
|
int seriesIndex = SOURCE.IndexOf(seriesEntry);
|
||||||
SOURCE.Insert(seriesIndex + 1, episodeEntry);
|
int episodeIndex = seriesEntry.Children.IndexOf(episodeEntry);
|
||||||
|
SOURCE.Insert(seriesIndex + 1 + episodeIndex, episodeEntry);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
existingEpisodeEntry.UpdateLibraryBook(episodeBook);
|
existingEpisodeEntry.UpdateLibraryBook(episodeBook);
|
||||||
@ -401,6 +427,10 @@ namespace LibationAvalonia.ViewModels
|
|||||||
foreach (var r in removable)
|
foreach (var r in removable)
|
||||||
r.Remove = true;
|
r.Remove = true;
|
||||||
}
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
Serilog.Log.Information("Audible login attempt cancelled by user");
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await MessageBox.ShowAdminAlert(
|
await MessageBox.ShowAdminAlert(
|
||||||
|
|||||||
82
Source/LibationAvalonia/Views/LiberateStatusButton.axaml
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:vm="clr-namespace:LibationAvalonia.ViewModels"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="200" d:DesignHeight="200" MinWidth="64" MinHeight="64"
|
||||||
|
Background="Transparent"
|
||||||
|
x:DataType="vm:LiberateStatusButtonViewModel"
|
||||||
|
x:Class="LibationAvalonia.Views.LiberateStatusButton">
|
||||||
|
|
||||||
|
<UserControl.Styles>
|
||||||
|
<Style Selector="Path">
|
||||||
|
<Setter Property="Fill" Value="{DynamicResource IconFill}" />
|
||||||
|
<Setter Property="Stretch" Value="Uniform" />
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Center" />
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Rectangle">
|
||||||
|
<Setter Property="Width" Value="20" />
|
||||||
|
<Setter Property="Height" Value="18" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="Grid > Path">
|
||||||
|
<Setter Property="Margin" Value="4,0,0,0" />
|
||||||
|
<Setter Property="Width" Value="28.8" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="Button:disabled /template/ ContentPresenter#PART_ContentPresenter">
|
||||||
|
<Setter Property="Background" Value="Transparent" />
|
||||||
|
<Setter Property="BorderBrush" Value="Transparent" />
|
||||||
|
</Style>
|
||||||
|
</UserControl.Styles>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
Name="button"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
IsEnabled="{Binding IsButtonEnabled}" Padding="0" Click="Button_Click" >
|
||||||
|
<Panel>
|
||||||
|
|
||||||
|
<Panel
|
||||||
|
Width="64" Height="64"
|
||||||
|
IsVisible="{CompiledBinding !IsError}">
|
||||||
|
|
||||||
|
<Panel IsVisible="{CompiledBinding IsSeries}">
|
||||||
|
<Path IsVisible="{CompiledBinding Expanded}" Data="{StaticResource CollapseIcon}" />
|
||||||
|
<Path IsVisible="{CompiledBinding !Expanded}" Data="{StaticResource ExpandIcon}" />
|
||||||
|
</Panel>
|
||||||
|
|
||||||
|
<Grid
|
||||||
|
IsVisible="{CompiledBinding !IsSeries}"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
ColumnDefinitions="Auto,Auto">
|
||||||
|
|
||||||
|
<Canvas Width="29.44" Height="64">
|
||||||
|
<Rectangle Canvas.Left="5" Canvas.Top="5" IsVisible="{CompiledBinding RedVisible}" Fill="{DynamicResource StoplightRed}" />
|
||||||
|
<Rectangle Canvas.Left="5" Canvas.Top="23" IsVisible="{CompiledBinding YellowVisible}" Fill="{DynamicResource StoplightYellow}" />
|
||||||
|
<Rectangle Canvas.Left="5" Canvas.Top="42" IsVisible="{CompiledBinding GreenVisible}" Fill="{DynamicResource StoplightGreen}" />
|
||||||
|
<Path Height="64" Stretch="Uniform" Data="{StaticResource StoplightBodyIcon}"/>
|
||||||
|
</Canvas>
|
||||||
|
|
||||||
|
<Path Grid.Column="1" IsVisible="{CompiledBinding PdfDownloadedVisible}" Data="{StaticResource PdfDownloadedIcon}"/>
|
||||||
|
<Path Grid.Column="1" IsVisible="{CompiledBinding PdfNotDownloadedVisible}" Data="{StaticResource PdfNotDownloadedIcon}"/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
</Panel>
|
||||||
|
|
||||||
|
<Path
|
||||||
|
Stretch="None" Width="64"
|
||||||
|
IsVisible="{CompiledBinding IsError}"
|
||||||
|
Fill="{DynamicResource CancelRed}"
|
||||||
|
Data="{StaticResource BookErrorIcon}" />
|
||||||
|
|
||||||
|
<Path
|
||||||
|
Stretch="Fill"
|
||||||
|
IsVisible="{CompiledBinding !IsButtonEnabled}"
|
||||||
|
Fill="{DynamicResource DisabledGrayBrush}"
|
||||||
|
Data="M0,0 H1 V1 H0" />
|
||||||
|
</Panel>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
</UserControl>
|
||||||
80
Source/LibationAvalonia/Views/LiberateStatusButton.axaml.cs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
using DataLayer;
|
||||||
|
using LibationAvalonia.ViewModels;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace LibationAvalonia.Views
|
||||||
|
{
|
||||||
|
public partial class LiberateStatusButton : UserControl
|
||||||
|
{
|
||||||
|
public event EventHandler Click;
|
||||||
|
|
||||||
|
public static readonly StyledProperty<LiberatedStatus> BookStatusProperty =
|
||||||
|
AvaloniaProperty.Register<LiberateStatusButton, LiberatedStatus>(nameof(BookStatus));
|
||||||
|
|
||||||
|
public static readonly StyledProperty<LiberatedStatus?> PdfStatusProperty =
|
||||||
|
AvaloniaProperty.Register<LiberateStatusButton, LiberatedStatus?>(nameof(PdfStatus));
|
||||||
|
|
||||||
|
public static readonly StyledProperty<bool> IsUnavailableProperty =
|
||||||
|
AvaloniaProperty.Register<LiberateStatusButton, bool>(nameof(IsUnavailable));
|
||||||
|
|
||||||
|
public static readonly StyledProperty<bool> ExpandedProperty =
|
||||||
|
AvaloniaProperty.Register<LiberateStatusButton, bool>(nameof(Expanded));
|
||||||
|
|
||||||
|
public static readonly StyledProperty<bool> IsSeriesProperty =
|
||||||
|
AvaloniaProperty.Register<LiberateStatusButton, bool>(nameof(IsSeries));
|
||||||
|
|
||||||
|
public LiberatedStatus BookStatus { get => GetValue(BookStatusProperty); set => SetValue(BookStatusProperty, value); }
|
||||||
|
public LiberatedStatus? PdfStatus { get => GetValue(PdfStatusProperty); set => SetValue(PdfStatusProperty, value); }
|
||||||
|
public bool IsUnavailable { get => GetValue(IsUnavailableProperty); set => SetValue(IsUnavailableProperty, value); }
|
||||||
|
public bool Expanded { get => GetValue(ExpandedProperty); set => SetValue(ExpandedProperty, value); }
|
||||||
|
public bool IsSeries { get => GetValue(IsSeriesProperty); set => SetValue(IsSeriesProperty, value); }
|
||||||
|
|
||||||
|
private readonly LiberateStatusButtonViewModel viewModel = new();
|
||||||
|
|
||||||
|
public LiberateStatusButton()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
button.DataContext = viewModel;
|
||||||
|
|
||||||
|
if (Design.IsDesignMode)
|
||||||
|
{
|
||||||
|
BookStatus = LiberatedStatus.PartialDownload;
|
||||||
|
PdfStatus = null;
|
||||||
|
IsSeries = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Button_Click(object sender, RoutedEventArgs e) => Click?.Invoke(this, EventArgs.Empty);
|
||||||
|
|
||||||
|
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||||
|
{
|
||||||
|
if (change.Property == BookStatusProperty)
|
||||||
|
{
|
||||||
|
viewModel.IsError = BookStatus is LiberatedStatus.Error;
|
||||||
|
viewModel.RedVisible = BookStatus is LiberatedStatus.NotLiberated;
|
||||||
|
viewModel.YellowVisible = BookStatus is LiberatedStatus.PartialDownload;
|
||||||
|
viewModel.GreenVisible = BookStatus is LiberatedStatus.Liberated;
|
||||||
|
}
|
||||||
|
else if (change.Property == PdfStatusProperty)
|
||||||
|
{
|
||||||
|
viewModel.PdfDownloadedVisible = PdfStatus is LiberatedStatus.Liberated;
|
||||||
|
viewModel.PdfNotDownloadedVisible = PdfStatus is LiberatedStatus.NotLiberated;
|
||||||
|
}
|
||||||
|
else if (change.Property == IsSeriesProperty)
|
||||||
|
{
|
||||||
|
viewModel.IsSeries = IsSeries;
|
||||||
|
}
|
||||||
|
else if (change.Property == ExpandedProperty)
|
||||||
|
{
|
||||||
|
viewModel.Expanded = Expanded;
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.IsButtonEnabled = !viewModel.IsError && (!IsUnavailable || (BookStatus is LiberatedStatus.Liberated && PdfStatus is null or LiberatedStatus.Liberated));
|
||||||
|
|
||||||
|
base.OnPropertyChanged(change);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -20,7 +20,7 @@ namespace LibationAvalonia.Views
|
|||||||
var options = new FilePickerSaveOptions
|
var options = new FilePickerSaveOptions
|
||||||
{
|
{
|
||||||
Title = "Where to export Library",
|
Title = "Where to export Library",
|
||||||
SuggestedStartLocation = new Avalonia.Platform.Storage.FileIO.BclStorageFolder(Configuration.Instance.Books.PathWithoutPrefix),
|
SuggestedStartLocation = await StorageProvider.TryGetFolderFromPathAsync(Configuration.Instance.Books.PathWithoutPrefix),
|
||||||
SuggestedFileName = $"Libation Library Export {DateTime.Now:yyyy-MM-dd}",
|
SuggestedFileName = $"Libation Library Export {DateTime.Now:yyyy-MM-dd}",
|
||||||
DefaultExtension = "xlsx",
|
DefaultExtension = "xlsx",
|
||||||
ShowOverwritePrompt = true,
|
ShowOverwritePrompt = true,
|
||||||
@ -46,26 +46,26 @@ namespace LibationAvalonia.Views
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var selectedFile = await StorageProvider.SaveFilePickerAsync(options);
|
var selectedFile = (await StorageProvider.SaveFilePickerAsync(options))?.TryGetLocalPath();
|
||||||
|
|
||||||
if (selectedFile?.TryGetUri(out var uri) is not true) return;
|
if (selectedFile is null) return;
|
||||||
|
|
||||||
var ext = FileUtility.GetStandardizedExtension(System.IO.Path.GetExtension(uri.LocalPath));
|
var ext = FileUtility.GetStandardizedExtension(System.IO.Path.GetExtension(selectedFile));
|
||||||
switch (ext)
|
switch (ext)
|
||||||
{
|
{
|
||||||
case ".xlsx": // xlsx
|
case ".xlsx": // xlsx
|
||||||
default:
|
default:
|
||||||
LibraryExporter.ToXlsx(uri.LocalPath);
|
LibraryExporter.ToXlsx(selectedFile);
|
||||||
break;
|
break;
|
||||||
case ".csv": // csv
|
case ".csv": // csv
|
||||||
LibraryExporter.ToCsv(uri.LocalPath);
|
LibraryExporter.ToCsv(selectedFile);
|
||||||
break;
|
break;
|
||||||
case ".json": // json
|
case ".json": // json
|
||||||
LibraryExporter.ToJson(uri.LocalPath);
|
LibraryExporter.ToJson(selectedFile);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
await MessageBox.Show("Library exported to:\r\n" + uri.LocalPath, "Library Exported");
|
await MessageBox.Show("Library exported to:\r\n" + selectedFile, "Library Exported");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||