MyLoading.xaml.vb 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. Imports PCL.MyLoading
  2. Public Class MyLoading
  3. Public Event IsErrorChanged(sender As Object, isError As Boolean)
  4. Public Event StateChanged(sender As Object, newState As MyLoadingState, oldState As MyLoadingState)
  5. Public Event Click(sender As Object, e As MouseButtonEventArgs)
  6. Public Property AutoRun As Boolean = True
  7. Private Uuid As Integer = GetUuid()
  8. #Region "颜色"
  9. Public Property Foreground As SolidColorBrush
  10. Get
  11. Return GetValue(ForegroundProperty)
  12. End Get
  13. Set(value As SolidColorBrush)
  14. SetValue(ForegroundProperty, value)
  15. End Set
  16. End Property
  17. Public Shared ReadOnly ForegroundProperty As DependencyProperty = DependencyProperty.Register("Foreground", GetType(SolidColorBrush), GetType(MyLoading))
  18. Public Sub New()
  19. InitializeComponent()
  20. SetResourceReference(ForegroundProperty, "ColorBrush3")
  21. End Sub
  22. #End Region
  23. #Region "文本"
  24. Private Property _ShowProgress As Boolean = False
  25. Public Property ShowProgress As Boolean
  26. Get
  27. Return _ShowProgress
  28. End Get
  29. Set(value As Boolean)
  30. If _ShowProgress = value Then Return
  31. _ShowProgress = value
  32. RefreshText()
  33. End Set
  34. End Property
  35. Private _Text As String = "加载中"
  36. Public Property Text As String
  37. Get
  38. Return _Text
  39. End Get
  40. Set(value As String)
  41. _Text = value
  42. RefreshText()
  43. End Set
  44. End Property
  45. Private _TextError As String = "加载失败"
  46. Public Property TextError As String
  47. Get
  48. Return _TextError
  49. End Get
  50. Set(value As String)
  51. _TextError = value
  52. RefreshText()
  53. End Set
  54. End Property
  55. ''' <summary>
  56. ''' 是否在使用 Loader 时使用 Loader 的错误输出来替换默认的错误文本显示。
  57. ''' </summary>
  58. Public Property TextErrorInherit As Boolean = True
  59. Private Sub RefreshText() Handles Me.IsErrorChanged, Me.Loaded, _State.ProgressChanged
  60. RunInUi(
  61. Sub()
  62. If InnerState = MyLoadingState.Error Then
  63. If TextErrorInherit AndAlso State.IsLoader Then
  64. Dim Ex As Exception = CType(State, Object).Error
  65. If Ex Is Nothing Then
  66. LabText.Text = "未知错误"
  67. Else
  68. Do While Ex.InnerException IsNot Nothing
  69. Ex = Ex.InnerException
  70. Loop
  71. LabText.Text = If(Ex.IsNetworkRelated(), "网络环境不佳,请稍后再试,或使用 VPN 改善网络环境", StrTrim(Ex.Message))
  72. End If
  73. Else
  74. LabText.Text = TextError
  75. End If
  76. Else
  77. If ShowProgress AndAlso State.IsLoader Then
  78. LabText.Text = Text & " - " & Math.Floor(CType(State, Object).Progress * 100) & "%"
  79. Else
  80. LabText.Text = Text
  81. End If
  82. End If
  83. End Sub)
  84. End Sub
  85. #End Region
  86. #Region "状态改变"
  87. '状态枚举
  88. Public Enum MyLoadingState
  89. Unloaded = -1
  90. Run = 0
  91. [Stop] = 1
  92. [Error] = 2
  93. End Enum
  94. '用于外部改变的公开状态
  95. Private WithEvents _State As ILoadingTrigger
  96. Public Property State As ILoadingTrigger
  97. Get
  98. InitState()
  99. Return _State
  100. End Get
  101. Set(value As ILoadingTrigger)
  102. _State = value
  103. RefreshState()
  104. End Set
  105. End Property
  106. Private Sub InitState() Handles Me.Loaded
  107. If _State Is Nothing Then
  108. _State = New MyLoadingStateSimulator
  109. If AutoRun Then _State.LoadingState = MyLoadingState.Run
  110. End If
  111. End Sub
  112. Private Sub RefreshState() Handles _State.LoadingStateChanged, Me.Loaded, Me.Unloaded
  113. If _State.LoadingState = MyLoadingState.Run AndAlso Not IsLoaded Then InnerState = MyLoadingState.Stop
  114. InnerState = _State.LoadingState
  115. OuterState = _State.LoadingState
  116. AniLoop()
  117. End Sub
  118. '用于引发外部事件的状态
  119. Private Property _OuterState As MyLoadingState = MyLoadingState.Unloaded
  120. Private Property OuterState As MyLoadingState
  121. Get
  122. Return _OuterState
  123. End Get
  124. Set(value As MyLoadingState)
  125. If _OuterState = value Then Return
  126. Dim OldValue = _OuterState
  127. _OuterState = value
  128. '引发事件
  129. RaiseEvent StateChanged(Me, value, OldValue)
  130. If (OldValue = MyLoadingState.Error) <> (value = MyLoadingState.Error) Then RaiseEvent IsErrorChanged(Me, value = MyLoadingState.Error)
  131. End Set
  132. End Property
  133. '用于引发内部动画事件的状态
  134. Private Property _InnerState As MyLoadingState = MyLoadingState.Unloaded
  135. Private Property InnerState As MyLoadingState
  136. Get
  137. Return _InnerState
  138. End Get
  139. Set(value As MyLoadingState)
  140. If _InnerState = value Then Return
  141. Dim OldValue = _InnerState
  142. _InnerState = value
  143. '引发事件
  144. AniLoop()
  145. If (OldValue = MyLoadingState.Error) <> (value = MyLoadingState.Error) Then ErrorAnimation(Me, value = MyLoadingState.Error)
  146. End Set
  147. End Property
  148. #End Region
  149. #Region "动画"
  150. ''' <summary>
  151. ''' 是否需要动画。
  152. ''' </summary>
  153. Public Property HasAnimation As Boolean = True
  154. ''' <summary>
  155. ''' 主动画循环是否正在运行中。
  156. ''' </summary>
  157. Private IsLooping As Boolean = False
  158. Private Sub AniLoop()
  159. '这坨循环代码也是老屎坑了,救救.jpg
  160. If Not HasAnimation OrElse IsLooping OrElse Not InnerState = MyLoadingState.Run OrElse AniSpeed > 10 OrElse Not IsLoaded Then Return
  161. IsLooping = True
  162. ErrorAnimationWaiting = True
  163. AniStart({
  164. AaRotateTransform(PathPickaxe, -20 - CType(PathPickaxe.RenderTransform, RotateTransform).Angle, 350, 250, New AniEaseInBack(AniEasePower.Weak)),
  165. AaRotateTransform(PathPickaxe, 50, 900,, New AniEaseOutFluent, True),
  166. AaRotateTransform(PathPickaxe, 25, 900,, New AniEaseOutElastic(AniEasePower.Weak)),
  167. AaCode(Sub()
  168. PathLeft.Opacity = 1
  169. PathLeft.Margin = New Thickness(7, 41, 0, 0)
  170. PathRight.Opacity = 1
  171. PathRight.Margin = New Thickness(14, 41, 0, 0)
  172. ErrorAnimationWaiting = False
  173. End Sub),
  174. AaOpacity(PathLeft, -1, 100, 50),
  175. AaX(PathLeft, -5, 180,, New AniEaseOutFluent),
  176. AaY(PathLeft, -6, 180,, New AniEaseOutFluent),
  177. AaOpacity(PathRight, -1, 100, 50),
  178. AaX(PathRight, 5, 180,, New AniEaseOutFluent),
  179. AaY(PathRight, -6, 180,, New AniEaseOutFluent),
  180. AaCode(Sub()
  181. IsLooping = False
  182. AniLoop()
  183. End Sub,, True)
  184. }, "MyLoader Loop " & Uuid & "/" & GetUuid())
  185. If ShowProgress Then
  186. End If
  187. End Sub
  188. ''' <summary>
  189. ''' 镐子是否还没挥下去,要求错误动画等待。
  190. ''' </summary>
  191. Private ErrorAnimationWaiting As Boolean = False
  192. Private Sub ErrorAnimation(sender As Object, isError As Boolean)
  193. If isError Then
  194. '非错误变为错误
  195. Dim Wait As Integer = If(ErrorAnimationWaiting, 400, 0)
  196. AniStart({
  197. AaColor(PanBack, ForegroundProperty, "ColorBrushRedLight", 300),
  198. AaOpacity(PathError, 1 - PathError.Opacity, 100, 300 + Wait),
  199. AaScaleTransform(PathError, 1 - CType(PathError.RenderTransform, ScaleTransform).ScaleX, 400, 300 + Wait, New AniEaseOutBack)
  200. }, "MyLoader Error " & Uuid)
  201. Else
  202. '错误变为非错误
  203. AniStart({
  204. AaOpacity(PathError, -PathError.Opacity, 100),
  205. AaScaleTransform(PathError, 0.5 - CType(PathError.RenderTransform, ScaleTransform).ScaleX, 200),
  206. AaColor(PanBack, ForegroundProperty, "ColorBrush3", 300)
  207. }, "MyLoader Error " & Uuid)
  208. End If
  209. End Sub
  210. #End Region
  211. #Region "点击事件"
  212. Private Sub Button_MouseUp(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonUp
  213. RaiseEvent Click(sender, e)
  214. End Sub
  215. Private IsMouseDown As Boolean = False
  216. Private Sub Button_MouseDown(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonDown
  217. '鼠标点击判定(务必放在点击事件之后,以使得 Button_MouseUp 先于 Button_MouseLeave 执行)
  218. IsMouseDown = True
  219. End Sub
  220. Private Sub Button_MouseLeave(sender As Object, e As Object) Handles Me.MouseLeave, Me.MouseLeftButtonUp
  221. IsMouseDown = False
  222. End Sub
  223. #End Region
  224. End Class
  225. Public Interface ILoadingTrigger
  226. ReadOnly Property IsLoader As Boolean
  227. Property LoadingState As MyLoadingState
  228. Event LoadingStateChanged(NewState As MyLoadingState, OldState As MyLoadingState)
  229. Event ProgressChanged(NewProgress As Double, OldProgress As Double)
  230. End Interface
  231. Public Class MyLoadingStateSimulator
  232. Implements ILoadingTrigger
  233. Private Property _LoadingState As MyLoadingState = MyLoadingState.Unloaded
  234. Public Property LoadingState As MyLoadingState Implements ILoadingTrigger.LoadingState
  235. Get
  236. Return _LoadingState
  237. End Get
  238. Set(value As MyLoadingState)
  239. If _LoadingState = value Then Return
  240. Dim OldState = _LoadingState
  241. _LoadingState = value
  242. RaiseEvent LoadingStateChanged(value, OldState)
  243. End Set
  244. End Property
  245. Public ReadOnly Property IsLoader As Boolean = False Implements ILoadingTrigger.IsLoader
  246. Public Event LoadingStateChanged(NewState As MyLoadingState, OldState As MyLoadingState) Implements ILoadingTrigger.LoadingStateChanged
  247. Public Event ProgressChanged(NewProgress As Double, OldProgress As Double) Implements ILoadingTrigger.ProgressChanged
  248. End Class