VScroll.xaml.vb 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. Public Class VScroll
  2. Public Event Change(ByVal sender As Object)
  3. #Region "属性"
  4. Private _ParentControl As Grid = Nothing
  5. Private _ParentSubControl As Grid = Nothing
  6. ''' <summary>
  7. ''' 对应的控件的容器。
  8. ''' </summary>
  9. ''' <remarks></remarks>
  10. Public ReadOnly Property ParentControl As Grid
  11. Get
  12. Return _ParentControl
  13. End Get
  14. End Property
  15. Private _Control As Object = Nothing
  16. ''' <summary>
  17. ''' 对应的控件。它是需要被滚动的容器,并且被一个Grid包裹。
  18. ''' </summary>
  19. ''' <remarks></remarks>
  20. Public ReadOnly Property Control As Object
  21. Get
  22. Return _Control
  23. End Get
  24. End Property
  25. ''' <summary>
  26. ''' 按钮离滚动条顶部的距离。
  27. ''' </summary>
  28. ''' <ValueMe></ValueMe>
  29. ''' <returns></returns>
  30. ''' <remarks></remarks>
  31. Public Property ValueMe As Double
  32. Get
  33. Return btnDrag.Margin.Top
  34. End Get
  35. Set(ByVal ValueMe As Double)
  36. If QuoteValue = 0 Then Exit Property
  37. SetTop(btnDrag, MathRange(ValueMe, 0, MaxValueMe - QuoteValueMe))
  38. SetTop(Control, -MathRange(
  39. If(QuoteValueMe = 15, (MaxValue - QuoteValue) / (MaxValueMe - QuoteValueMe) * ValueMe, MaxValue / QuoteValue * ValueMe),
  40. 0, MaxValue - QuoteValue))
  41. End Set
  42. End Property
  43. ''' <summary>
  44. ''' 按钮高度。
  45. ''' </summary>
  46. ''' <ValueMe></ValueMe>
  47. ''' <returns></returns>
  48. ''' <remarks></remarks>
  49. Public Property QuoteValueMe As Double
  50. Get
  51. Return btnDrag.ActualHeight
  52. End Get
  53. Set(ByVal ValueMe As Double)
  54. btnDrag.Height = ValueMe
  55. End Set
  56. End Property
  57. ''' <summary>
  58. ''' 滚动条高度。
  59. ''' </summary>
  60. ''' <ValueMe></ValueMe>
  61. ''' <returns></returns>
  62. ''' <remarks></remarks>
  63. Public ReadOnly Property MaxValueMe As Double
  64. Get
  65. Return Me.ActualHeight
  66. End Get
  67. End Property
  68. ''' <summary>
  69. ''' 控件滚动的距离。
  70. ''' </summary>
  71. ''' <ValueMe></ValueMe>
  72. ''' <returns></returns>
  73. ''' <remarks></remarks>
  74. Public Property Value As Double
  75. Get
  76. Return If(IsNothing(Control), 0, -Control.Margin.Top)
  77. End Get
  78. Set(ByVal ValueMe As Double)
  79. Me.ValueMe = If(QuoteValueMe = 15, (MaxValueMe - QuoteValueMe) / (MaxValue - QuoteValue) * ValueMe, MaxValueMe / MaxValue * ValueMe)
  80. End Set
  81. End Property
  82. ''' <summary>
  83. ''' 控件可以显示的最大高度。等同于容器的高度。
  84. ''' </summary>
  85. ''' <ValueMe></ValueMe>
  86. ''' <returns></returns>
  87. ''' <remarks></remarks>
  88. Public ReadOnly Property QuoteValue As Double
  89. Get
  90. Return If(IsNothing(ParentControl), 0, ParentControl.ActualHeight)
  91. End Get
  92. End Property
  93. ''' <summary>
  94. ''' 最大值。等同于控件的高度。
  95. ''' </summary>
  96. ''' <ValueMe></ValueMe>
  97. ''' <returns></returns>
  98. ''' <remarks></remarks>
  99. Public ReadOnly Property MaxValue As Double
  100. Get
  101. Return If(IsNothing(Control), 0, Control.ActualHeight)
  102. End Get
  103. End Property
  104. Private _InCenter As Boolean
  105. ''' <summary>
  106. ''' 是否在控件高度不足最高高度时把它居中。
  107. ''' </summary>
  108. ''' <ValueMe></ValueMe>
  109. ''' <returns></returns>
  110. ''' <remarks></remarks>
  111. Public ReadOnly Property InCenter As Boolean
  112. Get
  113. Return _InCenter
  114. End Get
  115. End Property
  116. ''' <summary>
  117. ''' 在判断滚动条是否显示时的容错范围。
  118. ''' </summary>
  119. ''' <value></value>
  120. ''' <returns></returns>
  121. ''' <remarks></remarks>
  122. Public Property WrongRange As Double = 2
  123. #End Region
  124. #Region "鼠标动画"
  125. Private Sub VScroll_MouseEnter(ByVal sender As Object, ByVal e As System.Windows.Input.MouseEventArgs) Handles Me.MouseEnter
  126. If Len(Me.Name) < 1 Then Me.Name = "Aniamtioner" & GetUUID() '确保这个控件有名称
  127. AniStart({
  128. AaOpacity(btnDrag, 1 - btnDrag.Opacity, 200),
  129. AaOpacity(btnBack, 0.3 - btnBack.Opacity, 200)
  130. }, Me.Name & "Mouse")
  131. End Sub
  132. Private Sub VScroll_MouseLeave(ByVal sender As Object, ByVal e As System.Windows.Input.MouseEventArgs) Handles Me.MouseLeave
  133. If IsNothing(DragingScroll) Then DragMouseLeave()
  134. End Sub
  135. ''' <summary>
  136. ''' 触发离开动画。
  137. ''' </summary>
  138. ''' <remarks></remarks>
  139. Public Sub DragMouseLeave()
  140. AniStart({
  141. AaOpacity(btnDrag, 0.4 - btnDrag.Opacity, 200),
  142. AaOpacity(btnBack, 0.01 - btnBack.Opacity, 200)
  143. }, Me.Name & "Mouse")
  144. End Sub
  145. #End Region
  146. #Region "鼠标拖动"
  147. Private ClickedY As Integer '鼠标指针在按钮上的相对Y坐标
  148. Private ShouldTop As Integer '按钮到顶部的距离
  149. Private Sub btnDrag_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Input.MouseButtonEventArgs) Handles btnDrag.MouseDown
  150. ShouldTop = btnDrag.Margin.Top
  151. ClickedY = e.GetPosition(btnDrag).Y
  152. DragingScroll = Me
  153. End Sub
  154. Public Sub Draging(ByVal Y As Integer)
  155. ShouldTop = MathRange(Y - ClickedY + ShouldTop, 0, MaxValueMe - QuoteValueMe) '实际应该在的位置
  156. If Not ValueMe = ShouldTop Then ValueMe = ShouldTop
  157. End Sub
  158. #End Region
  159. ''' <summary>
  160. ''' 设置绑定的控件。这个方法务必只能执行一次。
  161. ''' </summary>
  162. ''' <param name="Control">需要被拖动的控件。它需要被一个Grid覆盖。</param>
  163. ''' <param name="InCenter">是否在控件高度不足最高高度时把它居中。</param>
  164. ''' <remarks></remarks>
  165. Public Function SetControl(ByVal Control As StackPanel, ByVal InCenter As Boolean) As Boolean
  166. '可靠性检查
  167. If IsNothing(Control) Then Return False
  168. If IsNothing(Control.Parent) Then Return False
  169. If Not Control.Parent.GetType.Name = "Grid" Then Return False
  170. If Not IsNothing(ParentControl) Then Return False
  171. Try
  172. '初始化
  173. _ParentControl = Control.Parent
  174. _ParentControl.UpdateLayout()
  175. _ParentSubControl = New Grid With {.Width = _ParentControl.ActualWidth + 0.51, .VerticalAlignment = VerticalAlignment.Top}
  176. _Control = Control
  177. _InCenter = InCenter
  178. SetTop(btnDrag, 0)
  179. '初始化控件
  180. _ParentControl.SetValue(ContentPresenter.ContentProperty, Nothing)
  181. _ParentControl.Children.Clear()
  182. _ParentControl.Children.Add(_ParentSubControl)
  183. _ParentSubControl.Children.Add(_Control)
  184. _Control.VerticalAlignment = VerticalAlignment.Top
  185. '添加委托
  186. AddHandler Me.SizeChanged, AddressOf Refresh
  187. AddHandler Control.SizeChanged, AddressOf Refresh
  188. AddHandler ParentControl.SizeChanged, AddressOf Refresh
  189. AddHandler btnDrag.SizeChanged, AddressOf Refresh
  190. Catch ex As Exception
  191. ExShow(ex, "滚动条绑定失败", ErrorLevel.MsgboxAndFeedback)
  192. Return False
  193. End Try
  194. '刷新滚动条
  195. Refresh(Nothing, Nothing)
  196. Return True
  197. End Function
  198. ''' <summary>
  199. ''' 刷新QuoteValue与高度等。
  200. ''' </summary>
  201. ''' <returns></returns>
  202. ''' <remarks></remarks>
  203. Private Function Refresh(ByVal sender, ByVal e) As Boolean
  204. Try
  205. '可靠性检查
  206. If IsNothing(_Control) Or IsNothing(ParentControl) Then Return False
  207. If QuoteValue = 0 Or MaxValue = 0 Or Not MathCheck(QuoteValueMe) Then Return False
  208. _ParentSubControl.Width = _ParentControl.ActualWidth + 0.51
  209. Dim OldValueMe = ValueMe
  210. Dim OldValue = Value
  211. QuoteValueMe = MathRange(MaxValueMe / MaxValue * QuoteValue, 15, MaxValueMe)
  212. If MaxValueMe - QuoteValueMe < WrongRange Then
  213. '过短
  214. AniStart({AaOpacity(Me, -Me.Opacity, 100)}, Me.Name & "Opacity", False)
  215. _Control.Margin = New Thickness(0)
  216. If InCenter Then _ParentSubControl.VerticalAlignment = VerticalAlignment.Center
  217. Value = 0
  218. Else
  219. '正常
  220. AniStart({AaOpacity(Me, 1 - Me.Opacity, 100)}, Me.Name & "Opacity", False)
  221. _ParentSubControl.VerticalAlignment = VerticalAlignment.Top
  222. Value = OldValue
  223. End If
  224. If Not ValueMe = OldValueMe Then RaiseEvent Change(Me)
  225. Return True
  226. Catch ex As Exception
  227. ExShow(ex, "刷新滚动条失败", ErrorLevel.MsgboxAndFeedback)
  228. Return False
  229. End Try
  230. End Function
  231. ''' <summary>
  232. ''' 委托处理鼠标滚轮事件。将试图接管滚轮的控件添加一个Handler到这个事件。
  233. ''' </summary>
  234. ''' <param name="sender"></param>
  235. ''' <param name="e"></param>
  236. ''' <remarks></remarks>
  237. Public Sub RunMouseWheel(ByVal sender As Object, ByVal e As System.Windows.Input.MouseWheelEventArgs)
  238. If Me.Visibility = Visibility.Visible Then
  239. '如果时间为负数就不会执行,所以加绝对值
  240. AniStart({
  241. AaValue(Me, -e.Delta, Math.Abs(e.Delta) * 2, , New AniEaseEnd)
  242. }, "Scroll" & GetUUID(), False)
  243. End If
  244. End Sub
  245. ''' <summary>
  246. ''' 点击滚动条空白部分改变值。
  247. ''' </summary>
  248. ''' <param name="sender"></param>
  249. ''' <param name="e"></param>
  250. ''' <remarks></remarks>
  251. Private Sub btnBack_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Input.MouseButtonEventArgs) Handles btnBack.MouseUp
  252. If btnDrag.IsMouseOver Then Exit Sub '点在空白部分
  253. If e.GetPosition(btnDrag).Y < 0 Then
  254. AniStart({
  255. AaValue(Me, -QuoteValue, 200, , New AniEaseEnd)
  256. }, "ScrollClick" & GetUUID(), False)
  257. Else
  258. AniStart({
  259. AaValue(Me, QuoteValue, 200, , New AniEaseEnd)
  260. }, "ScrollClick" & GetUUID(), False)
  261. End If
  262. End Sub
  263. End Class