MyLoading.xaml.vb 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  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 Exit Property
  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
  60. If InnerState = MyLoadingState.Error Then
  61. If TextErrorInherit AndAlso State.IsLoader Then
  62. Dim Ex As Exception = CType(State, Object).Error
  63. If Ex Is Nothing Then
  64. LabText.Text = "未知错误"
  65. Else
  66. Do While Ex.InnerException IsNot Nothing
  67. Ex = Ex.InnerException
  68. Loop
  69. LabText.Text = StrTrim(Ex.Message)
  70. If {"远程主机强迫关闭了", "远程方已关闭传输流", "未能解析此远程名称", "由于目标计算机积极拒绝",
  71. "操作已超时", "操作超时", "服务器超时", "连接超时"}.Any(Function(s) LabText.Text.Contains(s)) Then
  72. LabText.Text = "网络环境不佳,请重试或尝试使用 VPN"
  73. End If
  74. End If
  75. Else
  76. LabText.Text = TextError
  77. End If
  78. Else
  79. If ShowProgress AndAlso State.IsLoader Then
  80. LabText.Text = Text & " - " & Math.Floor(CType(State, Object).Progress * 100) & "%"
  81. Else
  82. LabText.Text = Text
  83. End If
  84. End If
  85. End Sub
  86. #End Region
  87. #Region "状态改变"
  88. '状态枚举
  89. Public Enum MyLoadingState
  90. Unloaded = -1
  91. Run = 0
  92. [Stop] = 1
  93. [Error] = 2
  94. End Enum
  95. '用于外部改变的公开状态
  96. Private WithEvents _State As ILoadingTrigger
  97. Public Property State As ILoadingTrigger
  98. Get
  99. InitState()
  100. Return _State
  101. End Get
  102. Set(value As ILoadingTrigger)
  103. _State = value
  104. RefreshState()
  105. End Set
  106. End Property
  107. Private Sub InitState() Handles Me.Loaded
  108. If _State Is Nothing Then
  109. _State = New MyLoadingStateSimulator
  110. If AutoRun Then _State.LoadingState = MyLoadingState.Run
  111. End If
  112. End Sub
  113. Private Sub RefreshState() Handles _State.LoadingStateChanged, Me.Loaded, Me.Unloaded
  114. If _State.LoadingState = MyLoadingState.Run AndAlso Not IsLoaded Then InnerState = MyLoadingState.Stop
  115. InnerState = _State.LoadingState
  116. OuterState = _State.LoadingState
  117. AniLoop()
  118. End Sub
  119. '用于引发外部事件的状态
  120. Private Property _OuterState As MyLoadingState = MyLoadingState.Unloaded
  121. Private Property OuterState As MyLoadingState
  122. Get
  123. Return _OuterState
  124. End Get
  125. Set(value As MyLoadingState)
  126. If _OuterState = value Then Exit Property
  127. Dim OldValue = _OuterState
  128. _OuterState = value
  129. '引发事件
  130. RaiseEvent StateChanged(Me, value, OldValue)
  131. If (OldValue = MyLoadingState.Error) <> (value = MyLoadingState.Error) Then RaiseEvent IsErrorChanged(Me, value = MyLoadingState.Error)
  132. End Set
  133. End Property
  134. '用于引发内部动画事件的状态
  135. Private Property _InnerState As MyLoadingState = MyLoadingState.Unloaded
  136. Private Property InnerState As MyLoadingState
  137. Get
  138. Return _InnerState
  139. End Get
  140. Set(value As MyLoadingState)
  141. If _InnerState = value Then Exit Property
  142. Dim OldValue = _InnerState
  143. _InnerState = value
  144. '引发事件
  145. AniLoop()
  146. If (OldValue = MyLoadingState.Error) <> (value = MyLoadingState.Error) Then ErrorAnimation(Me, value = MyLoadingState.Error)
  147. End Set
  148. End Property
  149. #End Region
  150. #Region "动画"
  151. ''' <summary>
  152. ''' 是否需要动画。
  153. ''' </summary>
  154. Public Property HasAnimation As Boolean = True
  155. ''' <summary>
  156. ''' 主动画循环是否正在运行中。
  157. ''' </summary>
  158. Private IsLooping As Boolean = False
  159. Private Sub AniLoop()
  160. '这坨循环代码也是老屎坑了,救救.jpg
  161. If Not HasAnimation OrElse IsLooping OrElse Not InnerState = MyLoadingState.Run OrElse AniSpeed > 10 OrElse Not IsLoaded Then Exit Sub
  162. IsLooping = True
  163. ErrorAnimationWaiting = True
  164. If ShowProgress Then
  165. RefreshText()
  166. AniStart({
  167. AaRotateTransform(PathPickaxe, -20 - CType(PathPickaxe.RenderTransform, RotateTransform).Angle, 350, 250, New AniEaseInBack(AniEasePower.Weak)),
  168. AaCode(AddressOf RefreshText, 100), '这是为了省下一个 Timer 线程……
  169. AaCode(AddressOf RefreshText, 200),
  170. AaCode(AddressOf RefreshText, 300),
  171. AaCode(AddressOf RefreshText, 400),
  172. AaCode(AddressOf RefreshText, 500),
  173. AaRotateTransform(PathPickaxe, 50, 900,, New AniEaseOutFluent, True),
  174. AaRotateTransform(PathPickaxe, 25, 900,, New AniEaseOutElastic(AniEasePower.Weak)),
  175. AaCode(Sub()
  176. PathLeft.Opacity = 1
  177. PathLeft.Margin = New Thickness(7, 41, 0, 0)
  178. PathRight.Opacity = 1
  179. PathRight.Margin = New Thickness(14, 41, 0, 0)
  180. ErrorAnimationWaiting = False
  181. RefreshText()
  182. End Sub),
  183. AaCode(AddressOf RefreshText, 100),
  184. AaCode(AddressOf RefreshText, 200),
  185. AaCode(AddressOf RefreshText, 300),
  186. AaCode(AddressOf RefreshText, 400),
  187. AaCode(AddressOf RefreshText, 500),
  188. AaCode(AddressOf RefreshText, 600),
  189. AaCode(AddressOf RefreshText, 700),
  190. AaCode(AddressOf RefreshText, 800),
  191. AaOpacity(PathLeft, -1, 100, 70),
  192. AaX(PathLeft, -5, 180, 20, New AniEaseOutFluent),
  193. AaY(PathLeft, -6, 180, 20, New AniEaseOutFluent),
  194. AaOpacity(PathRight, -1, 100, 70),
  195. AaX(PathRight, 5, 180, 20, New AniEaseOutFluent),
  196. AaY(PathRight, -6, 180, 20, New AniEaseOutFluent),
  197. AaCode(Sub()
  198. IsLooping = False
  199. AniLoop()
  200. End Sub,, True)
  201. }, "MyLoader Loop " & Uuid & "/" & GetUuid())
  202. Else
  203. AniStart({
  204. AaRotateTransform(PathPickaxe, -20 - CType(PathPickaxe.RenderTransform, RotateTransform).Angle, 350, 250, New AniEaseInBack(AniEasePower.Weak)),
  205. AaRotateTransform(PathPickaxe, 50, 900,, New AniEaseOutFluent, True),
  206. AaRotateTransform(PathPickaxe, 25, 900,, New AniEaseOutElastic(AniEasePower.Weak)),
  207. AaCode(Sub()
  208. PathLeft.Opacity = 1
  209. PathLeft.Margin = New Thickness(7, 41, 0, 0)
  210. PathRight.Opacity = 1
  211. PathRight.Margin = New Thickness(14, 41, 0, 0)
  212. ErrorAnimationWaiting = False
  213. End Sub),
  214. AaOpacity(PathLeft, -1, 100, 50),
  215. AaX(PathLeft, -5, 180,, New AniEaseOutFluent),
  216. AaY(PathLeft, -6, 180,, New AniEaseOutFluent),
  217. AaOpacity(PathRight, -1, 100, 50),
  218. AaX(PathRight, 5, 180,, New AniEaseOutFluent),
  219. AaY(PathRight, -6, 180,, New AniEaseOutFluent),
  220. AaCode(Sub()
  221. IsLooping = False
  222. AniLoop()
  223. End Sub,, True)
  224. }, "MyLoader Loop " & Uuid & "/" & GetUuid())
  225. End If
  226. End Sub
  227. ''' <summary>
  228. ''' 镐子是否还没挥下去,要求错误动画等待。
  229. ''' </summary>
  230. Private ErrorAnimationWaiting As Boolean = False
  231. Private Sub ErrorAnimation(sender As Object, isError As Boolean)
  232. If isError Then
  233. '非错误变为错误
  234. Dim Wait As Integer = If(ErrorAnimationWaiting, 400, 0)
  235. AniStart({
  236. AaColor(PanBack, ForegroundProperty, "ColorBrushRedLight", 300),
  237. AaOpacity(PathError, 1 - PathError.Opacity, 100, 300 + Wait),
  238. AaScaleTransform(PathError, 1 - CType(PathError.RenderTransform, ScaleTransform).ScaleX, 400, 300 + Wait, New AniEaseOutBack)
  239. }, "MyLoader Error " & Uuid)
  240. Else
  241. '错误变为非错误
  242. AniStart({
  243. AaOpacity(PathError, -PathError.Opacity, 100),
  244. AaScaleTransform(PathError, 0.5 - CType(PathError.RenderTransform, ScaleTransform).ScaleX, 200),
  245. AaColor(PanBack, ForegroundProperty, "ColorBrush3", 300)
  246. }, "MyLoader Error " & Uuid)
  247. End If
  248. End Sub
  249. #End Region
  250. #Region "点击事件"
  251. Private Sub Button_MouseUp(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonUp
  252. RaiseEvent Click(sender, e)
  253. End Sub
  254. Private IsMouseDown As Boolean = False
  255. Private Sub Button_MouseDown(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonDown
  256. '鼠标点击判定(务必放在点击事件之后,以使得 Button_MouseUp 先于 Button_MouseLeave 执行)
  257. IsMouseDown = True
  258. End Sub
  259. Private Sub Button_MouseLeave(sender As Object, e As Object) Handles Me.MouseLeave, Me.MouseLeftButtonUp
  260. IsMouseDown = False
  261. End Sub
  262. #End Region
  263. End Class
  264. Public Interface ILoadingTrigger
  265. ReadOnly Property IsLoader As Boolean
  266. Property LoadingState As MyLoadingState
  267. Event LoadingStateChanged(NewState As MyLoadingState, OldState As MyLoadingState)
  268. End Interface
  269. Public Class MyLoadingStateSimulator
  270. Implements ILoadingTrigger
  271. Private Property _LoadingState As MyLoadingState = MyLoadingState.Unloaded
  272. Public Property LoadingState As MyLoadingState Implements ILoadingTrigger.LoadingState
  273. Get
  274. Return _LoadingState
  275. End Get
  276. Set(value As MyLoadingState)
  277. If _LoadingState = value Then Exit Property
  278. Dim OldState = _LoadingState
  279. _LoadingState = value
  280. RaiseEvent LoadingStateChanged(value, OldState)
  281. End Set
  282. End Property
  283. Public ReadOnly Property IsLoader As Boolean = False Implements ILoadingTrigger.IsLoader
  284. Public Event LoadingStateChanged(NewState As MyLoadingState, OldState As MyLoadingState) Implements ILoadingTrigger.LoadingStateChanged
  285. End Class