SystemDropShadowChrome.vb 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. Public NotInheritable Class SystemDropShadowChrome
  2. Inherits Decorator
  3. '源码来源于:https://referencesource.microsoft.com/#PresentationFramework.Aero/parent/Shared/Microsoft/Windows/Themes/SystemDropShadowChrome.cs,6d9c27d92a8128c1
  4. Public Shared ReadOnly ColorProperty As DependencyProperty = DependencyProperty.Register("Color", GetType(Color), GetType(SystemDropShadowChrome), New FrameworkPropertyMetadata(Color.FromArgb(&H71, &H0, &H0, &H0), FrameworkPropertyMetadataOptions.AffectsRender, New PropertyChangedCallback(AddressOf ClearBrushes)))
  5. Public Property Color As Color
  6. Get
  7. Return CType(GetValue(ColorProperty), Color)
  8. End Get
  9. Set(value As Color)
  10. SetValue(ColorProperty, value)
  11. End Set
  12. End Property
  13. Public Shared ReadOnly CornerRadiusProperty As DependencyProperty = DependencyProperty.Register("CornerRadius", GetType(CornerRadius), GetType(SystemDropShadowChrome), New FrameworkPropertyMetadata(New CornerRadius(), FrameworkPropertyMetadataOptions.AffectsRender, New PropertyChangedCallback(AddressOf ClearBrushes)), New ValidateValueCallback(AddressOf IsCornerRadiusValid))
  14. Public Property CornerRadius As CornerRadius
  15. Get
  16. Return CType(GetValue(CornerRadiusProperty), CornerRadius)
  17. End Get
  18. Set(value As CornerRadius)
  19. SetValue(CornerRadiusProperty, value)
  20. End Set
  21. End Property
  22. Private Shared Function IsCornerRadiusValid(value As Object) As Boolean
  23. Dim cr As CornerRadius = CType(value, CornerRadius)
  24. Return Not (cr.TopLeft < 0.0 OrElse cr.TopRight < 0.0 OrElse cr.BottomLeft < 0.0 OrElse cr.BottomRight < 0.0 OrElse Double.IsNaN(cr.TopLeft) OrElse Double.IsNaN(cr.TopRight) OrElse Double.IsNaN(cr.BottomLeft) OrElse Double.IsNaN(cr.BottomRight) OrElse Double.IsInfinity(cr.TopLeft) OrElse Double.IsInfinity(cr.TopRight) OrElse Double.IsInfinity(cr.BottomLeft) OrElse Double.IsInfinity(cr.BottomRight))
  25. End Function
  26. Private Const ShadowDepth As Double = 5
  27. Protected Overrides Sub OnRender(drawingContext As DrawingContext)
  28. Dim cornerRadius As CornerRadius = Me.CornerRadius
  29. Dim shadowBounds As New Rect(New Point(ShadowDepth, ShadowDepth), New Size(RenderSize.Width, RenderSize.Height))
  30. Dim color As Color = Me.Color
  31. If shadowBounds.Width > 0 AndAlso shadowBounds.Height > 0 AndAlso color.A > 0 Then
  32. Dim centerWidth As Double = shadowBounds.Right - shadowBounds.Left - 2 * ShadowDepth
  33. Dim centerHeight As Double = shadowBounds.Bottom - shadowBounds.Top - 2 * ShadowDepth
  34. Dim maxRadius As Double = Math.Min(centerWidth * 0.5, centerHeight * 0.5)
  35. cornerRadius.TopLeft = Math.Min(cornerRadius.TopLeft, maxRadius)
  36. cornerRadius.TopRight = Math.Min(cornerRadius.TopRight, maxRadius)
  37. cornerRadius.BottomLeft = Math.Min(cornerRadius.BottomLeft, maxRadius)
  38. cornerRadius.BottomRight = Math.Min(cornerRadius.BottomRight, maxRadius)
  39. Dim brushes As Brush() = GetBrushes(color, cornerRadius)
  40. Dim centerTop As Double = shadowBounds.Top + ShadowDepth
  41. Dim centerLeft As Double = shadowBounds.Left + ShadowDepth
  42. Dim centerRight As Double = shadowBounds.Right - ShadowDepth
  43. Dim centerBottom As Double = shadowBounds.Bottom - ShadowDepth
  44. Dim guidelineSetX As Double() = New Double() {centerLeft, centerLeft + cornerRadius.TopLeft, centerRight - cornerRadius.TopRight, centerLeft + cornerRadius.BottomLeft, centerRight - cornerRadius.BottomRight, centerRight}
  45. Dim guidelineSetY As Double() = New Double() {centerTop, centerTop + cornerRadius.TopLeft, centerTop + cornerRadius.TopRight, centerBottom - cornerRadius.BottomLeft, centerBottom - cornerRadius.BottomRight, centerBottom}
  46. drawingContext.PushGuidelineSet(New GuidelineSet(guidelineSetX, guidelineSetY))
  47. cornerRadius.TopLeft += ShadowDepth
  48. cornerRadius.TopRight += ShadowDepth
  49. cornerRadius.BottomLeft += ShadowDepth
  50. cornerRadius.BottomRight += ShadowDepth
  51. Dim topLeft As New Rect(shadowBounds.Left, shadowBounds.Top, cornerRadius.TopLeft, cornerRadius.TopLeft)
  52. drawingContext.DrawRectangle(brushes(Placement.TopLeft), Nothing, topLeft)
  53. Dim topWidth As Double = guidelineSetX(2) - guidelineSetX(1)
  54. If topWidth > 0 Then
  55. Dim top As New Rect(guidelineSetX(1), shadowBounds.Top, topWidth, ShadowDepth)
  56. drawingContext.DrawRectangle(brushes(Placement.Top), Nothing, top)
  57. End If
  58. Dim topRight As New Rect(guidelineSetX(2), shadowBounds.Top, cornerRadius.TopRight, cornerRadius.TopRight)
  59. drawingContext.DrawRectangle(brushes(Placement.TopRight), Nothing, topRight)
  60. Dim leftHeight As Double = guidelineSetY(3) - guidelineSetY(1)
  61. If leftHeight > 0 Then
  62. Dim left As New Rect(shadowBounds.Left, guidelineSetY(1), ShadowDepth, leftHeight)
  63. drawingContext.DrawRectangle(brushes(Placement.Left), Nothing, left)
  64. End If
  65. Dim rightHeight As Double = guidelineSetY(4) - guidelineSetY(2)
  66. If rightHeight > 0 Then
  67. Dim right As New Rect(guidelineSetX(5), guidelineSetY(2), ShadowDepth, rightHeight)
  68. drawingContext.DrawRectangle(brushes(Placement.Right), Nothing, right)
  69. End If
  70. Dim bottomLeft As New Rect(shadowBounds.Left, guidelineSetY(3), cornerRadius.BottomLeft, cornerRadius.BottomLeft)
  71. drawingContext.DrawRectangle(brushes(Placement.BottomLeft), Nothing, bottomLeft)
  72. Dim bottomWidth As Double = guidelineSetX(4) - guidelineSetX(3)
  73. If bottomWidth > 0 Then
  74. Dim bottom As New Rect(guidelineSetX(3), guidelineSetY(5), bottomWidth, ShadowDepth)
  75. drawingContext.DrawRectangle(brushes(Placement.Bottom), Nothing, bottom)
  76. End If
  77. Dim bottomRight As New Rect(guidelineSetX(4), guidelineSetY(4), cornerRadius.BottomRight, cornerRadius.BottomRight)
  78. drawingContext.DrawRectangle(brushes(Placement.BottomRight), Nothing, bottomRight)
  79. If cornerRadius.TopLeft = ShadowDepth AndAlso cornerRadius.TopLeft = cornerRadius.TopRight AndAlso cornerRadius.TopLeft = cornerRadius.BottomLeft AndAlso cornerRadius.TopLeft = cornerRadius.BottomRight Then
  80. Dim center As New Rect(guidelineSetX(0), guidelineSetY(0), centerWidth, centerHeight)
  81. drawingContext.DrawRectangle(brushes(Placement.Center), Nothing, center)
  82. Else
  83. Dim figure As New PathFigure()
  84. If cornerRadius.TopLeft > ShadowDepth Then
  85. figure.StartPoint = New Point(guidelineSetX(1), guidelineSetY(0))
  86. figure.Segments.Add(New LineSegment(New Point(guidelineSetX(1), guidelineSetY(1)), True))
  87. figure.Segments.Add(New LineSegment(New Point(guidelineSetX(0), guidelineSetY(1)), True))
  88. Else
  89. figure.StartPoint = New Point(guidelineSetX(0), guidelineSetY(0))
  90. End If
  91. If cornerRadius.BottomLeft > ShadowDepth Then
  92. figure.Segments.Add(New LineSegment(New Point(guidelineSetX(0), guidelineSetY(3)), True))
  93. figure.Segments.Add(New LineSegment(New Point(guidelineSetX(3), guidelineSetY(3)), True))
  94. figure.Segments.Add(New LineSegment(New Point(guidelineSetX(3), guidelineSetY(5)), True))
  95. Else
  96. figure.Segments.Add(New LineSegment(New Point(guidelineSetX(0), guidelineSetY(5)), True))
  97. End If
  98. If cornerRadius.BottomRight > ShadowDepth Then
  99. figure.Segments.Add(New LineSegment(New Point(guidelineSetX(4), guidelineSetY(5)), True))
  100. figure.Segments.Add(New LineSegment(New Point(guidelineSetX(4), guidelineSetY(4)), True))
  101. figure.Segments.Add(New LineSegment(New Point(guidelineSetX(5), guidelineSetY(4)), True))
  102. Else
  103. figure.Segments.Add(New LineSegment(New Point(guidelineSetX(5), guidelineSetY(5)), True))
  104. End If
  105. If cornerRadius.TopRight > ShadowDepth Then
  106. figure.Segments.Add(New LineSegment(New Point(guidelineSetX(5), guidelineSetY(2)), True))
  107. figure.Segments.Add(New LineSegment(New Point(guidelineSetX(2), guidelineSetY(2)), True))
  108. figure.Segments.Add(New LineSegment(New Point(guidelineSetX(2), guidelineSetY(0)), True))
  109. Else
  110. figure.Segments.Add(New LineSegment(New Point(guidelineSetX(5), guidelineSetY(0)), True))
  111. End If
  112. figure.IsClosed = True
  113. figure.Freeze()
  114. Dim geometry As New PathGeometry()
  115. geometry.Figures.Add(figure)
  116. geometry.Freeze()
  117. drawingContext.DrawGeometry(brushes(Placement.Center), Nothing, geometry)
  118. End If
  119. drawingContext.Pop()
  120. End If
  121. End Sub
  122. Private Enum Placement
  123. TopLeft = 0
  124. Top = 1
  125. TopRight = 2
  126. Left = 3
  127. Center = 4
  128. Right = 5
  129. BottomLeft = 6
  130. Bottom = 7
  131. BottomRight = 8
  132. End Enum
  133. Private Shared _commonBrushes As Brush()
  134. Private Shared _commonCornerRadius As CornerRadius
  135. Private Shared ReadOnly _resourceAccess As New Object()
  136. Private _brushes As Brush()
  137. Private Shared Sub ClearBrushes(o As DependencyObject, e As DependencyPropertyChangedEventArgs)
  138. CType(o, SystemDropShadowChrome)._brushes = Nothing
  139. End Sub
  140. Private Shared Function CreateStops(c As Color, cornerRadius As Double) As GradientStopCollection
  141. Dim gradientScale As Double = 1 / (cornerRadius + ShadowDepth)
  142. Dim gsc As New GradientStopCollection From {
  143. New GradientStop(c, (0.5 + cornerRadius) * gradientScale)
  144. }
  145. Dim stopColor As Color = c
  146. stopColor.A = CByte(0.74336 * c.A)
  147. gsc.Add(New GradientStop(stopColor, (1.5 + cornerRadius) * gradientScale))
  148. stopColor.A = CByte(0.38053 * c.A)
  149. gsc.Add(New GradientStop(stopColor, (2.5 + cornerRadius) * gradientScale))
  150. stopColor.A = CByte(0.12389 * c.A)
  151. gsc.Add(New GradientStop(stopColor, (3.5 + cornerRadius) * gradientScale))
  152. stopColor.A = CByte(0.02654 * c.A)
  153. gsc.Add(New GradientStop(stopColor, (4.5 + cornerRadius) * gradientScale))
  154. stopColor.A = 0
  155. gsc.Add(New GradientStop(stopColor, (5 + cornerRadius) * gradientScale))
  156. gsc.Freeze()
  157. Return gsc
  158. End Function
  159. Private Shared Function CreateBrushes(c As Color, cornerRadius As CornerRadius) As Brush()
  160. Dim brushes As Brush() = New Brush(8) {}
  161. brushes(Placement.Center) = New SolidColorBrush(c)
  162. brushes(Placement.Center).Freeze()
  163. Dim sideStops As GradientStopCollection = CreateStops(c, 0)
  164. Dim top As New LinearGradientBrush(sideStops, New Point(0, 1), New Point(0, 0))
  165. top.Freeze()
  166. brushes(Placement.Top) = top
  167. Dim left As New LinearGradientBrush(sideStops, New Point(1, 0), New Point(0, 0))
  168. left.Freeze()
  169. brushes(Placement.Left) = left
  170. Dim right As New LinearGradientBrush(sideStops, New Point(0, 0), New Point(1, 0))
  171. right.Freeze()
  172. brushes(Placement.Right) = right
  173. Dim bottom As New LinearGradientBrush(sideStops, New Point(0, 0), New Point(0, 1))
  174. bottom.Freeze()
  175. brushes(Placement.Bottom) = bottom
  176. Dim topLeftStops As GradientStopCollection
  177. If cornerRadius.TopLeft = 0 Then
  178. topLeftStops = sideStops
  179. Else
  180. topLeftStops = CreateStops(c, cornerRadius.TopLeft)
  181. End If
  182. Dim topLeft As New RadialGradientBrush(topLeftStops) With {
  183. .RadiusX = 1,
  184. .RadiusY = 1,
  185. .Center = New Point(1, 1),
  186. .GradientOrigin = New Point(1, 1)
  187. }
  188. topLeft.Freeze()
  189. brushes(Placement.TopLeft) = topLeft
  190. Dim topRightStops As GradientStopCollection
  191. If cornerRadius.TopRight = 0 Then
  192. topRightStops = sideStops
  193. ElseIf cornerRadius.TopRight = cornerRadius.TopLeft Then
  194. topRightStops = topLeftStops
  195. Else
  196. topRightStops = CreateStops(c, cornerRadius.TopRight)
  197. End If
  198. Dim topRight As New RadialGradientBrush(topRightStops) With {
  199. .RadiusX = 1,
  200. .RadiusY = 1,
  201. .Center = New Point(0, 1),
  202. .GradientOrigin = New Point(0, 1)
  203. }
  204. topRight.Freeze()
  205. brushes(Placement.TopRight) = topRight
  206. Dim bottomLeftStops As GradientStopCollection
  207. If cornerRadius.BottomLeft = 0 Then
  208. bottomLeftStops = sideStops
  209. ElseIf cornerRadius.BottomLeft = cornerRadius.TopLeft Then
  210. bottomLeftStops = topLeftStops
  211. ElseIf cornerRadius.BottomLeft = cornerRadius.TopRight Then
  212. bottomLeftStops = topRightStops
  213. Else
  214. bottomLeftStops = CreateStops(c, cornerRadius.BottomLeft)
  215. End If
  216. Dim bottomLeft As New RadialGradientBrush(bottomLeftStops) With {
  217. .RadiusX = 1,
  218. .RadiusY = 1,
  219. .Center = New Point(1, 0),
  220. .GradientOrigin = New Point(1, 0)
  221. }
  222. bottomLeft.Freeze()
  223. brushes(Placement.BottomLeft) = bottomLeft
  224. Dim bottomRightStops As GradientStopCollection
  225. If cornerRadius.BottomRight = 0 Then
  226. bottomRightStops = sideStops
  227. ElseIf cornerRadius.BottomRight = cornerRadius.TopLeft Then
  228. bottomRightStops = topLeftStops
  229. ElseIf cornerRadius.BottomRight = cornerRadius.TopRight Then
  230. bottomRightStops = topRightStops
  231. ElseIf cornerRadius.BottomRight = cornerRadius.BottomLeft Then
  232. bottomRightStops = bottomLeftStops
  233. Else
  234. bottomRightStops = CreateStops(c, cornerRadius.BottomRight)
  235. End If
  236. Dim bottomRight As New RadialGradientBrush(bottomRightStops) With {
  237. .RadiusX = 1,
  238. .RadiusY = 1,
  239. .Center = New Point(0, 0),
  240. .GradientOrigin = New Point(0, 0)
  241. }
  242. bottomRight.Freeze()
  243. brushes(Placement.BottomRight) = bottomRight
  244. Return brushes
  245. End Function
  246. Private Function GetBrushes(c As Color, cornerRadius As CornerRadius) As Brush()
  247. If _commonBrushes Is Nothing Then
  248. SyncLock _resourceAccess
  249. If _commonBrushes Is Nothing Then
  250. _commonBrushes = CreateBrushes(c, cornerRadius)
  251. _commonCornerRadius = cornerRadius
  252. End If
  253. End SyncLock
  254. End If
  255. If c = CType(_commonBrushes(Placement.Center), SolidColorBrush).Color AndAlso cornerRadius = _commonCornerRadius Then
  256. _brushes = Nothing
  257. Return _commonBrushes
  258. ElseIf _brushes Is Nothing Then
  259. _brushes = CreateBrushes(c, cornerRadius)
  260. End If
  261. Return _brushes
  262. End Function
  263. End Class