mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
generic AdvancedCollectionView
This commit is contained in:
@@ -16,25 +16,26 @@ using NotifyCollectionChangedAction = System.Collections.Specialized.NotifyColle
|
||||
|
||||
namespace Snap.Hutao.Control.Collection.AdvancedCollectionView;
|
||||
|
||||
internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyPropertyChanged, ISupportIncrementalLoading, IComparer<object>
|
||||
internal sealed class AdvancedCollectionView<T> : IAdvancedCollectionView<T>, INotifyPropertyChanged, ISupportIncrementalLoading, IComparer<T>
|
||||
where T : class
|
||||
{
|
||||
private readonly List<object> view;
|
||||
private readonly List<T> view;
|
||||
private readonly ObservableCollection<SortDescription> sortDescriptions;
|
||||
private readonly Dictionary<string, PropertyInfo> sortProperties;
|
||||
private readonly Dictionary<string, PropertyInfo?> sortProperties;
|
||||
private readonly bool liveShapingEnabled;
|
||||
private readonly HashSet<string> observedFilterProperties = [];
|
||||
private readonly HashSet<string?> observedFilterProperties = [];
|
||||
|
||||
private IList source;
|
||||
private Predicate<object>? filter;
|
||||
private IList<T> source;
|
||||
private Predicate<T>? filter;
|
||||
private int deferCounter;
|
||||
private WeakEventListener<AdvancedCollectionView, object?, NotifyCollectionChangedEventArgs>? sourceWeakEventListener;
|
||||
private WeakEventListener<AdvancedCollectionView<T>, object?, NotifyCollectionChangedEventArgs>? sourceWeakEventListener;
|
||||
|
||||
public AdvancedCollectionView()
|
||||
: this(new List<object>(0))
|
||||
: this(new List<T>(0))
|
||||
{
|
||||
}
|
||||
|
||||
public AdvancedCollectionView(IList source, bool isLiveShaping = false)
|
||||
public AdvancedCollectionView(IList<T> source, bool isLiveShaping = false)
|
||||
{
|
||||
liveShapingEnabled = isLiveShaping;
|
||||
view = [];
|
||||
@@ -44,22 +45,22 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
Source = source;
|
||||
}
|
||||
|
||||
public event VectorChangedEventHandler<object>? VectorChanged;
|
||||
|
||||
public event EventHandler<object>? CurrentChanged;
|
||||
|
||||
public event CurrentChangingEventHandler? CurrentChanging;
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
public IList Source
|
||||
public event VectorChangedEventHandler<object>? VectorChanged;
|
||||
|
||||
public IList<T> Source
|
||||
{
|
||||
get => source;
|
||||
|
||||
[MemberNotNull(nameof(source))]
|
||||
set
|
||||
{
|
||||
if (source == value)
|
||||
if (ReferenceEquals(source, value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -74,22 +75,21 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
|
||||
sourceWeakEventListener?.Detach();
|
||||
|
||||
if (source is INotifyCollectionChanged sourceNcc)
|
||||
if (source is INotifyCollectionChanged sourceNotifyCollectionChanged)
|
||||
{
|
||||
sourceWeakEventListener =
|
||||
new WeakEventListener<AdvancedCollectionView, object?, NotifyCollectionChangedEventArgs>(this)
|
||||
{
|
||||
// Call the actual collection changed event
|
||||
OnEventAction = (source, changed, arg3) => SourceNotifyCollectionChangedCollectionChanged(source, arg3),
|
||||
sourceWeakEventListener = new WeakEventListener<AdvancedCollectionView<T>, object?, NotifyCollectionChangedEventArgs>(this)
|
||||
{
|
||||
// Call the actual collection changed event
|
||||
OnEventAction = (source, changed, arg3) => SourceNotifyCollectionChangedCollectionChanged(source, arg3),
|
||||
|
||||
// The source doesn't exist anymore
|
||||
OnDetachAction = (listener) =>
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(sourceWeakEventListener);
|
||||
sourceNcc.CollectionChanged -= sourceWeakEventListener.OnEvent;
|
||||
},
|
||||
};
|
||||
sourceNcc.CollectionChanged += sourceWeakEventListener.OnEvent;
|
||||
// The source doesn't exist anymore
|
||||
OnDetachAction = (listener) =>
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(sourceWeakEventListener);
|
||||
sourceNotifyCollectionChanged.CollectionChanged -= sourceWeakEventListener.OnEvent;
|
||||
},
|
||||
};
|
||||
sourceNotifyCollectionChanged.CollectionChanged += sourceWeakEventListener.OnEvent;
|
||||
}
|
||||
|
||||
HandleSourceChanged();
|
||||
@@ -112,9 +112,9 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
get => default!;
|
||||
}
|
||||
|
||||
public object? CurrentItem
|
||||
public T? CurrentItem
|
||||
{
|
||||
get => CurrentPosition > -1 && CurrentPosition < view.Count ? view[CurrentPosition] : null;
|
||||
get => CurrentPosition > -1 && CurrentPosition < view.Count ? view[CurrentPosition] : default;
|
||||
set => MoveCurrentTo(value);
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
|
||||
public bool HasMoreItems
|
||||
{
|
||||
get => (source as ISupportIncrementalLoading)?.HasMoreItems ?? false;
|
||||
get => source is ISupportIncrementalLoading { HasMoreItems: true };
|
||||
}
|
||||
|
||||
public bool IsCurrentAfterLast
|
||||
@@ -140,7 +140,7 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
get => true;
|
||||
}
|
||||
|
||||
public Predicate<object> Filter
|
||||
public Predicate<T>? Filter
|
||||
{
|
||||
get => filter;
|
||||
set
|
||||
@@ -165,12 +165,17 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
get => sortDescriptions;
|
||||
}
|
||||
|
||||
public IEnumerable SourceCollection
|
||||
public IEnumerable<T> SourceCollection
|
||||
{
|
||||
get => source;
|
||||
}
|
||||
|
||||
public object this[int index]
|
||||
public IReadOnlyList<T> View
|
||||
{
|
||||
get => view;
|
||||
}
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get => view[index];
|
||||
set => view[index] = value;
|
||||
@@ -191,7 +196,7 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
HandleSortChanged();
|
||||
}
|
||||
|
||||
public IEnumerator<object> GetEnumerator()
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return view.GetEnumerator();
|
||||
}
|
||||
@@ -201,7 +206,7 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
return view.GetEnumerator();
|
||||
}
|
||||
|
||||
public void Add(object item)
|
||||
public void Add(T item)
|
||||
{
|
||||
ThrowHelper.NotSupportedIf(IsReadOnly, "Collection is read-only.");
|
||||
source.Add(item);
|
||||
@@ -213,29 +218,30 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
source.Clear();
|
||||
}
|
||||
|
||||
public bool Contains(object item)
|
||||
public bool Contains(T item)
|
||||
{
|
||||
return view.Contains(item);
|
||||
}
|
||||
|
||||
public void CopyTo(object[] array, int arrayIndex)
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
view.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
public bool Remove(object item)
|
||||
public bool Remove(T item)
|
||||
{
|
||||
ThrowHelper.NotSupportedIf(IsReadOnly, "Collection is read-only.");
|
||||
source.Remove(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
public int IndexOf(object item)
|
||||
[SuppressMessage("", "SH007")]
|
||||
public int IndexOf(T? item)
|
||||
{
|
||||
return view.IndexOf(item);
|
||||
return view.IndexOf(item!);
|
||||
}
|
||||
|
||||
public void Insert(int index, object item)
|
||||
public void Insert(int index, T item)
|
||||
{
|
||||
ThrowHelper.NotSupportedIf(IsReadOnly, "Collection is read-only.");
|
||||
source.Insert(index, item);
|
||||
@@ -246,9 +252,9 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
Remove(view[index]);
|
||||
}
|
||||
|
||||
public bool MoveCurrentTo(object? item)
|
||||
public bool MoveCurrentTo(T? item)
|
||||
{
|
||||
return item == CurrentItem || MoveCurrentToIndex(IndexOf(item));
|
||||
return (item is not null && item.Equals(CurrentItem)) || MoveCurrentToIndex(IndexOf(item));
|
||||
}
|
||||
|
||||
public bool MoveCurrentToPosition(int index)
|
||||
@@ -296,14 +302,14 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
return new NotificationDeferrer(this);
|
||||
}
|
||||
|
||||
int IComparer<object>.Compare(object? x, object? y)
|
||||
int IComparer<T>.Compare(T? x, T? y)
|
||||
{
|
||||
if (sortProperties.Count <= 0)
|
||||
{
|
||||
Type? listType = source?.GetType();
|
||||
Type listType = source.GetType();
|
||||
Type? type;
|
||||
|
||||
if (listType is { IsGenericType: true })
|
||||
if (listType.IsGenericType)
|
||||
{
|
||||
type = listType.GetGenericArguments()[0];
|
||||
}
|
||||
@@ -323,7 +329,7 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
|
||||
foreach (SortDescription sd in sortDescriptions)
|
||||
{
|
||||
object cx, cy;
|
||||
T? cx, cy;
|
||||
|
||||
if (string.IsNullOrEmpty(sd.PropertyName))
|
||||
{
|
||||
@@ -332,17 +338,17 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
}
|
||||
else
|
||||
{
|
||||
PropertyInfo pi = sortProperties[sd.PropertyName];
|
||||
PropertyInfo? pi = sortProperties[sd.PropertyName];
|
||||
|
||||
cx = pi.GetValue(x!);
|
||||
cy = pi.GetValue(y!);
|
||||
cx = (T?)pi?.GetValue(x);
|
||||
cy = (T?)pi?.GetValue(y);
|
||||
}
|
||||
|
||||
int cmp = sd.Comparer.Compare(cx, cy);
|
||||
|
||||
if (cmp != 0)
|
||||
if (cmp is not 0)
|
||||
{
|
||||
return sd.Direction == SortDirection.Ascending ? +cmp : -cmp;
|
||||
return sd.Direction is SortDirection.Ascending ? +cmp : -cmp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,25 +367,28 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
return;
|
||||
}
|
||||
|
||||
bool? filterResult = filter?.Invoke(item);
|
||||
ArgumentNullException.ThrowIfNull(item);
|
||||
T typedItem = (T)item;
|
||||
|
||||
bool? filterResult = filter?.Invoke(typedItem);
|
||||
|
||||
if (filterResult.HasValue && observedFilterProperties.Contains(e.PropertyName))
|
||||
{
|
||||
int viewIndex = view.IndexOf(item);
|
||||
int viewIndex = view.IndexOf(typedItem);
|
||||
if (viewIndex != -1 && !filterResult.Value)
|
||||
{
|
||||
RemoveFromView(viewIndex, item);
|
||||
RemoveFromView(viewIndex, typedItem);
|
||||
}
|
||||
else if (viewIndex == -1 && filterResult.Value)
|
||||
{
|
||||
int index = source.IndexOf(item);
|
||||
HandleItemAdded(index, item);
|
||||
int index = source.IndexOf(typedItem);
|
||||
HandleItemAdded(index, typedItem);
|
||||
}
|
||||
}
|
||||
|
||||
if ((filterResult ?? true) && SortDescriptions.Any(sd => sd.PropertyName == e.PropertyName))
|
||||
{
|
||||
int oldIndex = view.IndexOf(item);
|
||||
int oldIndex = view.IndexOf(typedItem);
|
||||
|
||||
// Check if item is in view:
|
||||
if (oldIndex < 0)
|
||||
@@ -388,7 +397,7 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
}
|
||||
|
||||
view.RemoveAt(oldIndex);
|
||||
int targetIndex = view.BinarySearch(item, this);
|
||||
int targetIndex = view.BinarySearch(typedItem, this);
|
||||
if (targetIndex < 0)
|
||||
{
|
||||
targetIndex = ~targetIndex;
|
||||
@@ -397,15 +406,15 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
// Only trigger expensive UI updates if the index really changed:
|
||||
if (targetIndex != oldIndex)
|
||||
{
|
||||
OnVectorChanged(new VectorChangedEventArgs(CollectionChange.ItemRemoved, oldIndex, item));
|
||||
OnVectorChanged(new VectorChangedEventArgs(CollectionChange.ItemRemoved, oldIndex, typedItem));
|
||||
|
||||
view.Insert(targetIndex, item);
|
||||
view.Insert(targetIndex, typedItem);
|
||||
|
||||
OnVectorChanged(new VectorChangedEventArgs(CollectionChange.ItemInserted, targetIndex, item));
|
||||
OnVectorChanged(new VectorChangedEventArgs(CollectionChange.ItemInserted, targetIndex, typedItem));
|
||||
}
|
||||
else
|
||||
{
|
||||
view.Insert(targetIndex, item);
|
||||
view.Insert(targetIndex, typedItem);
|
||||
}
|
||||
}
|
||||
else if (string.IsNullOrEmpty(e.PropertyName))
|
||||
@@ -421,9 +430,12 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (INotifyPropertyChanged item in items.OfType<INotifyPropertyChanged>())
|
||||
foreach (object item in items)
|
||||
{
|
||||
item.PropertyChanged += ItemOnPropertyChanged;
|
||||
if (item is INotifyPropertyChanged notifyPropertyChanged)
|
||||
{
|
||||
notifyPropertyChanged.PropertyChanged += ItemOnPropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,9 +446,12 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (INotifyPropertyChanged item in items.OfType<INotifyPropertyChanged>())
|
||||
foreach (object item in items)
|
||||
{
|
||||
item.PropertyChanged -= ItemOnPropertyChanged;
|
||||
if (item is INotifyPropertyChanged notifyPropertyChanged)
|
||||
{
|
||||
notifyPropertyChanged.PropertyChanged -= ItemOnPropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,7 +469,7 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
{
|
||||
for (int index = 0; index < view.Count; index++)
|
||||
{
|
||||
object item = view.ElementAt(index);
|
||||
T item = view[index];
|
||||
if (filter(item))
|
||||
{
|
||||
continue;
|
||||
@@ -465,12 +480,11 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
}
|
||||
}
|
||||
|
||||
HashSet<object> viewHash = new(view);
|
||||
HashSet<T> viewHash = new(view);
|
||||
int viewIndex = 0;
|
||||
for (int index = 0; index < source.Count; index++)
|
||||
{
|
||||
object? item = source[index];
|
||||
ArgumentNullException.ThrowIfNull(item);
|
||||
T item = source[index];
|
||||
if (viewHash.Contains(item))
|
||||
{
|
||||
viewIndex++;
|
||||
@@ -487,16 +501,16 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
private void HandleSourceChanged()
|
||||
{
|
||||
sortProperties.Clear();
|
||||
object? currentItem = CurrentItem;
|
||||
T? currentItem = CurrentItem;
|
||||
view.Clear();
|
||||
foreach (object? item in Source)
|
||||
foreach (T item in Source)
|
||||
{
|
||||
if (filter is not null && !filter(item))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sortDescriptions.Any())
|
||||
if (sortDescriptions.Count > 0)
|
||||
{
|
||||
int targetIndex = view.BinarySearch(item, this);
|
||||
if (targetIndex < 0)
|
||||
@@ -530,7 +544,7 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
{
|
||||
object? newItem = e.NewItems[0];
|
||||
ArgumentNullException.ThrowIfNull(newItem);
|
||||
HandleItemAdded(e.NewStartingIndex, newItem);
|
||||
HandleItemAdded(e.NewStartingIndex, (T)newItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -548,7 +562,7 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
{
|
||||
object? oldItem = e.OldItems[0];
|
||||
ArgumentNullException.ThrowIfNull(oldItem);
|
||||
HandleItemRemoved(e.OldStartingIndex, oldItem);
|
||||
HandleItemRemoved(e.OldStartingIndex, (T)oldItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -569,7 +583,7 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
}
|
||||
}
|
||||
|
||||
private bool HandleItemAdded(int newStartingIndex, object newItem, int? viewIndex = null)
|
||||
private bool HandleItemAdded(int newStartingIndex, T newItem, int? viewIndex = null)
|
||||
{
|
||||
if (filter is not null && !filter(newItem))
|
||||
{
|
||||
@@ -578,7 +592,7 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
|
||||
int newViewIndex = view.Count;
|
||||
|
||||
if (sortDescriptions.Any())
|
||||
if (sortDescriptions.Count > 0)
|
||||
{
|
||||
sortProperties.Clear();
|
||||
newViewIndex = view.BinarySearch(newItem, this);
|
||||
@@ -617,7 +631,7 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
break;
|
||||
}
|
||||
|
||||
if (view[j] == source[i])
|
||||
if (Equals(view[j], source[i]))
|
||||
{
|
||||
j++;
|
||||
}
|
||||
@@ -635,7 +649,7 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
return true;
|
||||
}
|
||||
|
||||
private void HandleItemRemoved(int oldStartingIndex, object oldItem)
|
||||
private void HandleItemRemoved(int oldStartingIndex, T oldItem)
|
||||
{
|
||||
if (filter is not null && !filter(oldItem))
|
||||
{
|
||||
@@ -655,7 +669,7 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
RemoveFromView(oldStartingIndex, oldItem);
|
||||
}
|
||||
|
||||
private void RemoveFromView(int itemIndex, object item)
|
||||
private void RemoveFromView(int itemIndex, T item)
|
||||
{
|
||||
view.RemoveAt(itemIndex);
|
||||
if (itemIndex <= CurrentPosition)
|
||||
@@ -734,10 +748,10 @@ internal sealed class AdvancedCollectionView : IAdvancedCollectionView, INotifyP
|
||||
|
||||
internal sealed class NotificationDeferrer : IDisposable
|
||||
{
|
||||
private readonly AdvancedCollectionView advancedCollectionView;
|
||||
private readonly object? currentItem;
|
||||
private readonly AdvancedCollectionView<T> advancedCollectionView;
|
||||
private readonly T? currentItem;
|
||||
|
||||
public NotificationDeferrer(AdvancedCollectionView acvs)
|
||||
public NotificationDeferrer(AdvancedCollectionView<T> acvs)
|
||||
{
|
||||
advancedCollectionView = acvs;
|
||||
currentItem = advancedCollectionView.CurrentItem;
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using CommunityToolkit.WinUI.Collections;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using System.Collections;
|
||||
|
||||
namespace Snap.Hutao.Control.Collection.AdvancedCollectionView;
|
||||
|
||||
internal interface IAdvancedCollectionView<T> : ICollectionView, IEnumerable
|
||||
where T : class
|
||||
{
|
||||
bool CanFilter { get; }
|
||||
|
||||
bool CanSort { get; }
|
||||
|
||||
object? ICollectionView.CurrentItem
|
||||
{
|
||||
get => CurrentItem;
|
||||
}
|
||||
|
||||
new T? CurrentItem { get; }
|
||||
|
||||
Predicate<T>? Filter { get; set; }
|
||||
|
||||
IList<SortDescription> SortDescriptions { get; }
|
||||
|
||||
IEnumerable<T> SourceCollection { get; }
|
||||
|
||||
object IList<object>.this[int index]
|
||||
{
|
||||
get => this[index];
|
||||
set => this[index] = (T)value;
|
||||
}
|
||||
|
||||
new T this[int index] { get; set; }
|
||||
|
||||
void ICollection<object>.Add(object item)
|
||||
{
|
||||
Add((T)item);
|
||||
}
|
||||
|
||||
void Add(T item);
|
||||
|
||||
void ClearObservedFilterProperties();
|
||||
|
||||
bool ICollection<object>.Contains(object item)
|
||||
{
|
||||
return Contains((T)item);
|
||||
}
|
||||
|
||||
bool Contains(T item);
|
||||
|
||||
void ICollection<object>.CopyTo(object[] array, int arrayIndex)
|
||||
{
|
||||
CopyTo((T[])array, arrayIndex);
|
||||
}
|
||||
|
||||
void CopyTo(T[] array, int arrayIndex);
|
||||
|
||||
IDisposable DeferRefresh();
|
||||
|
||||
IEnumerator<object> IEnumerable<object>.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
new IEnumerator<T> GetEnumerator();
|
||||
|
||||
int IList<object>.IndexOf(object item)
|
||||
{
|
||||
return IndexOf((T)item);
|
||||
}
|
||||
|
||||
int IndexOf(T item);
|
||||
|
||||
void IList<object>.Insert(int index, object item)
|
||||
{
|
||||
Insert(index, (T)item);
|
||||
}
|
||||
|
||||
void Insert(int index, T item);
|
||||
|
||||
bool ICollectionView.MoveCurrentTo(object item)
|
||||
{
|
||||
return MoveCurrentTo((T)item);
|
||||
}
|
||||
|
||||
bool MoveCurrentTo(T item);
|
||||
|
||||
void ObserveFilterProperty(string propertyName);
|
||||
|
||||
void Refresh();
|
||||
|
||||
void RefreshFilter();
|
||||
|
||||
void RefreshSorting();
|
||||
|
||||
bool ICollection<object>.Remove(object item)
|
||||
{
|
||||
return Remove((T)item);
|
||||
}
|
||||
|
||||
bool Remove(T item);
|
||||
}
|
||||
@@ -43,7 +43,7 @@ internal sealed partial class AchievementViewModel : Abstraction.ViewModel, INav
|
||||
private readonly JsonSerializerOptions options;
|
||||
private readonly ITaskContext taskContext;
|
||||
|
||||
private AdvancedCollectionView? achievements;
|
||||
private AdvancedCollectionView<AchievementView>? achievements;
|
||||
private List<AchievementGoalView>? achievementGoals;
|
||||
private AchievementGoalView? selectedAchievementGoal;
|
||||
private ObservableCollection<EntityAchievementArchive>? archives;
|
||||
@@ -85,7 +85,7 @@ internal sealed partial class AchievementViewModel : Abstraction.ViewModel, INav
|
||||
/// <summary>
|
||||
/// 成就视图
|
||||
/// </summary>
|
||||
public AdvancedCollectionView? Achievements
|
||||
public AdvancedCollectionView<AchievementView>? Achievements
|
||||
{
|
||||
get => achievements;
|
||||
set => SetProperty(ref achievements, value);
|
||||
@@ -342,17 +342,19 @@ internal sealed partial class AchievementViewModel : Abstraction.ViewModel, INav
|
||||
[Command("SortUncompletedSwitchCommand")]
|
||||
private void UpdateAchievementsSort()
|
||||
{
|
||||
if (Achievements is not null)
|
||||
if (Achievements is null)
|
||||
{
|
||||
if (IsUncompletedItemsFirst)
|
||||
{
|
||||
Achievements.SortDescriptions.Add(uncompletedItemsFirstSortDescription);
|
||||
Achievements.SortDescriptions.Add(completionTimeSortDescription);
|
||||
}
|
||||
else
|
||||
{
|
||||
Achievements.SortDescriptions.Clear();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsUncompletedItemsFirst)
|
||||
{
|
||||
Achievements.SortDescriptions.Add(uncompletedItemsFirstSortDescription);
|
||||
Achievements.SortDescriptions.Add(completionTimeSortDescription);
|
||||
}
|
||||
else
|
||||
{
|
||||
Achievements.SortDescriptions.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,7 +369,7 @@ internal sealed partial class AchievementViewModel : Abstraction.ViewModel, INav
|
||||
else
|
||||
{
|
||||
Model.Primitive.AchievementGoalId goalId = goal.Id;
|
||||
Achievements.Filter = (object o) => o is AchievementView view && view.Inner.Goal == goalId;
|
||||
Achievements.Filter = (AchievementView view) => view.Inner.Goal == goalId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -383,19 +385,18 @@ internal sealed partial class AchievementViewModel : Abstraction.ViewModel, INav
|
||||
{
|
||||
if (uint.TryParse(search, out uint achievementId))
|
||||
{
|
||||
Achievements.Filter = obj => ((AchievementView)obj).Inner.Id == achievementId;
|
||||
Achievements.Filter = view => view.Inner.Id == achievementId;
|
||||
return;
|
||||
}
|
||||
|
||||
if (VersionRegex().IsMatch(search))
|
||||
{
|
||||
Achievements.Filter = obj => ((AchievementView)obj).Inner.Version == search;
|
||||
Achievements.Filter = view => view.Inner.Version == search;
|
||||
return;
|
||||
}
|
||||
|
||||
Achievements.Filter = obj =>
|
||||
Achievements.Filter = view =>
|
||||
{
|
||||
AchievementView view = (AchievementView)obj;
|
||||
return view.Inner.Title.Contains(search, StringComparison.CurrentCultureIgnoreCase)
|
||||
|| view.Inner.Description.Contains(search, StringComparison.CurrentCultureIgnoreCase);
|
||||
};
|
||||
|
||||
@@ -15,13 +15,13 @@ internal sealed class GameAccountFilter
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public bool Filter(object? item)
|
||||
public bool Filter(GameAccount? item)
|
||||
{
|
||||
if (type is null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return item is GameAccount account && account.Type == type;
|
||||
return item is not null && item.Type == type;
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel, IView
|
||||
private readonly AppOptions appOptions;
|
||||
|
||||
private LaunchScheme? selectedScheme;
|
||||
private AdvancedCollectionView? gameAccountsView;
|
||||
private AdvancedCollectionView<GameAccount>? gameAccountsView;
|
||||
private GameAccount? selectedGameAccount;
|
||||
private GameResource? gameResource;
|
||||
private bool gamePathSelectedAndValid;
|
||||
@@ -75,7 +75,7 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel, IView
|
||||
set => SetSelectedSchemeAsync(value).SafeForget();
|
||||
}
|
||||
|
||||
public AdvancedCollectionView? GameAccountsView { get => gameAccountsView; set => SetProperty(ref gameAccountsView, value); }
|
||||
public AdvancedCollectionView<GameAccount>? GameAccountsView { get => gameAccountsView; set => SetProperty(ref gameAccountsView, value); }
|
||||
|
||||
public GameAccount? SelectedGameAccount { get => selectedGameAccount; set => SetProperty(ref selectedGameAccount, value); }
|
||||
|
||||
@@ -130,9 +130,9 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel, IView
|
||||
ArgumentNullException.ThrowIfNull(GameAccountsView);
|
||||
|
||||
// Exists in the source collection
|
||||
if (GameAccountsView.SourceCollection.Cast<GameAccount>().FirstOrDefault(g => g.AttachUid == uid) is { } sourceAccount)
|
||||
if (GameAccountsView.SourceCollection.FirstOrDefault(g => g.AttachUid == uid) is { } sourceAccount)
|
||||
{
|
||||
SelectedGameAccount = GameAccountsView.Cast<GameAccount>().FirstOrDefault(g => g.AttachUid == uid);
|
||||
SelectedGameAccount = GameAccountsView.View.FirstOrDefault(g => g.AttachUid == uid);
|
||||
|
||||
// But not exists in the view for current scheme
|
||||
if (SelectedGameAccount is null)
|
||||
|
||||
@@ -27,13 +27,13 @@ internal sealed partial class LaunchGameViewModelSlim : Abstraction.ViewModelSli
|
||||
private readonly IGameServiceFacade gameService;
|
||||
private readonly ITaskContext taskContext;
|
||||
|
||||
private AdvancedCollectionView? gameAccountsView;
|
||||
private AdvancedCollectionView<GameAccount>? gameAccountsView;
|
||||
private GameAccount? selectedGameAccount;
|
||||
private GameAccountFilter? gameAccountFilter;
|
||||
|
||||
public LaunchStatusOptions LaunchStatusOptions { get => launchStatusOptions; }
|
||||
|
||||
public AdvancedCollectionView? GameAccountsView { get => gameAccountsView; set => SetProperty(ref gameAccountsView, value); }
|
||||
public AdvancedCollectionView<GameAccount>? GameAccountsView { get => gameAccountsView; set => SetProperty(ref gameAccountsView, value); }
|
||||
|
||||
/// <summary>
|
||||
/// 选中的账号
|
||||
|
||||
@@ -17,9 +17,9 @@ internal static class AvatarFilter
|
||||
/// </summary>
|
||||
/// <param name="input">输入</param>
|
||||
/// <returns>筛选操作</returns>
|
||||
public static Predicate<object> Compile(string input)
|
||||
public static Predicate<Avatar> Compile(string input)
|
||||
{
|
||||
return (object o) => o is Avatar avatar && DoFilter(input, avatar);
|
||||
return (Avatar avatar) => DoFilter(input, avatar);
|
||||
}
|
||||
|
||||
private static bool DoFilter(string input, Avatar avatar)
|
||||
|
||||
@@ -17,9 +17,9 @@ internal static class WeaponFilter
|
||||
/// </summary>
|
||||
/// <param name="input">输入</param>
|
||||
/// <returns>筛选操作</returns>
|
||||
public static Predicate<object> Compile(string input)
|
||||
public static Predicate<Weapon> Compile(string input)
|
||||
{
|
||||
return (object o) => o is Weapon weapon && DoFilter(input, weapon);
|
||||
return (Weapon weapon) => DoFilter(input, weapon);
|
||||
}
|
||||
|
||||
private static bool DoFilter(string input, Weapon weapon)
|
||||
|
||||
@@ -43,7 +43,7 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel
|
||||
private readonly CalculateClient calculateClient;
|
||||
private readonly IUserService userService;
|
||||
|
||||
private AdvancedCollectionView? avatars;
|
||||
private AdvancedCollectionView<Avatar>? avatars;
|
||||
private Avatar? selected;
|
||||
private string? filterText;
|
||||
private BaseValueInfo? baseValueInfo;
|
||||
@@ -53,7 +53,7 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel
|
||||
/// <summary>
|
||||
/// 角色列表
|
||||
/// </summary>
|
||||
public AdvancedCollectionView? Avatars { get => avatars; set => SetProperty(ref avatars, value); }
|
||||
public AdvancedCollectionView<Avatar>? Avatars { get => avatars; set => SetProperty(ref avatars, value); }
|
||||
|
||||
/// <summary>
|
||||
/// 选中的角色
|
||||
@@ -99,8 +99,8 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel
|
||||
await CombineComplexDataAsync(list, idMaterialMap).ConfigureAwait(false);
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
Avatars = new AdvancedCollectionView(list, true);
|
||||
Selected = Avatars.Cast<Avatar>().FirstOrDefault();
|
||||
Avatars = new(list, true);
|
||||
Selected = Avatars.View.ElementAtOrDefault(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ internal sealed partial class WikiMonsterViewModel : Abstraction.ViewModel
|
||||
private readonly IMetadataService metadataService;
|
||||
private readonly ITaskContext taskContext;
|
||||
|
||||
private AdvancedCollectionView? monsters;
|
||||
private AdvancedCollectionView<Monster>? monsters;
|
||||
private Monster? selected;
|
||||
private BaseValueInfo? baseValueInfo;
|
||||
private Dictionary<Level, Dictionary<GrowCurveType, float>>? levelMonsterCurveMap;
|
||||
@@ -28,7 +28,7 @@ internal sealed partial class WikiMonsterViewModel : Abstraction.ViewModel
|
||||
/// <summary>
|
||||
/// 角色列表
|
||||
/// </summary>
|
||||
public AdvancedCollectionView? Monsters { get => monsters; set => SetProperty(ref monsters, value); }
|
||||
public AdvancedCollectionView<Monster>? Monsters { get => monsters; set => SetProperty(ref monsters, value); }
|
||||
|
||||
/// <summary>
|
||||
/// 选中的角色
|
||||
@@ -65,8 +65,8 @@ internal sealed partial class WikiMonsterViewModel : Abstraction.ViewModel
|
||||
List<Monster> ordered = monsters.SortBy(m => m.RelationshipId.Value);
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
|
||||
Monsters = new AdvancedCollectionView(ordered, true);
|
||||
Selected = Monsters.Cast<Monster>().FirstOrDefault();
|
||||
Monsters = new(ordered, true);
|
||||
Selected = Monsters.View.ElementAtOrDefault(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel
|
||||
private readonly IInfoBarService infoBarService;
|
||||
private readonly IUserService userService;
|
||||
|
||||
private AdvancedCollectionView? weapons;
|
||||
private AdvancedCollectionView<Weapon>? weapons;
|
||||
private Weapon? selected;
|
||||
private string? filterText;
|
||||
private BaseValueInfo? baseValueInfo;
|
||||
@@ -50,7 +50,7 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel
|
||||
/// <summary>
|
||||
/// 角色列表
|
||||
/// </summary>
|
||||
public AdvancedCollectionView? Weapons { get => weapons; set => SetProperty(ref weapons, value); }
|
||||
public AdvancedCollectionView<Weapon>? Weapons { get => weapons; set => SetProperty(ref weapons, value); }
|
||||
|
||||
/// <summary>
|
||||
/// 选中的角色
|
||||
@@ -96,8 +96,8 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel
|
||||
|
||||
await taskContext.SwitchToMainThreadAsync();
|
||||
|
||||
Weapons = new AdvancedCollectionView(list, true);
|
||||
Selected = Weapons.Cast<Weapon>().FirstOrDefault();
|
||||
Weapons = new(list, true);
|
||||
Selected = Weapons.View.ElementAtOrDefault(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user