mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
refine AdvancedCollectionView
This commit is contained in:
@@ -18,7 +18,7 @@ internal sealed class AdvancedDbCollectionView<TEntity> : AdvancedCollectionView
|
||||
private bool savingToDatabase = true;
|
||||
|
||||
public AdvancedDbCollectionView(IList<TEntity> source, IServiceProvider serviceProvider)
|
||||
: base(source, true)
|
||||
: base(source)
|
||||
{
|
||||
this.serviceProvider = serviceProvider;
|
||||
}
|
||||
@@ -85,7 +85,7 @@ internal sealed class AdvancedDbCollectionView<TEntityAccess, TEntity> : Advance
|
||||
private bool savingToDatabase = true;
|
||||
|
||||
public AdvancedDbCollectionView(IList<TEntityAccess> source, IServiceProvider serviceProvider)
|
||||
: base(source, true)
|
||||
: base(source)
|
||||
{
|
||||
this.serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ internal sealed partial class SummaryFactory : ISummaryFactory
|
||||
|
||||
return new()
|
||||
{
|
||||
Avatars = new(views, true),
|
||||
Avatars = new(views),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -212,7 +212,7 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
|
||||
return new()
|
||||
{
|
||||
// history
|
||||
HistoryWishes = taskContext.InvokeOnMainThread(() => new AdvancedCollectionView<HistoryWish>(historyWishes, true)),
|
||||
HistoryWishes = taskContext.InvokeOnMainThread(() => new AdvancedCollectionView<HistoryWish>(historyWishes)),
|
||||
|
||||
// avatars
|
||||
OrangeAvatars = orangeAvatarCounter.ToStatisticsList(),
|
||||
|
||||
@@ -214,7 +214,7 @@ internal sealed partial class UserInitializationService : IUserInitializationSer
|
||||
if (userGameRolesResponse.IsOk())
|
||||
{
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
user.UserGameRoles = new(userGameRolesResponse.Data.List, true);
|
||||
user.UserGameRoles = new(userGameRolesResponse.Data.List);
|
||||
return user.UserGameRoles.Count > 0;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -7,6 +7,7 @@ using Microsoft.UI.Xaml.Data;
|
||||
using System.Collections;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
@@ -17,10 +18,10 @@ namespace Snap.Hutao.UI.Xaml.Data;
|
||||
internal class AdvancedCollectionView<T> : IAdvancedCollectionView<T>, INotifyPropertyChanged, ISupportIncrementalLoading, IComparer<T>
|
||||
where T : class, IAdvancedCollectionViewItem
|
||||
{
|
||||
private readonly bool created;
|
||||
private readonly List<T> view;
|
||||
private readonly ObservableCollection<SortDescription> sortDescriptions;
|
||||
private readonly bool liveShapingEnabled;
|
||||
private readonly HashSet<string?> observedFilterProperties = [];
|
||||
//private readonly HashSet<string?> observedFilterProperties = [];
|
||||
|
||||
private IList<T> source;
|
||||
private Predicate<T>? filter;
|
||||
@@ -32,13 +33,14 @@ internal class AdvancedCollectionView<T> : IAdvancedCollectionView<T>, INotifyPr
|
||||
{
|
||||
}
|
||||
|
||||
public AdvancedCollectionView(IList<T> source, bool isLiveShaping = true)
|
||||
public AdvancedCollectionView(IList<T> source)
|
||||
{
|
||||
liveShapingEnabled = isLiveShaping;
|
||||
view = [];
|
||||
sortDescriptions = [];
|
||||
sortDescriptions.CollectionChanged += SortDescriptionsCollectionChanged;
|
||||
Source = source;
|
||||
|
||||
created = true;
|
||||
}
|
||||
|
||||
public event EventHandler<object>? CurrentChanged;
|
||||
@@ -49,7 +51,61 @@ internal class AdvancedCollectionView<T> : IAdvancedCollectionView<T>, INotifyPr
|
||||
|
||||
public event VectorChangedEventHandler<object>? VectorChanged;
|
||||
|
||||
public IList<T> Source
|
||||
public int Count
|
||||
{
|
||||
get => view.Count;
|
||||
}
|
||||
|
||||
public bool IsReadOnly { get => source is null || source is not INotifyCollectionChanged || source.IsReadOnly; }
|
||||
|
||||
public IObservableVector<object> CollectionGroups
|
||||
{
|
||||
get => default!;
|
||||
}
|
||||
|
||||
public T? CurrentItem
|
||||
{
|
||||
get => CurrentPosition > -1 && CurrentPosition < view.Count ? view[CurrentPosition] : default;
|
||||
set => MoveCurrentTo(value);
|
||||
}
|
||||
|
||||
public int CurrentPosition { get; private set; }
|
||||
|
||||
public bool HasMoreItems { get => source is ISupportIncrementalLoading { HasMoreItems: true }; }
|
||||
|
||||
public bool IsCurrentAfterLast { get => CurrentPosition >= view.Count; }
|
||||
|
||||
public bool IsCurrentBeforeFirst { get => CurrentPosition < 0; }
|
||||
|
||||
public bool CanFilter { get => true; }
|
||||
|
||||
public Predicate<T>? Filter
|
||||
{
|
||||
get => filter;
|
||||
set
|
||||
{
|
||||
if (filter == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
filter = value;
|
||||
HandleFilterChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanSort
|
||||
{
|
||||
get => true;
|
||||
}
|
||||
|
||||
public ObservableCollection<SortDescription> SortDescriptions { get => sortDescriptions; }
|
||||
|
||||
public IList<T> SourceCollection { get => source; }
|
||||
|
||||
public List<T> View { get => view; }
|
||||
|
||||
private IList<T> Source
|
||||
{
|
||||
get => source;
|
||||
|
||||
@@ -73,101 +129,24 @@ internal class AdvancedCollectionView<T> : IAdvancedCollectionView<T>, INotifyPr
|
||||
|
||||
if (source is INotifyCollectionChanged sourceINCC)
|
||||
{
|
||||
sourceWeakEventListener = new WeakEventListener<AdvancedCollectionView<T>, object?, NotifyCollectionChangedEventArgs>(this)
|
||||
sourceWeakEventListener = new(this)
|
||||
{
|
||||
OnEventAction = static (target, source, args) => target.SourceNotifyCollectionChangedCollectionChanged(args),
|
||||
OnDetachAction = (listener) => sourceINCC.CollectionChanged -= listener.OnEvent,
|
||||
OnEventAction = OnSourceNotifyCollectionCollectionChanged,
|
||||
OnDetachAction = listener => sourceINCC.CollectionChanged -= listener.OnEvent,
|
||||
};
|
||||
sourceINCC.CollectionChanged += sourceWeakEventListener.OnEvent;
|
||||
}
|
||||
|
||||
HandleSourceChanged();
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get => view.Count;
|
||||
}
|
||||
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get => source is null;
|
||||
}
|
||||
|
||||
public IObservableVector<object> CollectionGroups
|
||||
{
|
||||
get => default!;
|
||||
}
|
||||
|
||||
public T? CurrentItem
|
||||
{
|
||||
get => CurrentPosition > -1 && CurrentPosition < view.Count ? view[CurrentPosition] : default;
|
||||
set => MoveCurrentTo(value);
|
||||
}
|
||||
|
||||
public int CurrentPosition
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public bool HasMoreItems
|
||||
{
|
||||
get => source is ISupportIncrementalLoading { HasMoreItems: true };
|
||||
}
|
||||
|
||||
public bool IsCurrentAfterLast
|
||||
{
|
||||
get => CurrentPosition >= view.Count;
|
||||
}
|
||||
|
||||
public bool IsCurrentBeforeFirst
|
||||
{
|
||||
get => CurrentPosition < 0;
|
||||
}
|
||||
|
||||
public bool CanFilter
|
||||
{
|
||||
get => true;
|
||||
}
|
||||
|
||||
public Predicate<T>? Filter
|
||||
{
|
||||
get => filter;
|
||||
set
|
||||
{
|
||||
if (filter == value)
|
||||
static void OnSourceNotifyCollectionCollectionChanged(AdvancedCollectionView<T> target, object? source, NotifyCollectionChangedEventArgs args)
|
||||
{
|
||||
return;
|
||||
target.SourceNotifyCollectionChangedCollectionChanged(args);
|
||||
}
|
||||
|
||||
filter = value;
|
||||
HandleFilterChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanSort
|
||||
{
|
||||
get => true;
|
||||
}
|
||||
|
||||
public ObservableCollection<SortDescription> SortDescriptions
|
||||
{
|
||||
get => sortDescriptions;
|
||||
}
|
||||
|
||||
public IList<T> SourceCollection
|
||||
{
|
||||
get => source;
|
||||
}
|
||||
|
||||
public List<T> View
|
||||
{
|
||||
get => view;
|
||||
}
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get => view[index];
|
||||
@@ -275,16 +254,6 @@ internal class AdvancedCollectionView<T> : IAdvancedCollectionView<T>, INotifyPr
|
||||
return (source as ISupportIncrementalLoading)?.LoadMoreItemsAsync(count);
|
||||
}
|
||||
|
||||
public void ObserveFilterProperty(string propertyName)
|
||||
{
|
||||
observedFilterProperties.Add(propertyName);
|
||||
}
|
||||
|
||||
public void ClearObservedFilterProperties()
|
||||
{
|
||||
observedFilterProperties.Clear();
|
||||
}
|
||||
|
||||
public IDisposable DeferRefresh()
|
||||
{
|
||||
return new NotificationDeferrer(this);
|
||||
@@ -324,36 +293,20 @@ internal class AdvancedCollectionView<T> : IAdvancedCollectionView<T>, INotifyPr
|
||||
|
||||
private void OnPropertyChanged([CallerMemberName] string propertyName = default!)
|
||||
{
|
||||
if (!created)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
private void ItemOnPropertyChanged(object? item, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (!liveShapingEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ArgumentNullException.ThrowIfNull(item);
|
||||
T typedItem = (T)item;
|
||||
|
||||
bool? filterResult = filter?.Invoke(typedItem);
|
||||
|
||||
if (filterResult.HasValue && observedFilterProperties.Contains(e.PropertyName))
|
||||
{
|
||||
int viewIndex = view.IndexOf(typedItem);
|
||||
if (viewIndex != -1 && !filterResult.Value)
|
||||
{
|
||||
RemoveFromView(viewIndex, typedItem);
|
||||
}
|
||||
else if (viewIndex == -1 && filterResult.Value)
|
||||
{
|
||||
int index = source.IndexOf(typedItem);
|
||||
HandleSourceItemAdded(index, typedItem);
|
||||
}
|
||||
}
|
||||
|
||||
if ((filterResult ?? true) && SortDescriptions.Any(sd => sd.PropertyName == e.PropertyName))
|
||||
if ((filter?.Invoke(typedItem) ?? true) && SortDescriptions.Any(sd => sd.PropertyName == e.PropertyName))
|
||||
{
|
||||
int oldIndex = view.IndexOf(typedItem);
|
||||
|
||||
@@ -392,7 +345,7 @@ internal class AdvancedCollectionView<T> : IAdvancedCollectionView<T>, INotifyPr
|
||||
|
||||
private void AttachPropertyChangedHandler(IEnumerable items)
|
||||
{
|
||||
if (!liveShapingEnabled || items is null)
|
||||
if (items is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -408,7 +361,7 @@ internal class AdvancedCollectionView<T> : IAdvancedCollectionView<T>, INotifyPr
|
||||
|
||||
private void DetachPropertyChangedHandler(IEnumerable items)
|
||||
{
|
||||
if (!liveShapingEnabled || items is null)
|
||||
if (items is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -467,26 +420,36 @@ internal class AdvancedCollectionView<T> : IAdvancedCollectionView<T>, INotifyPr
|
||||
{
|
||||
T? currentItem = CurrentItem;
|
||||
view.Clear();
|
||||
foreach (T item in Source)
|
||||
{
|
||||
if (filter is not null && !filter(item))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
view.TrimExcess();
|
||||
|
||||
if (sortDescriptions.Count > 0)
|
||||
if (filter is null && sortDescriptions.Count <= 0)
|
||||
{
|
||||
// Fast path
|
||||
View.AddRange(Source);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (T item in Source)
|
||||
{
|
||||
int targetIndex = view.BinarySearch(item, this);
|
||||
if (targetIndex < 0)
|
||||
if (filter is not null && !filter(item))
|
||||
{
|
||||
targetIndex = ~targetIndex;
|
||||
continue;
|
||||
}
|
||||
|
||||
view.Insert(targetIndex, item);
|
||||
}
|
||||
else
|
||||
{
|
||||
view.Add(item);
|
||||
if (sortDescriptions.Count > 0)
|
||||
{
|
||||
int targetIndex = view.BinarySearch(item, this);
|
||||
if (targetIndex < 0)
|
||||
{
|
||||
targetIndex = ~targetIndex;
|
||||
}
|
||||
|
||||
view.Insert(targetIndex, item);
|
||||
}
|
||||
else
|
||||
{
|
||||
view.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -664,6 +627,7 @@ internal class AdvancedCollectionView<T> : IAdvancedCollectionView<T>, INotifyPr
|
||||
|
||||
if (i < -1 || i >= view.Count)
|
||||
{
|
||||
Debugger.Break(); // Figure out how this will hit.
|
||||
OnPropertyChanged(nameof(CurrentItem));
|
||||
return false;
|
||||
}
|
||||
@@ -682,7 +646,7 @@ internal class AdvancedCollectionView<T> : IAdvancedCollectionView<T>, INotifyPr
|
||||
|
||||
private void OnCurrentChanging(CurrentChangingEventArgs e)
|
||||
{
|
||||
if (deferCounter > 0)
|
||||
if (!created || deferCounter > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -692,7 +656,7 @@ internal class AdvancedCollectionView<T> : IAdvancedCollectionView<T>, INotifyPr
|
||||
|
||||
private void OnCurrentChanged()
|
||||
{
|
||||
if (deferCounter > 0)
|
||||
if (!created || deferCounter > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -704,7 +668,7 @@ internal class AdvancedCollectionView<T> : IAdvancedCollectionView<T>, INotifyPr
|
||||
|
||||
private void OnVectorChanged(IVectorChangedEventArgs e)
|
||||
{
|
||||
if (deferCounter > 0)
|
||||
if (!created || deferCounter > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -44,8 +44,6 @@ internal interface IAdvancedCollectionView<T> : ICollectionView, IEnumerable
|
||||
|
||||
void Add(T item);
|
||||
|
||||
void ClearObservedFilterProperties();
|
||||
|
||||
bool ICollection<object>.Contains(object item)
|
||||
{
|
||||
return Contains((T)item);
|
||||
@@ -101,8 +99,6 @@ internal interface IAdvancedCollectionView<T> : ICollectionView, IEnumerable
|
||||
|
||||
bool MoveCurrentTo(T? item);
|
||||
|
||||
void ObserveFilterProperty(string propertyName);
|
||||
|
||||
void Refresh();
|
||||
|
||||
void RefreshFilter();
|
||||
|
||||
@@ -140,7 +140,7 @@ internal sealed partial class AchievementViewModel : Abstraction.ViewModel, INav
|
||||
IAdvancedDbCollectionView<EntityArchive> archives = await scopeContext.AchievementService.GetArchivesAsync(CancellationToken).ConfigureAwait(false);
|
||||
await scopeContext.TaskContext.SwitchToMainThreadAsync();
|
||||
|
||||
AchievementGoals = new(sortedGoals, true);
|
||||
AchievementGoals = new(sortedGoals);
|
||||
Archives = archives;
|
||||
Archives.MoveCurrentTo(Archives.SourceCollection.SelectedOrDefault());
|
||||
return true;
|
||||
@@ -299,7 +299,7 @@ internal sealed partial class AchievementViewModel : Abstraction.ViewModel, INav
|
||||
}
|
||||
|
||||
await scopeContext.TaskContext.SwitchToMainThreadAsync();
|
||||
Achievements = new(combined, true);
|
||||
Achievements = new(combined);
|
||||
AchievementFinishPercent.Update(this);
|
||||
UpdateAchievementsFilterByGoal(AchievementGoals?.CurrentItem);
|
||||
UpdateAchievementsSort();
|
||||
|
||||
@@ -328,7 +328,7 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel, IView
|
||||
ObservableReorderableDbCollection<GameAccount> accounts = gameService.GameAccountCollection;
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
GameAccountsView = new(accounts, true)
|
||||
GameAccountsView = new(accounts)
|
||||
{
|
||||
Filter = gameAccountFilter.Filter,
|
||||
};
|
||||
|
||||
@@ -61,7 +61,7 @@ internal sealed partial class LaunchGameViewModelSlim : Abstraction.ViewModelSli
|
||||
gameAccountFilter = new(scheme?.GetSchemeType());
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
GameAccountsView = new(accounts, true)
|
||||
GameAccountsView = new(accounts)
|
||||
{
|
||||
Filter = gameAccountFilter.Filter,
|
||||
};
|
||||
|
||||
@@ -86,7 +86,7 @@ internal sealed partial class SpiralAbyssRecordViewModel : Abstraction.ViewModel
|
||||
}
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
SpiralAbyssEntries = new(collection, true);
|
||||
SpiralAbyssEntries = new(collection);
|
||||
SpiralAbyssEntries.MoveCurrentTo(SpiralAbyssEntries.SourceCollection.FirstOrDefault(s => s.Engaged));
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
|
||||
@@ -110,7 +110,7 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel
|
||||
using (await EnterCriticalSectionAsync().ConfigureAwait(false))
|
||||
{
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
Avatars = new(list, true);
|
||||
Avatars = new(list);
|
||||
Selected = Avatars.View.ElementAtOrDefault(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ internal sealed partial class WikiMonsterViewModel : Abstraction.ViewModel
|
||||
using (await EnterCriticalSectionAsync().ConfigureAwait(false))
|
||||
{
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
Monsters = new(ordered, true);
|
||||
Monsters = new(ordered);
|
||||
Selected = Monsters.View.ElementAtOrDefault(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel
|
||||
{
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
|
||||
Weapons = new(list, true);
|
||||
Weapons = new(list);
|
||||
Selected = Weapons.View.ElementAtOrDefault(0);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user