Files
1989-06-04 00:00:05 +09:00

377 lines
18 KiB
VB.net
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Public Class MyPageRight
Inherits AdornerDecorator
Public PageUuid As Integer = GetUuid()
'当前状态
Public Enum PageStates
Empty '默认状态,页面全空
LoaderWait '加载环初始等待
LoaderEnter '加载环进入动画
LoaderStayForce '加载环正常显示(强制等待)
LoaderStay '加载环正常显示
LoaderExit '加载环退出动画
ContentEnter '内容进入动画
ContentStay '内容正常显示
ReloadExit '刷新导致的全部退出动画
PageExit '切换页面导致的全部退出动画
End Enum
Private _PageState As PageStates = PageStates.Empty
Public Property PageState As PageStates
Get
Return _PageState
End Get
Set(value As PageStates)
If _PageState = value Then Exit Property
_PageState = value
If ModeDebug Then Log("[UI] 页面状态切换为 " & GetStringFromEnum(value))
End Set
End Property
#Region "加载器"
Private PageLoader As LoaderBase
Private PageLoaderInputInvoke
Private PageLoaderUi As MyLoading
Private PanLoader As FrameworkElement
Private PanContent As FrameworkElement
Private PanAlways As FrameworkElement
Private PageLoaderAutoRun As Boolean
'初始化
''' <summary>
''' 表明页面存在需要在后台执行的加载器。
''' </summary>
''' <param name="LoaderUi">MyLoading 控件。</param>
''' <param name="PanLoader">MyLoading 控件对应的卡片。</param>
''' <param name="PanContent">加载结束后出现的内容容器。</param>
''' <param name="PanAlways">无论是否在加载总是要显示的容器。可以为 Nothing。</param>
''' <param name="RealLoader">在工作线程执行的加载器。</param>
''' <param name="FinishedInvoke">当加载器执行完成,在 UI 线程触发的 UI 初始化事件。</param>
Public Sub PageLoaderInit(LoaderUi As MyLoading, PanLoader As FrameworkElement, PanContent As FrameworkElement, PanAlways As FrameworkElement,
RealLoader As LoaderBase, FinishedInvoke As Action(Of LoaderBase), Optional InputInvoke As Func(Of Object) = Nothing,
Optional AutoRun As Boolean = True)
'初始化参数
Me.PanLoader = PanLoader
Me.PanContent = PanContent
Me.PanAlways = PanAlways
Me.PageLoader = RealLoader
Me.PageLoaderUi = LoaderUi
Me.PageLoaderInputInvoke = InputInvoke
Me.PageLoaderAutoRun = AutoRun
'添加结束 Invoke
AddHandler RealLoader.PreviewFinish,
Sub()
Do While PageState = MyPageRight.PageStates.PageExit OrElse PageState = MyPageRight.PageStates.ReloadExit
Thread.Sleep(10) '不在退出动画时执行 UI 线程操作,避免退出动画被重置
Loop
RunInUiWait(Sub() FinishedInvoke(RealLoader))
Thread.Sleep(20) '由于大量初始化控件会导致掉帧,延迟触发 State 改变事件
End Sub
AddHandler RealLoader.OnStateChangedUi, Sub(Loader As LoaderBase, NewState As LoadState, OldState As LoadState) RunInUi(Sub() PageLoaderState(Loader, NewState, OldState))
'隐藏 UI
PanLoader.Visibility = Visibility.Collapsed
PanContent.Visibility = Visibility.Collapsed
If PanAlways IsNot Nothing Then PanAlways.Visibility = Visibility.Collapsed
'初次运行加载器
If PageLoaderAutoRun Then
If PageLoader.GetType.Name.StartsWith("LoaderTask") Then
PageLoader.Start(CType(PageLoader, Object).StartGetInput(Nothing, PageLoaderInputInvoke))
Else
Dim Input = Nothing
If PageLoaderInputInvoke IsNot Nothing Then Input = PageLoaderInputInvoke()
PageLoader.Start(Input)
End If
End If
If PageLoader.State = LoadState.Finished Then
RunInUiWait(Sub() FinishedInvoke(RealLoader)) '加载器已提前完成,直接触发事件
End If
'设置加载环
PageLoaderUi.State = RealLoader
AddHandler PageLoaderUi.Click, Sub() If RealLoader.State = LoadState.Failed Then PageLoaderRestart() '点击重试事件
End Sub
'重试
Public Sub PageLoaderRestart(Optional Input As Object = Nothing, Optional IsForceRestart As Boolean = True) '由外部调用的重试
If Not PageLoaderAutoRun Then Exit Sub
If PageLoader.GetType.Name.StartsWith("LoaderTask") Then
PageLoader.Start(CType(PageLoader, Object).StartGetInput(Input, PageLoaderInputInvoke), IsForceRestart:=IsForceRestart)
Else
If Input Is Nothing AndAlso PageLoaderInputInvoke IsNot Nothing Then Input = PageLoaderInputInvoke()
PageLoader.Start(Input, IsForceRestart:=IsForceRestart)
End If
End Sub
#End Region
#Region "事件"
'外部触发的事件
''' <summary>
''' 需要切换到当前页面,并且原本的 Loaded 事件已执行完成。
''' 需要根据加载器状态,从 Empty 切换到 ContentEnter、LoaderWait、LoaderEnter。
''' </summary>
Public Sub PageOnEnter()
If ModeDebug Then Log("[UI] 已触发 PageOnEnter")
RaiseEvent OnPageEnter()
Select Case PageState
Case PageStates.Empty
If PageLoader Is Nothing OrElse PageLoader.State = LoadState.Finished Then
PageState = PageStates.ContentEnter
TriggerEnterAnimation(PanAlways, If(PanContent, Child))
ElseIf PageLoader.State = LoadState.Loading Then
PageState = PageStates.LoaderWait
AniStart(AaCode(AddressOf PageOnLoaderWaitFinished, 200), "PageRight PageChange " & PageUuid)
ElseIf PageLoader.State = LoadState.Failed Then
PageState = PageStates.LoaderEnter
TriggerEnterAnimation(PanAlways, PanLoader)
ElseIf PageLoader.State = LoadState.Waiting Then
'由于 “页面加载器 Minecraft Version List 不应在 PageOnEnter 事件出现 Waiting 状态” 的神秘反馈添加的临时修复
PageState = PageStates.LoaderWait
AniStart(AaCode(AddressOf PageOnLoaderWaitFinished, 200), "PageRight PageChange " & PageUuid)
Log("[Anim] 警告:页面加载器 " & PageLoader.Name & " 不应在 PageOnEnter 事件出现 " & GetStringFromEnum(PageLoader.State) & " 状态", LogLevel.Debug)
Else
Throw New Exception("页面加载器 " & PageLoader.Name & " 不应在 PageOnEnter 事件出现 " & GetStringFromEnum(PageLoader.State) & " 状态。")
End If
Case Else
Throw New Exception("在状态为 " & GetStringFromEnum(PageState) & " 时触发了 PageOnEnter 事件。")
End Select
End Sub
Public Event OnPageEnter()
''' <summary>
''' 需要切换到其他页面。
''' 需要立即切换至 PageExit 或 Empty。
''' </summary>
Public Sub PageOnExit()
If ModeDebug Then Log("[UI] 已触发 PageOnExit")
Select Case PageState
Case PageStates.ContentEnter, PageStates.ContentStay
PageState = PageStates.PageExit
TriggerExitAnimation(PanAlways, If(PanContent, Child))
Case PageStates.LoaderEnter, PageStates.LoaderStayForce, PageStates.LoaderStay
PageState = PageStates.PageExit
TriggerExitAnimation(PanAlways, PanLoader)
Case PageStates.LoaderWait
PageState = PageStates.PageExit
TriggerExitAnimation(PanAlways)
Case PageStates.LoaderExit, PageStates.ReloadExit
PageState = PageStates.PageExit
If PanAlways IsNot Nothing Then TriggerExitAnimation(PanAlways, If(PanContent, Child))
Case PageStates.PageExit, PageStates.Empty
End Select
End Sub
''' <summary>
''' 即将切换到其他页面,需要强制完成页面状态清理。
''' 需要立即切换至 Empty。
''' </summary>
Public Sub PageOnForceExit()
If PageState = PageStates.Empty Then Exit Sub
If ModeDebug Then Log("[UI] 已触发 PageOnForceExit")
PageState = PageStates.Empty
AniStop("PageRight PageChange " & PageUuid)
'由于动画会被强制中止,所以需要手动进行隐藏
If PageLoader Is Nothing Then
Child.Visibility = Visibility.Collapsed
Else
PanContent.Visibility = Visibility.Collapsed
PanLoader.Visibility = Visibility.Collapsed
If PanAlways IsNot Nothing Then PanAlways.Visibility = Visibility.Collapsed
End If
End Sub
'内部触发的事件
''' <summary>
''' 逐个进入动画已执行完成。
''' 需要根据目前状态,从 ContentEnter 切换到 ContentStay或从 LoaderEnter 切换到 LoaderStayForce。
''' </summary>
Private Sub PageOnEnterAnimationFinished()
If ModeDebug Then Log("[UI] 已触发 PageOnEnterAnimationFinished")
Select Case PageState
Case PageStates.ContentEnter
PageState = PageStates.ContentStay
Case PageStates.LoaderEnter
PageState = PageStates.LoaderStayForce
AniStart(AaCode(AddressOf PageOnLoaderStayFinished, 400), "PageRight PageChange " & PageUuid)
Case Else
Throw New Exception("在状态为 " & GetStringFromEnum(PageState) & " 时触发了 PageOnEnterAnimationFinished 事件。")
End Select
End Sub
''' <summary>
''' 逐个退出动画已执行完成。
''' 需要根据目前状态,从 AllExit 切换到 Empty或从 LoaderExit 切换到 ContentEnter或从 ContentExit 重新触发 PageOnEnter。
''' </summary>
Private Sub PageOnExitAnimationFinished()
If ModeDebug Then Log("[UI] 已触发 PageOnExitAnimationFinished")
Select Case PageState
Case PageStates.PageExit
PageState = PageStates.Empty
Case PageStates.ReloadExit
PageState = PageStates.Empty
PageOnEnter()
Case PageStates.LoaderExit
PageState = PageStates.ContentEnter
TriggerEnterAnimation(PanContent)
Case Else
Throw New Exception("在状态为 " & GetStringFromEnum(PageState) & " 时触发了 PageOnExitAnimationFinished 事件。")
End Select
End Sub
''' <summary>
''' 加载环进入等待已结束。
''' 需要从 LoaderWait 切换到 LoaderEnter。
''' </summary>
Private Sub PageOnLoaderWaitFinished()
If ModeDebug Then Log("[UI] 已触发 PageOnLoaderWaitFinished")
Select Case PageState
Case PageStates.LoaderWait
PageState = PageStates.LoaderEnter
If PanAlways IsNot Nothing AndAlso PanAlways.Visibility = Visibility.Collapsed Then
TriggerEnterAnimation(PanAlways, PanLoader)
Else
TriggerEnterAnimation(PanLoader)
End If
Case Else
Throw New Exception("在状态为 " & GetStringFromEnum(PageState) & " 时触发了 PageOnLoaderWaitFinished 事件。")
End Select
End Sub
''' <summary>
''' 加载环展示等待已结束。
''' 需要从 LoaderStayForce 切换到 LoaderStay 或 LoaderExit。
''' </summary>
Private Sub PageOnLoaderStayFinished()
If ModeDebug Then Log("[UI] 已触发 PageOnLoaderStayFinished")
Select Case PageState
Case PageStates.LoaderStayForce
If PageLoader.State = LoadState.Finished Then
PageState = PageStates.LoaderExit
TriggerExitAnimation(PanLoader)
Else
PageState = PageStates.LoaderStay
End If
Case Else
Throw New Exception("在状态为 " & GetStringFromEnum(PageState) & " 时触发了 PageOnLoaderWaitFinished 事件。")
End Select
End Sub
''' <summary>
''' 全局加载状态已改变。
''' </summary>
Private Sub PageLoaderState(sender As Object, NewState As LoadState, OldState As LoadState)
Select Case NewState
Case LoadState.Failed, LoadState.Loading
If Not OldState = LoadState.Finished Then Exit Sub
If ModeDebug Then Log("[UI] 已触发 PageLoaderState (Refresh)")
'重新开始运行
'需要从部分状态切换到 ReloadExit
Select Case PageState
Case PageStates.ContentEnter, PageStates.ContentStay
PageState = PageStates.ReloadExit
TriggerExitAnimation(PanContent)
Case PageStates.LoaderExit, PageStates.ReloadExit
PageState = PageStates.ReloadExit
End Select
Case LoadState.Finished
If Not OldState = LoadState.Loading Then Exit Sub
If ModeDebug Then Log("[UI] 已触发 PageLoaderState (Stop)")
'运行结束
'需要从 LoaderWait 切换到 ContentEnter或从 LoaderStay 切换到 LoaderExit
Select Case PageState
Case PageStates.LoaderWait
PageState = PageStates.ContentEnter
If PanAlways IsNot Nothing AndAlso PanAlways.Visibility = Visibility.Collapsed Then
TriggerEnterAnimation(PanAlways, PanContent)
Else
TriggerEnterAnimation(PanContent)
End If
Case PageStates.LoaderStay
PageState = PageStates.LoaderExit
TriggerExitAnimation(PanLoader)
End Select
End Select
End Sub
#End Region
#Region "动画"
'逐个进入动画
Public Sub TriggerEnterAnimation(ParamArray Elements As FrameworkElement())
'去除 Nothing 项
Dim RealElements As New List(Of FrameworkElement)
For Each Element In Elements
If Element IsNot Nothing Then RealElements.Add(Element)
Next
For Each Element In RealElements
Element.Visibility = Visibility.Visible '页面均处于默认的隐藏状态
Next
Dim AniList As New List(Of AniData)
Dim Delay As Integer = 0
For Each Element In RealElements
For Each Control As FrameworkElement In GetAllAnimControls(Element, True)
'还原被隐藏的卡片的消失动画
Control.IsHitTestVisible = True
If Control.RenderTransform IsNot Nothing AndAlso TypeOf Control.RenderTransform Is TranslateTransform Then Control.RenderTransform = Nothing
Next
For Each Control As FrameworkElement In GetAllAnimControls(Element)
Control.Opacity = 0
Control.RenderTransform = New TranslateTransform(0, -16)
AniList.Add(AaOpacity(Control, 1, 150, Delay, New AniEaseOutFluent(AniEasePower.Weak)))
AniList.Add(AaTranslateY(Control, 5, 250, Delay, New AniEaseOutFluent))
AniList.Add(AaTranslateY(Control, 11, 350, Delay, New AniEaseOutBack))
Delay += 40
Next
Next
AniList.Add(AaCode(Sub() PageOnEnterAnimationFinished(),, True))
AniStart(AniList, "PageRight PageChange " & PageUuid)
End Sub
'逐个退出动画
Public Sub TriggerExitAnimation(ParamArray Elements As FrameworkElement())
'去除 Nothing 项
Dim RealElements As New List(Of FrameworkElement)
For Each Element In Elements
If Element IsNot Nothing Then RealElements.Add(Element)
Next
Dim AniList As New List(Of AniData)
Dim Delay As Integer = 0
For Each Element In RealElements
For Each Control As FrameworkElement In GetAllAnimControls(Element)
Control.IsHitTestVisible = False
AniList.Add(AaOpacity(Control, -1, 90, Delay))
AniList.Add(AaTranslateY(Control, -6, 90, Delay))
Delay += 20
Next
Next
AniList.Add(AaCode(Sub()
For Each Element In RealElements
Element.Visibility = Visibility.Collapsed
Next
PageOnExitAnimationFinished()
End Sub,, True))
AniStart(AniList, "PageRight PageChange " & PageUuid)
End Sub
'遍历获取所有需要生成动画的控件
Friend Function GetAllAnimControls(Element As FrameworkElement, Optional IgnoreInvisibility As Boolean = False) As List(Of FrameworkElement)
Dim AllControls As New List(Of FrameworkElement)
GetAllAnimControls(Element, AllControls, IgnoreInvisibility)
Return AllControls
End Function
Private Sub GetAllAnimControls(Element As FrameworkElement, ByRef AllControls As List(Of FrameworkElement), IgnoreInvisibility As Boolean)
If Not IgnoreInvisibility AndAlso Element.Visibility = Visibility.Collapsed Then Exit Sub
If TypeOf Element Is MyCard OrElse TypeOf Element Is MyHint Then
AllControls.Add(Element)
ElseIf TypeOf Element Is ContentControl Then
GetAllAnimControls(CType(Element, ContentControl).Content, AllControls, IgnoreInvisibility)
ElseIf TypeOf Element Is Panel Then
For Each Element2 As FrameworkElement In CType(Element, Panel).Children
GetAllAnimControls(Element2, AllControls, IgnoreInvisibility)
Next
End If
End Sub
#End Region
End Class