| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- Imports System.Windows.Markup
- <ContentProperty("SearchTags")>
- Public Class PageComp
- #Region "属性"
- ''' <summary>
- ''' 用于 XAML 快速设置的 Tag 下拉框列表。
- ''' </summary>
- Public ReadOnly Property SearchTags As ItemCollection
- Get
- Return ComboSearchTag.Items
- End Get
- End Property
- ''' <summary>
- ''' 英文前后不含空格的可读资源类型名,例如 "Mod"、"整合包"。
- ''' </summary>
- Public Property TypeName As String
- Get
- Return _TypeName
- End Get
- Set(Value As String)
- If _TypeName = Value Then Return
- _TypeName = Value
- Loader.Name = $"社区资源获取:{Value}"
- End Set
- End Property
- Private _TypeName As String = ""
- ''' <summary>
- ''' 英文前后含一个空格的可读资源类型名,例如 " Mod "、"整合包"。
- ''' </summary>
- Public Property TypeNameSpaced As String
- Get
- Return _TypeNameSpaced
- End Get
- Set(Value As String)
- If _TypeNameSpaced = Value Then Return
- _TypeNameSpaced = Value
- PanAlways.Title = $"搜索{Value}"
- Load.Text = $"正在获取{Value}列表"
- End Set
- End Property
- Private _TypeNameSpaced As String = ""
- ''' <summary>
- ''' 该页面对应的资源类型。
- ''' </summary>
- Public Property PageType As CompType
- Get
- Return _Type
- End Get
- Set(Value As CompType)
- If _Type = Value Then Return
- _Type = Value
- BtnSearchInstallModPack.Visibility = If(Value = CompType.ModPack, Visibility.Visible, Visibility.Collapsed)
- End Set
- End Property
- Private _Type As CompType = -1
- #End Region
- #Region "加载"
- ''' <summary>
- ''' 在切换到页面时,应自动将筛选项设置为与该目标 MC 版本和加载器相同。
- ''' </summary>
- Public Shared TargetInstance As McInstance = Nothing
- ''' <summary>
- ''' 在切换到该页面时自动设置的搜索框内的内容。
- ''' </summary>
- Public Shared TargetName As String = Nothing
- '在点击 MyCompItem 时会获取 Loader 的输入,以使资源详情页面可以应用相同的筛选项
- Public Loader As New LoaderTask(Of CompProjectRequest, Integer)("社区资源获取:XXX", AddressOf CompProjectsGet, AddressOf LoaderInput) With {.ReloadTimeout = 60 * 1000}
- Private Sub PageCompControls_Inited(sender As Object, e As EventArgs) Handles Me.Loaded
- Static IsFirstLoaded As Boolean = False
- '不知道从 Initialized 改成 Loaded 会不会有问题,但用 Initialized 会导致初始的筛选器修改被覆盖回默认值
- If TargetInstance IsNot Nothing Then
- '设置目标
- ResetFilter() '重置筛选器
- TextSearchVersion.Text = TargetInstance.Version.VanillaName
- Dim GetTargetItemByName =
- Function(Name As String) As MyComboBoxItem
- For Each Item As MyComboBoxItem In ComboSearchLoader.Items
- If Item.Content = Name Then Return Item
- Next
- Return ComboSearchLoader.Items(0)
- End Function
- If TargetInstance.Version.HasForge Then
- ComboSearchLoader.SelectedItem = GetTargetItemByName("Forge")
- ElseIf TargetInstance.Version.HasFabric Then
- ComboSearchLoader.SelectedItem = GetTargetItemByName("Fabric")
- ElseIf TargetInstance.Version.HasNeoForge Then
- ComboSearchLoader.SelectedItem = GetTargetItemByName("NeoForge")
- End If
- TargetInstance = Nothing
- If TargetName IsNot Nothing Then
- TextSearchName.Text = TargetName
- TargetName = Nothing
- End If
- '如果已经完成请求,则重新开始
- If IsFirstLoaded Then StartNewSearch()
- ScrollToHome()
- End If
- '加载器初始化
- If IsFirstLoaded Then Return
- IsFirstLoaded = True
- CType(Parent, MyPageRight).PageLoaderInit(Load, PanLoad, PanContent, PanAlways, Loader, AddressOf Load_OnFinish, AddressOf LoaderInput)
- '将最高 Drop 加入筛选
- If AllDrops?.Any AndAlso AllDrops.First > 250 Then
- Dim HighestVersion As String = McVersion.DropToVersion(AllDrops.First)
- If CType(TextSearchVersion.Items(1), MyComboBoxItem).Content.ToString <> HighestVersion Then '0 是全部
- TextSearchVersion.Items.Insert(1, New MyComboBoxItem With {.Content = HighestVersion})
- End If
- End If
- End Sub
- Private Function LoaderInput() As CompProjectRequest
- Dim Request As New CompProjectRequest(PageType, Storage, (Page + 1) * PAGE_SIZE)
- Dim GameVersion As String = If(TextSearchVersion.Text = "全部 (也可自行输入)", Nothing,
- If(TextSearchVersion.Text.Contains(".") OrElse TextSearchVersion.Text.Contains("w"), TextSearchVersion.Text, Nothing))
- With Request
- .SearchText = TextSearchName.Text
- .GameVersion = GameVersion
- .Tag = ComboSearchTag.SelectedItem.Tag
- .ModLoader = If(PageType = CompType.Mod, Val(ComboSearchLoader.SelectedItem.Tag), CompModLoaderType.Any)
- .Source = CType(Val(ComboSearchSource.SelectedItem.Tag), CompSourceType)
- End With
- Return Request
- End Function
- #End Region
- Public Storage As New CompProjectStorage
- ''' <summary>
- ''' 每页展示的结果数量。
- ''' </summary>
- Public Const PAGE_SIZE = 40
- Public Page As Integer = 0
- '结果 UI 化
- Private Sub Load_OnFinish()
- Try
- Log($"[Comp] 开始可视化{TypeNameSpaced}列表,已储藏 {Storage.Results.Count} 个结果,当前在第 {Page + 1} 页")
- '列表项
- PanProjects.Children.Clear()
- Dim Index As Integer = Math.Min(Page * PAGE_SIZE, Storage.Results.Count - 1)
- For Each Result In Storage.Results.GetRange(Index, Math.Min(Storage.Results.Count - Index, PAGE_SIZE))
- PanProjects.Children.Add(Result.ToCompItem(
- ShowMcVersionDesc:=Loader.Input.GameVersion Is Nothing,
- ShowLoaderDesc:=Loader.Input.ModLoader = CompModLoaderType.Any AndAlso (PageType = CompType.Mod OrElse PageType = CompType.ModPack)))
- Next
- '页码
- CardPages.Visibility = If(Storage.Results.Count > 40 OrElse
- Storage.CurseForgeOffset < Storage.CurseForgeTotal OrElse Storage.ModrinthOffset < Storage.ModrinthTotal,
- Visibility.Visible, Visibility.Collapsed)
- LabPage.Text = Page + 1
- BtnPageFirst.IsEnabled = Page > 1
- BtnPageFirst.Opacity = If(Page > 1, 1, 0.2)
- BtnPageLeft.IsEnabled = Page > 0
- BtnPageLeft.Opacity = If(Page > 0, 1, 0.2)
- Dim IsRightEnabled As Boolean = '由于 WPF 的未知 bug,读取到的 IsEnabled 可能是错误的值(#3319)
- Storage.Results.Count > PAGE_SIZE * (Page + 1) OrElse
- Storage.CurseForgeOffset < Storage.CurseForgeTotal OrElse Storage.ModrinthOffset < Storage.ModrinthTotal
- BtnPageRight.IsEnabled = IsRightEnabled
- BtnPageRight.Opacity = If(IsRightEnabled, 1, 0.2)
- '错误信息
- If Storage.ErrorMessage Is Nothing Then
- HintError.Visibility = Visibility.Collapsed
- Else
- HintError.Visibility = Visibility.Visible
- HintError.Text = Storage.ErrorMessage
- End If
- '强制返回顶部
- ScrollToTop()
- Catch ex As Exception
- Log(ex, $"可视化{TypeNameSpaced}列表出错", LogLevel.Feedback)
- End Try
- End Sub
- '自动重试
- Private Sub Load_State(sender As Object, state As MyLoading.MyLoadingState, oldState As MyLoading.MyLoadingState) Handles Load.StateChanged
- Select Case Loader.State
- Case LoadState.Failed
- Dim ErrorMessage As String = ""
- If Loader.Error IsNot Nothing Then ErrorMessage = Loader.Error.Message
- If ErrorMessage.Contains("不是有效的 json 文件") Then
- Log($"[Download] 下载的{TypeNameSpaced}列表 json 文件损坏,已自动重试", LogLevel.Debug)
- CType(Parent, MyPageRight).PageLoaderRestart()
- End If
- End Select
- End Sub
- '切换页码
- Private Sub BtnPageFirst_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageFirst.Click
- ChangePage(0)
- End Sub
- Private Sub BtnPageLeft_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageLeft.Click
- ChangePage(Page - 1)
- End Sub
- Private Sub BtnPageRight_Click(sender As Object, e As RoutedEventArgs) Handles BtnPageRight.Click
- ChangePage(Page + 1)
- End Sub
- Private Sub ChangePage(NewPage As Integer)
- CardPages.IsEnabled = False
- Page = NewPage
- FrmMain.BackToTop()
- Log($"[Download] {TypeName}:切换到第 {Page + 1} 页")
- RunInThread(
- Sub()
- Thread.Sleep(100) '等待向上滚的动画结束
- RunInUi(Sub() CardPages.IsEnabled = True)
- Loader.Start()
- End Sub)
- End Sub
- #Region "搜索"
- '搜索按钮
- Private Sub StartNewSearch() Handles BtnSearchRun.Click
- Page = 0
- If Loader.ShouldStart(LoaderInput()) Then Storage = New CompProjectStorage '避免连续搜索两次使得 CompProjectStorage 引用丢失(#1311)
- Loader.Start()
- End Sub
- Private Sub EnterTrigger(sender As Object, e As KeyEventArgs) Handles TextSearchName.KeyDown, TextSearchVersion.KeyDown
- If e.Key = Key.Enter Then StartNewSearch()
- End Sub
- '重置按钮
- Private Sub ResetFilter() Handles BtnSearchReset.Click
- TextSearchName.Text = ""
- TextSearchVersion.Text = "全部 (也可自行输入)"
- TextSearchVersion.SelectedIndex = 0
- ComboSearchSource.SelectedIndex = 0
- ComboSearchTag.SelectedIndex = 0
- ComboSearchLoader.SelectedIndex = 0
- Loader.LastFinishedTime = 0 '要求强制重新开始
- End Sub
- '版本选择
- '#3067:当下拉菜单展开时,程序会被 WPF 挂起,因而无法更新 Grid 布局,所以必须延迟到下拉菜单收起后才能更新
- Private Sub TextSearchVersion_TextChanged() Handles TextSearchVersion.TextChanged
- If Not TextSearchVersion.IsDropDownOpen Then UpdateSearchLoaderVisibility()
- End Sub
- Private Sub UpdateSearchLoaderVisibility() Handles TextSearchVersion.DropDownClosed
- If PageType = CompType.Mod AndAlso (TextSearchVersion.Text.Contains(".") OrElse TextSearchVersion.Text.Contains("w")) Then
- ComboSearchLoader.Visibility = Visibility.Visible
- Grid.SetColumnSpan(TextSearchVersion, 1)
- Else
- ComboSearchLoader.Visibility = Visibility.Collapsed
- Grid.SetColumnSpan(TextSearchVersion, 2)
- ComboSearchLoader.SelectedIndex = 0
- End If
- End Sub
- #End Region
- '安装已有整合包按钮
- Private Sub BtnSearchInstallModPack_Click(sender As Object, e As EventArgs) Handles BtnSearchInstallModPack.Click
- ModpackInstall()
- End Sub
- End Class
|