MyCard.vb 17 KB

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