Imports PCL.MyLoading
Public Class MyLoading
Public Event IsErrorChanged(sender As Object, isError As Boolean)
Public Event StateChanged(sender As Object, newState As MyLoadingState, oldState As MyLoadingState)
Public Event Click(sender As Object, e As MouseButtonEventArgs)
Public Property AutoRun As Boolean = True
Private Uuid As Integer = GetUuid()
#Region "颜色"
Public Property Foreground As SolidColorBrush
Get
Return GetValue(ForegroundProperty)
End Get
Set(value As SolidColorBrush)
SetValue(ForegroundProperty, value)
End Set
End Property
Public Shared ReadOnly ForegroundProperty As DependencyProperty = DependencyProperty.Register("Foreground", GetType(SolidColorBrush), GetType(MyLoading))
Public Sub New()
InitializeComponent()
SetResourceReference(ForegroundProperty, "ColorBrush3")
End Sub
#End Region
#Region "文本"
Private Property _ShowProgress As Boolean = False
Public Property ShowProgress As Boolean
Get
Return _ShowProgress
End Get
Set(value As Boolean)
If _ShowProgress = value Then Exit Property
_ShowProgress = value
RefreshText()
End Set
End Property
Private _Text As String = "加载中"
Public Property Text As String
Get
Return _Text
End Get
Set(ByVal value As String)
_Text = value
RefreshText()
End Set
End Property
Private _TextError As String = "加载失败"
Public Property TextError As String
Get
Return _TextError
End Get
Set(ByVal value As String)
_TextError = value
RefreshText()
End Set
End Property
'''
''' 是否在使用 Loader 时使用 Loader 的错误输出来替换默认的错误文本显示。
'''
Public Property TextErrorInherit As Boolean = True
Private Sub RefreshText() Handles Me.IsErrorChanged, Me.Loaded
If InnerState = MyLoadingState.Error Then
If TextErrorInherit AndAlso State.IsLoader Then
Dim Ex As Exception = CType(State, Object).Error
If Ex Is Nothing Then
LabText.Text = "未知错误"
Else
Do While Ex.InnerException IsNot Nothing
Ex = Ex.InnerException
Loop
LabText.Text = StrTrim(Ex.Message)
If LabText.Text.Contains("远程主机强迫关闭了一个现有的连接") OrElse LabText.Text.Contains("操作已超时") OrElse LabText.Text.Contains("操作超时") OrElse LabText.Text.Contains("服务器超时") OrElse LabText.Text.Contains("连接超时") Then LabText.Text = "网络环境不佳,请重试或尝试使用 VPN"
End If
Else
LabText.Text = TextError
End If
Else
If ShowProgress AndAlso State.IsLoader Then
LabText.Text = Text & " - " & Math.Floor(CType(State, Object).Progress * 100) & "%"
Else
LabText.Text = Text
End If
End If
End Sub
#End Region
#Region "状态改变"
'状态枚举
Public Enum MyLoadingState
Unloaded = -1
Run = 0
[Stop] = 1
[Error] = 2
End Enum
'用于外部改变的公开状态
Private WithEvents _State As ILoadingTrigger
Public Property State As ILoadingTrigger
Get
InitState()
Return _State
End Get
Set(value As ILoadingTrigger)
_State = value
RefreshState()
End Set
End Property
Private Sub InitState() Handles Me.Loaded
If _State Is Nothing Then
_State = New MyLoadingStateSimulator
If AutoRun Then _State.LoadingState = MyLoadingState.Run
End If
End Sub
Private Sub RefreshState() Handles _State.LoadingStateChanged, Me.Loaded, Me.Unloaded
If _State.LoadingState = MyLoadingState.Run AndAlso Not IsLoaded Then InnerState = MyLoadingState.Stop
InnerState = _State.LoadingState
OuterState = _State.LoadingState
AniLoop()
End Sub
'用于引发外部事件的状态
Private Property _OuterState As MyLoadingState = MyLoadingState.Unloaded
Private Property OuterState As MyLoadingState
Get
Return _OuterState
End Get
Set(value As MyLoadingState)
If _OuterState = value Then Exit Property
Dim OldValue = _OuterState
_OuterState = value
'引发事件
RaiseEvent StateChanged(Me, value, OldValue)
If (OldValue = MyLoadingState.Error) <> (value = MyLoadingState.Error) Then RaiseEvent IsErrorChanged(Me, value = MyLoadingState.Error)
End Set
End Property
'用于引发内部动画事件的状态
Private Property _InnerState As MyLoadingState = MyLoadingState.Unloaded
Private Property InnerState As MyLoadingState
Get
Return _InnerState
End Get
Set(value As MyLoadingState)
If _InnerState = value Then Exit Property
Dim OldValue = _InnerState
_InnerState = value
'引发事件
AniLoop()
If (OldValue = MyLoadingState.Error) <> (value = MyLoadingState.Error) Then ErrorAnimation(Me, value = MyLoadingState.Error)
End Set
End Property
#End Region
#Region "动画"
'''
''' 是否需要动画。
'''
Public Property HasAnimation As Boolean = True
'''
''' 主动画循环是否正在运行中。
'''
Private IsLooping As Boolean = False
Private Sub AniLoop()
'这坨循环代码也是老屎坑了,救救.jpg
If Not HasAnimation OrElse IsLooping OrElse Not InnerState = MyLoadingState.Run OrElse AniSpeed > 10 OrElse Not IsLoaded Then Exit Sub
IsLooping = True
ErrorAnimationWaiting = True
If ShowProgress Then
RefreshText()
AniStart({
AaRotateTransform(PathPickaxe, -20 - CType(PathPickaxe.RenderTransform, RotateTransform).Angle, 350, 250, New AniEaseInBack(AniEasePower.Weak)),
AaCode(AddressOf RefreshText, 100), '这是为了省下一个 Timer 线程……
AaCode(AddressOf RefreshText, 200),
AaCode(AddressOf RefreshText, 300),
AaCode(AddressOf RefreshText, 400),
AaCode(AddressOf RefreshText, 500),
AaRotateTransform(PathPickaxe, 50, 900,, New AniEaseOutFluent, True),
AaRotateTransform(PathPickaxe, 25, 900,, New AniEaseOutElastic(AniEasePower.Weak)),
AaCode(Sub()
PathLeft.Opacity = 1
PathLeft.Margin = New Thickness(7, 41, 0, 0)
PathRight.Opacity = 1
PathRight.Margin = New Thickness(14, 41, 0, 0)
ErrorAnimationWaiting = False
RefreshText()
End Sub),
AaCode(AddressOf RefreshText, 100),
AaCode(AddressOf RefreshText, 200),
AaCode(AddressOf RefreshText, 300),
AaCode(AddressOf RefreshText, 400),
AaCode(AddressOf RefreshText, 500),
AaCode(AddressOf RefreshText, 600),
AaCode(AddressOf RefreshText, 700),
AaCode(AddressOf RefreshText, 800),
AaOpacity(PathLeft, -1, 100, 70),
AaX(PathLeft, -5, 180, 20, New AniEaseOutFluent),
AaY(PathLeft, -6, 180, 20, New AniEaseOutFluent),
AaOpacity(PathRight, -1, 100, 70),
AaX(PathRight, 5, 180, 20, New AniEaseOutFluent),
AaY(PathRight, -6, 180, 20, New AniEaseOutFluent),
AaCode(Sub()
IsLooping = False
AniLoop()
End Sub,, True)
}, "MyLoader Loop " & Uuid & "/" & GetUuid())
Else
AniStart({
AaRotateTransform(PathPickaxe, -20 - CType(PathPickaxe.RenderTransform, RotateTransform).Angle, 350, 250, New AniEaseInBack(AniEasePower.Weak)),
AaRotateTransform(PathPickaxe, 50, 900,, New AniEaseOutFluent, True),
AaRotateTransform(PathPickaxe, 25, 900,, New AniEaseOutElastic(AniEasePower.Weak)),
AaCode(Sub()
PathLeft.Opacity = 1
PathLeft.Margin = New Thickness(7, 41, 0, 0)
PathRight.Opacity = 1
PathRight.Margin = New Thickness(14, 41, 0, 0)
ErrorAnimationWaiting = False
End Sub),
AaOpacity(PathLeft, -1, 100, 50),
AaX(PathLeft, -5, 180,, New AniEaseOutFluent),
AaY(PathLeft, -6, 180,, New AniEaseOutFluent),
AaOpacity(PathRight, -1, 100, 50),
AaX(PathRight, 5, 180,, New AniEaseOutFluent),
AaY(PathRight, -6, 180,, New AniEaseOutFluent),
AaCode(Sub()
IsLooping = False
AniLoop()
End Sub,, True)
}, "MyLoader Loop " & Uuid & "/" & GetUuid())
End If
End Sub
'''
''' 镐子是否还没挥下去,要求错误动画等待。
'''
Private ErrorAnimationWaiting As Boolean = False
Private Sub ErrorAnimation(sender As Object, isError As Boolean)
If isError Then
'非错误变为错误
Dim Wait As Integer = If(ErrorAnimationWaiting, 400, 0)
AniStart({
AaColor(PanBack, ForegroundProperty, "ColorBrushRedLight", 300),
AaOpacity(PathError, 1 - PathError.Opacity, 100, 300 + Wait),
AaScaleTransform(PathError, 1 - CType(PathError.RenderTransform, ScaleTransform).ScaleX, 400, 300 + Wait, New AniEaseOutBack)
}, "MyLoader Error " & Uuid)
Else
'错误变为非错误
AniStart({
AaOpacity(PathError, -PathError.Opacity, 100),
AaScaleTransform(PathError, 0.5 - CType(PathError.RenderTransform, ScaleTransform).ScaleX, 200),
AaColor(PanBack, ForegroundProperty, "ColorBrush3", 300)
}, "MyLoader Error " & Uuid)
End If
End Sub
#End Region
#Region "点击事件"
Private Sub Button_MouseUp(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonUp
RaiseEvent Click(sender, e)
End Sub
Private IsMouseDown As Boolean = False
Private Sub Button_MouseDown(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonDown
'鼠标点击判定(务必放在点击事件之后,以使得 Button_MouseUp 先于 Button_MouseLeave 执行)
IsMouseDown = True
End Sub
Private Sub Button_MouseLeave(sender As Object, e As Object) Handles Me.MouseLeave, Me.MouseLeftButtonUp
IsMouseDown = False
End Sub
#End Region
End Class
Public Interface ILoadingTrigger
ReadOnly Property IsLoader As Boolean
Property LoadingState As MyLoadingState
Event LoadingStateChanged(NewState As MyLoadingState, OldState As MyLoadingState)
End Interface
Public Class MyLoadingStateSimulator
Implements ILoadingTrigger
Private Property _LoadingState As MyLoadingState = MyLoadingState.Unloaded
Public Property LoadingState As MyLoadingState Implements ILoadingTrigger.LoadingState
Get
Return _LoadingState
End Get
Set(value As MyLoadingState)
If _LoadingState = value Then Exit Property
Dim OldState = _LoadingState
_LoadingState = value
RaiseEvent LoadingStateChanged(value, OldState)
End Set
End Property
Public ReadOnly Property IsLoader As Boolean = False Implements ILoadingTrigger.IsLoader
Public Event LoadingStateChanged(NewState As MyLoadingState, OldState As MyLoadingState) Implements ILoadingTrigger.LoadingStateChanged
End Class