refine AdvancedCollectionView

This commit is contained in:
DismissedLight
2024-07-15 17:31:06 +08:00
parent 6489f66d13
commit bfdb4b0060
13 changed files with 116 additions and 156 deletions

View File

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

View File

@@ -41,7 +41,7 @@ internal sealed partial class SummaryFactory : ISummaryFactory
return new()
{
Avatars = new(views, true),
Avatars = new(views),
};
}
}

View File

@@ -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(),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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