MyCard.vb 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. Public Class MyCard
  2. '控件
  3. Inherits Grid
  4. Private ReadOnly MainGrid As Grid
  5. Private ReadOnly MainChrome As SystemDropShadowChrome
  6. Private ReadOnly MainBorder As Border
  7. Public Property BorderChild As UIElement
  8. Get
  9. Return MainBorder.Child
  10. End Get
  11. Set(value As UIElement)
  12. MainBorder.Child = value
  13. End Set
  14. End Property
  15. Public MainTextBlock As TextBlock
  16. Public MainSwap As Shapes.Path
  17. '属性
  18. Public Uuid As Integer = GetUuid()
  19. Public Property Title As String
  20. Get
  21. Return GetValue(TitleProperty)
  22. End Get
  23. Set(value As String)
  24. SetValue(TitleProperty, value)
  25. If MainTextBlock IsNot Nothing Then MainTextBlock.Text = value
  26. End Set
  27. End Property
  28. Public Shared ReadOnly TitleProperty As DependencyProperty = DependencyProperty.Register("Title", GetType(String), GetType(MyCard), New PropertyMetadata(""))
  29. 'UI 建立
  30. Public Sub New()
  31. MainChrome = New SystemDropShadowChrome With {.Margin = New Thickness(-9.5, -9, 0.5, -0.5), .Opacity = 0.1, .CornerRadius = New CornerRadius(6)}
  32. MainChrome.SetResourceReference(SystemDropShadowChrome.ColorProperty, "ColorObject1")
  33. Children.Insert(0, MainChrome)
  34. MainBorder = New Border With {.Background = New SolidColorBrush(Color.FromArgb(205, 255, 255, 255)), .CornerRadius = New CornerRadius(6), .IsHitTestVisible = False}
  35. Children.Insert(1, MainBorder)
  36. MainGrid = New Grid
  37. Children.Add(MainGrid)
  38. End Sub
  39. Private IsLoad As Boolean = False
  40. Private Sub MyCard_Loaded() Handles Me.Loaded
  41. '卡片的进入时动画已被页面通用切换动画替代
  42. 'If LoadAnimation AndAlso IsLoad Then
  43. ' StartHeightAnimation(Math.Min(50, ActualHeight), ActualHeight - Math.Min(50, ActualHeight), True)
  44. 'End If
  45. If IsLoad Then Exit Sub
  46. '初次加载限定
  47. If Not Title = "" AndAlso IsNothing(MainTextBlock) Then
  48. MainTextBlock = New TextBlock With {.HorizontalAlignment = HorizontalAlignment.Left, .VerticalAlignment = VerticalAlignment.Top, .Margin = New Thickness(15, 12, 0, 0), .FontWeight = FontWeights.Bold, .FontSize = 13, .IsHitTestVisible = False}
  49. MainTextBlock.SetResourceReference(TextBlock.ForegroundProperty, "ColorBrush1")
  50. MainTextBlock.SetBinding(TextBlock.TextProperty, New Binding("Title") With {.Source = Me, .Mode = BindingMode.OneWay})
  51. MainGrid.Children.Add(MainTextBlock)
  52. End If
  53. If CanSwap OrElse SwapControl IsNot Nothing Then
  54. If SwapControl Is Nothing AndAlso Children.Count > 3 Then SwapControl = Children(3)
  55. MainSwap = New Shapes.Path With {.HorizontalAlignment = HorizontalAlignment.Right, .Stretch = Stretch.Uniform, .Height = 6, .Width = 10, .VerticalAlignment = VerticalAlignment.Top, .Margin = New Thickness(0, 17, 16, 0), .Data = New GeometryConverter().ConvertFromString("M2,4 l-2,2 10,10 10,-10 -2,-2 -8,8 -8,-8 z"), .RenderTransform = New RotateTransform(180), .RenderTransformOrigin = New Point(0.5, 0.5)}
  56. MainSwap.SetResourceReference(Shapes.Path.FillProperty, "ColorBrush1")
  57. MainGrid.Children.Add(MainSwap)
  58. End If
  59. IsLoad = True
  60. '改变默认的折叠
  61. If IsSwaped AndAlso SwapControl IsNot Nothing Then
  62. MainSwap.RenderTransform = New RotateTransform(If(SwapLogoRight, 270, 0))
  63. '取消由于高度变化被迫触发的高度动画
  64. Dim RawUseAnimation As Boolean = UseAnimation
  65. UseAnimation = False
  66. Height = SwapedHeight
  67. AniStop("MyCard Height " & Uuid)
  68. IsHeightAnimating = False
  69. RunInUi(Sub() UseAnimation = RawUseAnimation, True)
  70. End If
  71. End Sub
  72. Public Sub StackInstall()
  73. StackInstall(SwapControl, SwapType, Title)
  74. TriggerForceResize()
  75. End Sub
  76. Public Shared Sub StackInstall(ByRef Stack As StackPanel, Type As Integer, Optional CardTitle As String = "")
  77. '这一部分的代码是好几年前留下的究极屎坑,当时还不知道该咋正确调用这种方法,就写了这么一坨屎
  78. '但是现在……反正勉强能用……懒得改了就这样吧.jpg
  79. '别骂了别骂了.jpg
  80. If IsNothing(Stack.Tag) Then Exit Sub
  81. '排序
  82. Select Case Type
  83. Case 3
  84. Stack.Tag = Sort(CType(Stack.Tag, List(Of DlOptiFineListEntry)), Function(Left As DlOptiFineListEntry, Right As DlOptiFineListEntry) As Boolean
  85. Return VersionSortBoolean(Left.NameDisplay, Right.NameDisplay)
  86. End Function)
  87. Case 4, 10
  88. Stack.Tag = Sort(CType(Stack.Tag, List(Of DlLiteLoaderListEntry)), Function(Left As DlLiteLoaderListEntry, Right As DlLiteLoaderListEntry) As Boolean
  89. Return VersionSortBoolean(Left.Inherit, Right.Inherit)
  90. End Function)
  91. Case 6
  92. Stack.Tag = Sort(CType(Stack.Tag, List(Of DlForgeVersionEntry)), Function(Left As DlForgeVersionEntry, Right As DlForgeVersionEntry) As Boolean
  93. Return VersionSortBoolean(Left.Version, Right.Version)
  94. End Function)
  95. Case 8, 9
  96. Stack.Tag = Sort(CType(Stack.Tag, List(Of CompFile)), Function(Left As CompFile, Right As CompFile) As Boolean
  97. Return Left.ReleaseDate > Right.ReleaseDate
  98. End Function)
  99. End Select
  100. '控件转换
  101. Select Case Type
  102. Case 5
  103. Dim LoadingPickaxe As New MyLoading With {.Text = "正在获取版本列表", .Margin = New Thickness(5)}
  104. Dim Loader = New LoaderTask(Of String, List(Of DlForgeVersionEntry))("DlForgeVersion Main", AddressOf DlForgeVersionMain)
  105. LoadingPickaxe.State = Loader
  106. Loader.Start(Stack.Tag)
  107. AddHandler LoadingPickaxe.StateChanged, AddressOf FrmDownloadForge.Forge_StateChanged
  108. AddHandler LoadingPickaxe.Click, AddressOf FrmDownloadForge.Forge_Click
  109. Stack.Children.Add(LoadingPickaxe)
  110. Case 6
  111. ForgeDownloadListItemPreload(Stack, Stack.Tag, AddressOf ForgeSave_Click, True)
  112. Case 8
  113. CompFilesCardPreload(Stack, Stack.Tag, AddressOf FrmDownloadMod.ProjectClick)
  114. End Select
  115. '实现控件虚拟化
  116. For Each Data As Object In Stack.Tag
  117. Select Case Type
  118. Case 0
  119. Stack.Children.Add(PageSelectRight.McVersionListItem(Data))
  120. Case 2
  121. Stack.Children.Add(McDownloadListItem(Data, AddressOf McDownloadMenuSave, True))
  122. Case 3
  123. Stack.Children.Add(OptiFineDownloadListItem(Data, AddressOf OptiFineSave_Click, True))
  124. Case 4
  125. Stack.Children.Add(LiteLoaderDownloadListItem(Data, AddressOf FrmDownloadLiteLoader.DownloadStart, False))
  126. Case 5
  127. Case 6
  128. Stack.Children.Add(ForgeDownloadListItem(Data, AddressOf ForgeSave_Click, True))
  129. Case 7
  130. Stack.Children.Add(McDownloadListItem(Data, Sub(sender, e) '不能使用 AddressOf,这导致了 #535,原因完全不明,疑似是编译器 Bug
  131. FrmDownloadInstall.MinecraftSelected(sender, e)
  132. End Sub, False))
  133. Case 8
  134. Stack.Children.Add(CType(Data, CompFile).ToListItem(AddressOf FrmDownloadCompDetail.Save_Click))
  135. Case 9
  136. Stack.Children.Add(CType(Data, CompFile).ToListItem(AddressOf FrmDownloadCompDetail.Install_Click, AddressOf FrmDownloadCompDetail.Save_Click))
  137. Case 10
  138. Stack.Children.Add(LiteLoaderDownloadListItem(Data, AddressOf LiteLoaderSave_Click, True))
  139. Case 11
  140. Stack.Children.Add(CType(Data, HelpEntry).ToListItem)
  141. Case Else
  142. Log("未知的虚拟化种类:" & Type, LogLevel.Feedback)
  143. End Select
  144. Next
  145. Stack.Children.Add(New FrameworkElement With {.Height = 18}) '下边距,同时适应折叠
  146. Stack.Tag = Nothing
  147. End Sub
  148. '事件
  149. Private Sub MyCard_MouseEnter(sender As Object, e As MouseEventArgs) Handles Me.MouseEnter
  150. Dim AniList As New List(Of AniData)
  151. If Not IsNothing(MainTextBlock) Then AniList.Add(AaColor(MainTextBlock, TextBlock.ForegroundProperty, "ColorBrush2", 150))
  152. If Not IsNothing(MainSwap) Then AniList.Add(AaColor(MainSwap, Shapes.Path.FillProperty, "ColorBrush2", 150))
  153. AniList.AddRange({
  154. AaColor(MainChrome, SystemDropShadowChrome.ColorProperty, "ColorObject2", 180),
  155. AaColor(MainBorder, Border.BackgroundProperty, New MyColor(230, 255, 255, 255) - MainBorder.Background, 180),
  156. AaOpacity(MainChrome, 0.3 - MainChrome.Opacity, 180)
  157. })
  158. AniStart(AniList, "MyCard Mouse " & Uuid)
  159. End Sub
  160. Private Sub MyCard_MouseLeave(sender As Object, e As MouseEventArgs) Handles Me.MouseLeave
  161. Dim AniList As New List(Of AniData)
  162. If Not IsNothing(MainTextBlock) Then AniList.Add(AaColor(MainTextBlock, TextBlock.ForegroundProperty, "ColorBrush1", 250))
  163. If Not IsNothing(MainSwap) Then AniList.Add(AaColor(MainSwap, Shapes.Path.FillProperty, "ColorBrush1", 250))
  164. AniList.AddRange({
  165. AaColor(MainChrome, SystemDropShadowChrome.ColorProperty, "ColorObject1", 300),
  166. AaColor(MainBorder, Border.BackgroundProperty, New MyColor(205, 255, 255, 255) - MainBorder.Background, 300),
  167. AaOpacity(MainChrome, 0.1 - MainChrome.Opacity, 300)
  168. })
  169. AniStart(AniList, "MyCard Mouse " & Uuid)
  170. End Sub
  171. #Region "高度改变动画"
  172. ''' <summary>
  173. ''' 是否启用高度改变动画。
  174. ''' </summary>
  175. Public Property UseAnimation As Boolean = True
  176. Private IsHeightAnimating As Boolean = False
  177. Private ActualUsedHeight As Double '回滚实际高度(例如 NaN)
  178. Private Sub MySizeChanged(sender As Object, e As SizeChangedEventArgs) Handles Me.SizeChanged
  179. If Not UseAnimation Then Exit Sub
  180. Dim DeltaHeight As Double = If(IsSwaped, SwapedHeight, e.NewSize.Height) - e.PreviousSize.Height
  181. '卡片的进入时动画已被页面通用切换动画替代
  182. If e.PreviousSize.Height = 0 OrElse IsHeightAnimating OrElse Math.Abs(DeltaHeight) < 1 OrElse ActualHeight = 0 Then Exit Sub
  183. StartHeightAnimation(DeltaHeight, e.PreviousSize.Height, False)
  184. End Sub
  185. Private Sub StartHeightAnimation(DeltaHeight As Double, PreviousHeight As Double, IsLoadAnimation As Boolean)
  186. If IsHeightAnimating OrElse FrmMain Is Nothing Then Exit Sub '避免 XAML 设计器出错
  187. Dim AnimList As New List(Of AniData)
  188. If DeltaHeight > 10 OrElse (DeltaHeight < -10 AndAlso Not IsNothing(SwapControl)) Then '如果不是需要折叠的卡片,高度减小时的弹跳会吞掉按钮下边框
  189. '高度增加较大,使用弹起动画
  190. Dim Delta As Double = MathRange(Math.Abs(DeltaHeight) * 0.05, 3, 10) * Math.Sign(DeltaHeight)
  191. AnimList.AddRange({
  192. AaHeight(Me, DeltaHeight + Delta, 300, If(IsLoadAnimation, 30, 0), If(DeltaHeight > FrmMain.Height, New AniEaseInFluent(AniEasePower.ExtraStrong), New AniEaseOutFluent(AniEasePower.ExtraStrong))),
  193. AaHeight(Me, -Delta, 150, 260, Ease:=New AniEaseOutFluent(AniEasePower.Strong))
  194. })
  195. Else
  196. '普通的改变就行啦
  197. AnimList.AddRange({
  198. AaHeight(Me, DeltaHeight, MathRange(Math.Abs(DeltaHeight) * 4, 150, 250),, New AniEaseOutFluent)
  199. })
  200. End If
  201. AnimList.Add(AaCode(Sub()
  202. IsHeightAnimating = False
  203. Height = ActualUsedHeight
  204. If IsSwaped Then SwapControl.Visibility = Visibility.Collapsed
  205. End Sub,, True))
  206. AniStart(AnimList, "MyCard Height " & Uuid)
  207. IsHeightAnimating = True
  208. ActualUsedHeight = If(IsSwaped, SwapedHeight, Height)
  209. Height = PreviousHeight
  210. End Sub
  211. ''' <summary>
  212. ''' 通知 MyCard,控件内容已改变,需要中断动画并更新高度。
  213. ''' </summary>
  214. Public Sub TriggerForceResize()
  215. Height = If(IsSwaped, SwapedHeight, Double.NaN)
  216. AniStop("MyCard Height " & Uuid)
  217. IsHeightAnimating = False
  218. End Sub
  219. #End Region
  220. '折叠
  221. '若设置了 CanSwap,或 SwapControl 不为空,则判定为会进行折叠
  222. '这是因为不能直接在 XAML 中设置 SwapControl
  223. Public SwapControl As Object
  224. Public Property CanSwap As Boolean = False
  225. ''' <summary>
  226. ''' 被折叠的种类,用于控件虚拟化。
  227. ''' </summary>
  228. Public Property SwapType As Integer
  229. ''' <summary>
  230. ''' 是否已被折叠。
  231. ''' </summary>
  232. Public Property IsSwaped As Boolean
  233. Get
  234. Return _IsSwaped
  235. End Get
  236. Set(value As Boolean)
  237. If _IsSwaped = value Then Exit Property
  238. _IsSwaped = value
  239. If SwapControl Is Nothing Then Exit Property
  240. '展开
  241. If Not IsSwaped AndAlso TypeOf SwapControl Is StackPanel Then StackInstall(SwapControl, SwapType, Title)
  242. '若尚未加载,会在 Loaded 事件中触发无动画的折叠,不需要在这里进行
  243. If Not IsLoaded Then Exit Property
  244. '更新高度
  245. SwapControl.Visibility = Visibility.Visible
  246. TriggerForceResize()
  247. '改变箭头
  248. AniStart(AaRotateTransform(MainSwap, If(_IsSwaped, If(SwapLogoRight, 270, 0), 180) - CType(MainSwap.RenderTransform, RotateTransform).Angle, 400,, New AniEaseOutBack(AniEasePower.Weak)), "MyCard Swap " & Uuid, True)
  249. End Set
  250. End Property
  251. Private _IsSwaped As Boolean = False
  252. Public Property SwapLogoRight As Boolean = False
  253. Private IsMouseDown As Boolean = False
  254. Public Event PreviewSwap(sender As Object, e As RouteEventArgs)
  255. Public Event Swap(sender As Object, e As RouteEventArgs)
  256. Public Const SwapedHeight As Integer = 40
  257. Private Sub MyCard_MouseLeftButtonDown(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonDown
  258. Dim Pos As Double = Mouse.GetPosition(Me).Y
  259. If Not IsSwaped AndAlso (IsNothing(SwapControl) OrElse Pos > SwapedHeight OrElse (Pos = 0 AndAlso Not IsMouseDirectlyOver)) Then Exit Sub '检测点击位置;或已经不在可视树上的误判
  260. IsMouseDown = True
  261. End Sub
  262. Private Sub MyCard_MouseLeftButtonUp(sender As Object, e As MouseButtonEventArgs) Handles Me.MouseLeftButtonUp
  263. If IsMouseDown Then
  264. IsMouseDown = False
  265. Dim Pos As Double = Mouse.GetPosition(Me).Y
  266. If Not IsSwaped AndAlso (IsNothing(SwapControl) OrElse Pos > SwapedHeight OrElse (Pos = 0 AndAlso Not IsMouseDirectlyOver)) Then Exit Sub '检测点击位置;或已经不在可视树上的误判
  267. Dim ee = New RouteEventArgs(True)
  268. RaiseEvent PreviewSwap(Me, ee)
  269. If ee.Handled Then
  270. IsMouseDown = False
  271. Exit Sub
  272. End If
  273. IsSwaped = Not IsSwaped
  274. Log("[Control] " & If(IsSwaped, "折叠卡片", "展开卡片") & If(Title Is Nothing, "", ":" & Title))
  275. RaiseEvent Swap(Me, ee)
  276. End If
  277. End Sub
  278. Private Sub MyCard_MouseLeave_Swap(sender As Object, e As MouseEventArgs) Handles Me.MouseLeave
  279. IsMouseDown = False
  280. End Sub
  281. End Class
  282. Partial Public Module ModAnimation
  283. Public Sub AniDispose(Control As MyCard, RemoveFromChildren As Boolean, Optional CallBack As ParameterizedThreadStart = Nothing)
  284. If Control.IsHitTestVisible Then
  285. Control.IsHitTestVisible = False
  286. AniStart({
  287. AaScaleTransform(Control, -0.08, 200,, New AniEaseInFluent),
  288. AaOpacity(Control, -1, 200,, New AniEaseOutFluent),
  289. AaHeight(Control, -Control.ActualHeight, 150, 100, New AniEaseOutFluent),
  290. AaCode(Sub()
  291. If RemoveFromChildren Then
  292. If Control.Parent Is Nothing Then Exit Sub
  293. CType(Control.Parent, Object).Children.Remove(Control)
  294. Else
  295. Control.Visibility = Visibility.Collapsed
  296. End If
  297. If CallBack IsNot Nothing Then CallBack(Control)
  298. End Sub,, True)
  299. }, "MyCard Dispose " & Control.Uuid)
  300. Else
  301. If RemoveFromChildren Then
  302. If Control.Parent Is Nothing Then Exit Sub
  303. CType(Control.Parent, Object).Children.Remove(Control)
  304. Else
  305. Control.Visibility = Visibility.Collapsed
  306. End If
  307. If CallBack IsNot Nothing Then CallBack(Control)
  308. End If
  309. End Sub
  310. End Module