diff --git a/src/Snap.Hutao/Snap.Hutao/Control/ScopedPage.cs b/src/Snap.Hutao/Snap.Hutao/Control/ScopedPage.cs
index d3eac6c9..06db70e8 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/ScopedPage.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/ScopedPage.cs
@@ -41,14 +41,12 @@ internal class ScopedPage : Page
/// 应当在 InitializeComponent() 前调用
///
/// 视图模型类型
- protected TViewModel InitializeWith()
+ protected void InitializeWith()
where TViewModel : class, IViewModel
{
IViewModel viewModel = currentScope.ServiceProvider.GetRequiredService();
viewModel.CancellationToken = viewCancellationTokenSource.Token;
DataContext = viewModel;
-
- return (TViewModel)viewModel;
}
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/AutoSuggestTokenBox.cs b/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/AutoSuggestTokenBox.cs
new file mode 100644
index 00000000..6ed4af22
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/AutoSuggestTokenBox.cs
@@ -0,0 +1,82 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using CommunityToolkit.WinUI.Controls;
+using Microsoft.UI.Xaml.Controls;
+using Snap.Hutao.Control.SuggestBox;
+
+namespace Snap.Hutao.View.Control;
+
+[DependencyProperty("FilterCommand", typeof(ICommand))]
+[DependencyProperty("ITokenizableItemsSource", typeof(IEnumerable))]
+internal sealed partial class AutoSuggestTokenBox : TokenizingTextBox
+{
+ public AutoSuggestTokenBox()
+ {
+ TextChanged += OnFilterSuggestionRequested;
+ QuerySubmitted += OnQuerySubmitted;
+ TokenItemAdding += OnTokenItemAdding;
+ TokenItemAdded += OnTokenItemModified;
+ TokenItemRemoved += OnTokenItemModified;
+ }
+
+ private void OnFilterSuggestionRequested(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
+ {
+ if (SuggestedItemsSource is not IEnumerable availableQueries)
+ {
+ return;
+ }
+
+ if (string.IsNullOrWhiteSpace(Text))
+ {
+ return;
+ }
+
+ if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
+ {
+ sender.ItemsSource = availableQueries.Where(q => q.Contains(Text, StringComparison.OrdinalIgnoreCase));
+ }
+ }
+
+ private void OnQuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
+ {
+ if (args.ChosenSuggestion is not null)
+ {
+ return;
+ }
+
+ if (FilterCommand.CanExecute(null))
+ {
+ FilterCommand.Execute(null);
+ }
+ }
+
+ private void OnTokenItemAdding(TokenizingTextBox sender, TokenItemAddingEventArgs args)
+ {
+ if (string.IsNullOrWhiteSpace(args.TokenText))
+ {
+ return;
+ }
+
+ if (ITokenizableItemsSource is null)
+ {
+ return;
+ }
+
+ if (ITokenizableItemsSource.SingleOrDefault(i => i.Name == args.TokenText) is { } item)
+ {
+ args.Item = item.Tokenize();
+ return;
+ }
+
+ args.Item = new SearchToken(args.TokenText);
+ }
+
+ private void OnTokenItemModified(TokenizingTextBox sender, object args)
+ {
+ if (FilterCommand.CanExecute(null))
+ {
+ FilterCommand.Execute(null);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/ISearchToken.cs b/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/ISearchToken.cs
new file mode 100644
index 00000000..7b054f10
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/ISearchToken.cs
@@ -0,0 +1,9 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Control.SuggestBox;
+
+internal interface ISearchToken
+{
+ string Value { get; set; }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/ITokenizable.cs b/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/ITokenizable.cs
new file mode 100644
index 00000000..bb8f32b8
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/ITokenizable.cs
@@ -0,0 +1,11 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Control.SuggestBox;
+
+internal interface ITokenizable
+{
+ string Name { get; }
+
+ ISearchToken Tokenize();
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/SearchToken.cs b/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/SearchToken.Wiki.cs
similarity index 73%
rename from src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/SearchToken.cs
rename to src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/SearchToken.Wiki.cs
index cb7617a2..256f51ee 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/SearchToken.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/SearchToken.Wiki.cs
@@ -2,38 +2,23 @@
// Licensed under the MIT license.
using Snap.Hutao.Model.Intrinsic.Frozen;
-using Snap.Hutao.Model.Metadata.Avatar;
using Snap.Hutao.Model.Metadata.Converter;
-using Snap.Hutao.Model.Metadata.Weapon;
using Windows.UI;
-namespace Snap.Hutao.ViewModel.Wiki;
+namespace Snap.Hutao.Control.SuggestBox;
-internal class SearchToken
+internal partial class SearchToken
{
private SearchTokenKind kind;
private bool isKindInitialized;
private object isKindInitializedLock = new();
- public SearchToken(string value)
+ public SearchToken(string value, Uri? sideIconUri)
+ : this(value)
{
- Value = value;
+ SideIconUri = sideIconUri;
}
- public SearchToken(Avatar avatar)
- {
- Value = avatar.Name;
- SideIconUri = AvatarSideIconConverter.IconNameToUri(avatar.SideIcon);
- }
-
- public SearchToken(Weapon weapon)
- {
- Value = weapon.Name;
- SideIconUri = EquipIconConverter.IconNameToUri(weapon.Icon);
- }
-
- public string Value { get; }
-
public Uri? SideIconUri { get; }
public Uri? IconUri
@@ -98,21 +83,4 @@ internal class SearchToken
}
}
}
-
- public override string ToString()
- {
- return Value;
- }
}
-
-[SuppressMessage("", "SA1201")]
-internal enum SearchTokenKind
-{
- AssociationTypes,
- BodyTypes,
- ElementNames,
- FightProperties,
- ItemQualities,
- Other, // Include avatar and weapon
- WeaponTypes,
-}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/SearchToken.cs b/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/SearchToken.cs
new file mode 100644
index 00000000..0eb17654
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/SearchToken.cs
@@ -0,0 +1,19 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Control.SuggestBox;
+
+internal sealed partial class SearchToken : ISearchToken
+{
+ public SearchToken(string value)
+ {
+ Value = value;
+ }
+
+ public string Value { get; set; } = default!;
+
+ public override string ToString()
+ {
+ return Value;
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/SearchTokenKind.Wiki.cs b/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/SearchTokenKind.Wiki.cs
new file mode 100644
index 00000000..4f1868c8
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Control/SuggestBox/SearchTokenKind.Wiki.cs
@@ -0,0 +1,15 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Control.SuggestBox;
+
+internal enum SearchTokenKind
+{
+ AssociationTypes,
+ BodyTypes,
+ ElementNames,
+ FightProperties,
+ ItemQualities,
+ Other, // Include avatar and weapon
+ WeaponTypes,
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs
index 4871692d..343afd8b 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs
@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Snap.Hutao.Control.SuggestBox;
using Snap.Hutao.Model.Calculable;
using Snap.Hutao.Model.Metadata.Abstraction;
using Snap.Hutao.Model.Metadata.Converter;
@@ -14,7 +15,7 @@ namespace Snap.Hutao.Model.Metadata.Avatar;
///
/// 角色的接口实现部分
///
-internal partial class Avatar : IStatisticsItemSource, ISummaryItemSource, IItemSource, INameQuality, ICalculableSource
+internal partial class Avatar : IStatisticsItemSource, ISummaryItemSource, IItemSource, INameQuality, ICalculableSource, ITokenizable
{
///
/// [非元数据] 搭配数据
@@ -96,4 +97,9 @@ internal partial class Avatar : IStatisticsItemSource, ISummaryItemSource, IItem
IsUp = isUp,
};
}
+
+ public ISearchToken Tokenize()
+ {
+ return new SearchToken(Name, AvatarSideIconConverter.IconNameToUri(SideIcon));
+ }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.Implementation.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.Implementation.cs
index 86a4465c..f677d19b 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.Implementation.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Weapon/Weapon.Implementation.cs
@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Snap.Hutao.Control.SuggestBox;
using Snap.Hutao.Model.Calculable;
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Metadata.Abstraction;
@@ -14,7 +15,7 @@ namespace Snap.Hutao.Model.Metadata.Weapon;
///
/// 武器的接口实现
///
-internal sealed partial class Weapon : IStatisticsItemSource, ISummaryItemSource, IItemSource, INameQuality, ICalculableSource
+internal sealed partial class Weapon : IStatisticsItemSource, ISummaryItemSource, IItemSource, INameQuality, ICalculableSource, ITokenizable
{
///
/// [非元数据] 搭配数据
@@ -104,4 +105,9 @@ internal sealed partial class Weapon : IStatisticsItemSource, ISummaryItemSource
IsUp = isUp,
};
}
+
+ public ISearchToken Tokenize()
+ {
+ return new SearchToken(Name, EquipIconConverter.IconNameToUri(Icon));
+ }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml
index 43c5d46e..af3cf59f 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml
@@ -305,13 +305,15 @@
-
public WikiAvatarPage()
{
- WikiAvatarViewModel viewModel = InitializeWith();
+ InitializeWith();
InitializeComponent();
-
- viewModel.Initialize(new TokenizingTextBoxAccessor(AvatarSuggestBox));
- }
-
- private class TokenizingTextBoxAccessor : ITokenizingTextBoxAccessor
- {
- public TokenizingTextBoxAccessor(TokenizingTextBox tokenizingTextBox)
- {
- TokenizingTextBox = tokenizingTextBox;
- }
-
- public TokenizingTextBox TokenizingTextBox { get; private set; }
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiWeaponPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiWeaponPage.xaml
index af05a9e6..4db320fc 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiWeaponPage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiWeaponPage.xaml
@@ -172,13 +172,15 @@
LocalSettingKeySuffixForCurrent="WikiWeaponPage.Weapons"/>
-
public WikiWeaponPage()
{
- WikiWeaponViewModel viewModel = InitializeWith();
+ InitializeWith();
InitializeComponent();
-
- viewModel.Initialize(new TokenizingTextBoxAccessor(WeaponSuggestBox));
- }
-
- private class TokenizingTextBoxAccessor : ITokenizingTextBoxAccessor
- {
- public TokenizingTextBoxAccessor(TokenizingTextBox tokenizingTextBox)
- {
- TokenizingTextBox = tokenizingTextBox;
- }
-
- public TokenizingTextBox TokenizingTextBox { get; private set; }
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/AvatarFilter.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/AvatarFilter.cs
index 9ddad9e7..c18c963c 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/AvatarFilter.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/AvatarFilter.cs
@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Snap.Hutao.Control.SuggestBox;
using Snap.Hutao.Model.Intrinsic.Frozen;
using Snap.Hutao.Model.Metadata.Avatar;
using System.Collections.ObjectModel;
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WeaponFilter.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WeaponFilter.cs
index 392a6027..99fdd8a0 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WeaponFilter.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WeaponFilter.cs
@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Snap.Hutao.Control.SuggestBox;
using Snap.Hutao.Model.Intrinsic.Frozen;
using Snap.Hutao.Model.Metadata.Weapon;
using System.Collections.ObjectModel;
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs
index 1691c7ab..4c55dca1 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiAvatarViewModel.cs
@@ -1,9 +1,8 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-using CommunityToolkit.WinUI.Controls;
-using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Control.Collection.AdvancedCollectionView;
+using Snap.Hutao.Control.SuggestBox;
using Snap.Hutao.Factory.ContentDialog;
using Snap.Hutao.Model.Calculable;
using Snap.Hutao.Model.Entity.Primitive;
@@ -37,7 +36,7 @@ namespace Snap.Hutao.ViewModel.Wiki;
[HighQuality]
[ConstructorGenerated]
[Injection(InjectAs.Scoped)]
-internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel, IWikiViewModelInitialization
+internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel
{
private readonly IContentDialogFactory contentDialogFactory;
private readonly ICultivationService cultivationService;
@@ -90,15 +89,6 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel, IWiki
public FrozenSet? AvailableQueries { get => availableQueries; }
- public void Initialize(ITokenizingTextBoxAccessor accessor)
- {
- accessor.TokenizingTextBox.TextChanged += OnFilterSuggestionRequested;
- accessor.TokenizingTextBox.QuerySubmitted += OnQuerySubmitted;
- accessor.TokenizingTextBox.TokenItemAdding += OnTokenItemAdding;
- accessor.TokenizingTextBox.TokenItemAdded += OnTokenItemModified;
- accessor.TokenizingTextBox.TokenItemRemoved += OnTokenItemModified;
- }
-
protected override async ValueTask InitializeUIAsync()
{
if (!await metadataService.InitializeAsync().ConfigureAwait(false))
@@ -234,60 +224,7 @@ internal sealed partial class WikiAvatarViewModel : Abstraction.ViewModel, IWiki
BaseValueInfo = new(avatar.MaxLevel, propertyCurveValues, levelAvatarCurveMap, avatarPromoteMap);
}
- private void OnFilterSuggestionRequested(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
- {
- if (Avatars is null)
- {
- return;
- }
-
- if (string.IsNullOrWhiteSpace(FilterToken))
- {
- return;
- }
-
- if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
- {
- sender.ItemsSource = availableQueries.Where(q => q.Contains(FilterToken, StringComparison.OrdinalIgnoreCase));
- }
- }
-
- private void OnQuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
- {
- if (args.ChosenSuggestion is not null)
- {
- return;
- }
-
- ApplyFilter();
- }
-
- private void OnTokenItemAdding(TokenizingTextBox sender, TokenItemAddingEventArgs args)
- {
- if (string.IsNullOrWhiteSpace(args.TokenText))
- {
- return;
- }
-
- if (Avatars is null)
- {
- return;
- }
-
- if (Avatars.SourceCollection.SingleOrDefault(a => a.Name == args.TokenText) is { } avatar)
- {
- args.Item = new SearchToken(avatar);
- return;
- }
-
- args.Item = new SearchToken(args.TokenText);
- }
-
- private void OnTokenItemModified(TokenizingTextBox sender, object args)
- {
- ApplyFilter();
- }
-
+ [Command("FilterCommand")]
private void ApplyFilter()
{
if (Avatars is null)
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs
index 3d8a2a3d..05dfbb1c 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Wiki/WikiWeaponViewModel.cs
@@ -1,9 +1,8 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-using CommunityToolkit.WinUI.Controls;
-using Microsoft.UI.Xaml.Controls;
using Snap.Hutao.Control.Collection.AdvancedCollectionView;
+using Snap.Hutao.Control.SuggestBox;
using Snap.Hutao.Factory.ContentDialog;
using Snap.Hutao.Model.Calculable;
using Snap.Hutao.Model.Entity.Primitive;
@@ -34,7 +33,7 @@ namespace Snap.Hutao.ViewModel.Wiki;
///
[ConstructorGenerated]
[Injection(InjectAs.Scoped)]
-internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel, IWikiViewModelInitialization
+internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel
{
private readonly IContentDialogFactory contentDialogFactory;
private readonly CalculateClient calculateClient;
@@ -87,15 +86,6 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel, IWiki
public FrozenSet AvailableQueries { get => availableQueries; }
- public void Initialize(ITokenizingTextBoxAccessor accessor)
- {
- accessor.TokenizingTextBox.TextChanged += OnFilterSuggestionRequested;
- accessor.TokenizingTextBox.QuerySubmitted += OnQuerySubmitted;
- accessor.TokenizingTextBox.TokenItemAdding += OnTokenItemAdding;
- accessor.TokenizingTextBox.TokenItemAdded += OnTokenItemModified;
- accessor.TokenizingTextBox.TokenItemRemoved += OnTokenItemModified;
- }
-
///
protected override async Task OpenUIAsync()
{
@@ -216,60 +206,7 @@ internal sealed partial class WikiWeaponViewModel : Abstraction.ViewModel, IWiki
BaseValueInfo = new(weapon.MaxLevel, propertyCurveValues, levelWeaponCurveMap, weaponPromoteMap);
}
- private void OnFilterSuggestionRequested(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
- {
- if (Weapons is null)
- {
- return;
- }
-
- if (string.IsNullOrWhiteSpace(FilterToken))
- {
- return;
- }
-
- if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
- {
- sender.ItemsSource = availableQueries.Where(q => q.Contains(FilterToken, StringComparison.OrdinalIgnoreCase));
- }
- }
-
- private void OnQuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
- {
- if (args.ChosenSuggestion is not null)
- {
- return;
- }
-
- ApplyFilter();
- }
-
- private void OnTokenItemAdding(TokenizingTextBox sender, TokenItemAddingEventArgs args)
- {
- if (string.IsNullOrWhiteSpace(args.TokenText))
- {
- return;
- }
-
- if (Weapons is null)
- {
- return;
- }
-
- if (Weapons.SourceCollection.SingleOrDefault(w => w.Name == args.TokenText) is { } weapon)
- {
- args.Item = new SearchToken(weapon);
- return;
- }
-
- args.Item = new SearchToken(args.TokenText);
- }
-
- private void OnTokenItemModified(TokenizingTextBox sender, object args)
- {
- ApplyFilter();
- }
-
+ [Command("FilterCommand")]
private void ApplyFilter()
{
if (Weapons is null)