377 lines
18 KiB
VB.net
377 lines
18 KiB
VB.net
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 OrElse PageLoader.State = LoadState.Aborted 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
|