DashedBorder.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  1. using System;
  2. using System.Windows;
  3. using System.Windows.Controls;
  4. using System.Windows.Media;
  5. using HandyControl.Data;
  6. using HandyControl.Expression.Drawing;
  7. using HandyControl.Tools;
  8. using HandyControl.Tools.Extension;
  9. namespace HandyControl.Controls;
  10. public class DashedBorder : Decorator
  11. {
  12. private bool _useComplexRenderCodePath;
  13. private Pen GeometryPenCache { get; set; }
  14. private Pen LeftPenCache { get; set; }
  15. private Pen RightPenCache { get; set; }
  16. private Pen TopPenCache { get; set; }
  17. private Pen BottomPenCache { get; set; }
  18. private StreamGeometry BackgroundGeometryCache { get; set; }
  19. private StreamGeometry BorderGeometryCache { get; set; }
  20. private static void OnClearPenCache(DependencyObject d, DependencyPropertyChangedEventArgs e)
  21. {
  22. var border = (DashedBorder) d;
  23. border.LeftPenCache = null;
  24. border.RightPenCache = null;
  25. border.TopPenCache = null;
  26. border.BottomPenCache = null;
  27. border.GeometryPenCache = null;
  28. }
  29. public static readonly DependencyProperty BorderThicknessProperty = DependencyProperty.Register(
  30. nameof(BorderThickness), typeof(Thickness), typeof(DashedBorder), new FrameworkPropertyMetadata(default(Thickness),
  31. FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender,
  32. OnClearPenCache));
  33. public Thickness BorderThickness
  34. {
  35. get => (Thickness) GetValue(BorderThicknessProperty);
  36. set => SetValue(BorderThicknessProperty, value);
  37. }
  38. public static readonly DependencyProperty BorderDashThicknessProperty = DependencyProperty.Register(
  39. nameof(BorderDashThickness), typeof(double), typeof(DashedBorder), new FrameworkPropertyMetadata(ValueBoxes.Double0Box,
  40. FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender,
  41. OnClearPenCache));
  42. public double BorderDashThickness
  43. {
  44. get => (double) GetValue(BorderDashThicknessProperty);
  45. set => SetValue(BorderDashThicknessProperty, value);
  46. }
  47. public static readonly DependencyProperty PaddingProperty = DependencyProperty.Register(
  48. nameof(Padding), typeof(Thickness), typeof(DashedBorder), new FrameworkPropertyMetadata(default(Thickness),
  49. FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender));
  50. public Thickness Padding
  51. {
  52. get => (Thickness) GetValue(PaddingProperty);
  53. set => SetValue(PaddingProperty, value);
  54. }
  55. public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register(
  56. nameof(CornerRadius), typeof(CornerRadius), typeof(DashedBorder), new FrameworkPropertyMetadata(default(CornerRadius),
  57. FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender));
  58. public CornerRadius CornerRadius
  59. {
  60. get => (CornerRadius) GetValue(CornerRadiusProperty);
  61. set => SetValue(CornerRadiusProperty, value);
  62. }
  63. public static readonly DependencyProperty BorderBrushProperty = DependencyProperty.Register(
  64. nameof(BorderBrush), typeof(Brush), typeof(DashedBorder), new FrameworkPropertyMetadata(default(Brush),
  65. FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
  66. OnClearPenCache));
  67. public Brush BorderBrush
  68. {
  69. get => (Brush) GetValue(BorderBrushProperty);
  70. set => SetValue(BorderBrushProperty, value);
  71. }
  72. public static readonly DependencyProperty BackgroundProperty = DependencyProperty.Register(
  73. nameof(Background), typeof(Brush), typeof(DashedBorder), new FrameworkPropertyMetadata(default(Brush),
  74. FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
  75. OnClearPenCache));
  76. public Brush Background
  77. {
  78. get => (Brush) GetValue(BackgroundProperty);
  79. set => SetValue(BackgroundProperty, value);
  80. }
  81. public static readonly DependencyProperty BorderDashArrayProperty = DependencyProperty.Register(
  82. nameof(BorderDashArray), typeof(DoubleCollection), typeof(DashedBorder), new FrameworkPropertyMetadata(default(DoubleCollection),
  83. FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender,
  84. OnClearPenCache));
  85. public DoubleCollection BorderDashArray
  86. {
  87. get => (DoubleCollection) GetValue(BorderDashArrayProperty);
  88. set => SetValue(BorderDashArrayProperty, value);
  89. }
  90. public static readonly DependencyProperty BorderDashCapProperty = DependencyProperty.Register(
  91. nameof(BorderDashCap), typeof(PenLineCap), typeof(DashedBorder), new FrameworkPropertyMetadata(default(PenLineCap),
  92. FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender,
  93. OnClearPenCache));
  94. public PenLineCap BorderDashCap
  95. {
  96. get => (PenLineCap) GetValue(BorderDashCapProperty);
  97. set => SetValue(BorderDashCapProperty, value);
  98. }
  99. public static readonly DependencyProperty BorderDashOffsetProperty = DependencyProperty.Register(
  100. nameof(BorderDashOffset), typeof(double), typeof(DashedBorder), new FrameworkPropertyMetadata(ValueBoxes.Double0Box,
  101. FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender,
  102. OnClearPenCache));
  103. public double BorderDashOffset
  104. {
  105. get => (double) GetValue(BorderDashOffsetProperty);
  106. set => SetValue(BorderDashOffsetProperty, value);
  107. }
  108. private static Size ConvertThickness2Size(Thickness th) => new(th.Left + th.Right, th.Top + th.Bottom);
  109. private static Rect DeflateRect(Rect rt, Thickness thick) => new(rt.Left + thick.Left,
  110. rt.Top + thick.Top,
  111. Math.Max(0.0, rt.Width - thick.Left - thick.Right),
  112. Math.Max(0.0, rt.Height - thick.Top - thick.Bottom));
  113. private static bool AreUniformCorners(CornerRadius borderRadii)
  114. {
  115. var topLeft = borderRadii.TopLeft;
  116. return MathHelper.AreClose(topLeft, borderRadii.TopRight) &&
  117. MathHelper.AreClose(topLeft, borderRadii.BottomLeft) &&
  118. MathHelper.AreClose(topLeft, borderRadii.BottomRight);
  119. }
  120. private static void GenerateGeometry(StreamGeometryContext ctx, Rect rect, in Radii radii)
  121. {
  122. var topLeft = new Point(radii.LeftTop, 0);
  123. var topRight = new Point(rect.Width - radii.RightTop, 0);
  124. var rightTop = new Point(rect.Width, radii.TopRight);
  125. var rightBottom = new Point(rect.Width, rect.Height - radii.BottomRight);
  126. var bottomRight = new Point(rect.Width - radii.RightBottom, rect.Height);
  127. var bottomLeft = new Point(radii.LeftBottom, rect.Height);
  128. var leftBottom = new Point(0, rect.Height - radii.BottomLeft);
  129. var leftTop = new Point(0, radii.TopLeft);
  130. // top edge
  131. if (topLeft.X > topRight.X)
  132. {
  133. var v = radii.LeftTop / (radii.LeftTop + radii.RightTop) * rect.Width;
  134. topLeft.X = v;
  135. topRight.X = v;
  136. }
  137. // right edge
  138. if (rightTop.Y > rightBottom.Y)
  139. {
  140. var v = radii.TopRight / (radii.TopRight + radii.BottomRight) * rect.Height;
  141. rightTop.Y = v;
  142. rightBottom.Y = v;
  143. }
  144. // bottom edge
  145. if (bottomRight.X < bottomLeft.X)
  146. {
  147. var v = radii.LeftBottom / (radii.LeftBottom + radii.RightBottom) * rect.Width;
  148. bottomRight.X = v;
  149. bottomLeft.X = v;
  150. }
  151. // left edge
  152. if (leftBottom.Y < leftTop.Y)
  153. {
  154. var v = radii.TopLeft / (radii.TopLeft + radii.BottomLeft) * rect.Height;
  155. leftBottom.Y = v;
  156. leftTop.Y = v;
  157. }
  158. // add on offsets
  159. var offset = new Vector(rect.TopLeft.X, rect.TopLeft.Y);
  160. topLeft += offset;
  161. topRight += offset;
  162. rightTop += offset;
  163. rightBottom += offset;
  164. bottomRight += offset;
  165. bottomLeft += offset;
  166. leftBottom += offset;
  167. leftTop += offset;
  168. // create the border geometry
  169. ctx.BeginFigure(topLeft, true, true);
  170. // Top line
  171. ctx.LineTo(topRight, true, false);
  172. // Upper-right corner
  173. var radiusX = rect.TopRight.X - topRight.X;
  174. var radiusY = rightTop.Y - rect.TopRight.Y;
  175. if (!MathHelper.IsZero(radiusX) || !MathHelper.IsZero(radiusY))
  176. {
  177. ctx.ArcTo(rightTop, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, true, false);
  178. }
  179. // Right line
  180. ctx.LineTo(rightBottom, true, false);
  181. // Lower-right corner
  182. radiusX = rect.BottomRight.X - bottomRight.X;
  183. radiusY = rect.BottomRight.Y - rightBottom.Y;
  184. if (!MathHelper.IsZero(radiusX) || !MathHelper.IsZero(radiusY))
  185. {
  186. ctx.ArcTo(bottomRight, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, true, false);
  187. }
  188. // Bottom line
  189. ctx.LineTo(bottomLeft, true, false);
  190. // Lower-left corner
  191. radiusX = bottomLeft.X - rect.BottomLeft.X;
  192. radiusY = rect.BottomLeft.Y - leftBottom.Y;
  193. if (!MathHelper.IsZero(radiusX) || !MathHelper.IsZero(radiusY))
  194. {
  195. ctx.ArcTo(leftBottom, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, true, false);
  196. }
  197. // Left line
  198. ctx.LineTo(leftTop, true, false);
  199. // Upper-left corner
  200. radiusX = topLeft.X - rect.TopLeft.X;
  201. radiusY = leftTop.Y - rect.TopLeft.Y;
  202. if (!MathHelper.IsZero(radiusX) || !MathHelper.IsZero(radiusY))
  203. {
  204. ctx.ArcTo(topLeft, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, true, false);
  205. }
  206. }
  207. protected override Size MeasureOverride(Size constraint)
  208. {
  209. var child = Child;
  210. var borderThickness = BorderThickness;
  211. var padding = Padding;
  212. if (UseLayoutRounding)
  213. {
  214. var dpiScaleX = DpiHelper.DeviceDpiX;
  215. var dpiScaleY = DpiHelper.DeviceDpiY;
  216. borderThickness = new Thickness(
  217. DpiHelper.RoundLayoutValue(borderThickness.Left, dpiScaleX),
  218. DpiHelper.RoundLayoutValue(borderThickness.Top, dpiScaleY),
  219. DpiHelper.RoundLayoutValue(borderThickness.Right, dpiScaleX),
  220. DpiHelper.RoundLayoutValue(borderThickness.Bottom, dpiScaleY));
  221. }
  222. var borderSize = ConvertThickness2Size(borderThickness);
  223. var paddingSize = ConvertThickness2Size(padding);
  224. var mySize = new Size();
  225. if (child != null)
  226. {
  227. var combined = new Size(borderSize.Width + paddingSize.Width, borderSize.Height + paddingSize.Height);
  228. var childConstraint = new Size(Math.Max(0.0, constraint.Width - combined.Width), Math.Max(0.0, constraint.Height - combined.Height));
  229. child.Measure(childConstraint);
  230. var childSize = child.DesiredSize;
  231. mySize.Width = childSize.Width + combined.Width;
  232. mySize.Height = childSize.Height + combined.Height;
  233. }
  234. else
  235. {
  236. mySize = new Size(borderSize.Width + paddingSize.Width, borderSize.Height + paddingSize.Height);
  237. }
  238. return mySize;
  239. }
  240. protected override Size ArrangeOverride(Size arrangeSize)
  241. {
  242. var borderThickness = BorderThickness;
  243. if (UseLayoutRounding)
  244. {
  245. var dpiScaleX = DpiHelper.DeviceDpiX;
  246. var dpiScaleY = DpiHelper.DeviceDpiY;
  247. borderThickness = new Thickness(
  248. DpiHelper.RoundLayoutValue(borderThickness.Left, dpiScaleX),
  249. DpiHelper.RoundLayoutValue(borderThickness.Top, dpiScaleY),
  250. DpiHelper.RoundLayoutValue(borderThickness.Right, dpiScaleX),
  251. DpiHelper.RoundLayoutValue(borderThickness.Bottom, dpiScaleY));
  252. }
  253. var boundRect = new Rect(arrangeSize);
  254. var innerRect = DeflateRect(boundRect, borderThickness);
  255. var child = Child;
  256. if (child != null)
  257. {
  258. var padding = Padding;
  259. var childRect = DeflateRect(innerRect, padding);
  260. child.Arrange(childRect);
  261. }
  262. var radii = CornerRadius;
  263. var borderBrush = BorderBrush;
  264. var uniformCorners = AreUniformCorners(radii);
  265. _useComplexRenderCodePath = !uniformCorners;
  266. if (!_useComplexRenderCodePath && borderBrush != null)
  267. {
  268. _useComplexRenderCodePath = !MathHelper.IsZero(radii.TopLeft) && !borderThickness.IsUniform();
  269. }
  270. if (_useComplexRenderCodePath)
  271. {
  272. var innerRadii = new Radii(radii, borderThickness, false);
  273. StreamGeometry backgroundGeometry = null;
  274. if (!MathHelper.IsZero(innerRect.Width) && !MathHelper.IsZero(innerRect.Height))
  275. {
  276. backgroundGeometry = new StreamGeometry();
  277. using (var ctx = backgroundGeometry.Open())
  278. {
  279. GenerateGeometry(ctx, innerRect, innerRadii);
  280. }
  281. backgroundGeometry.Freeze();
  282. BackgroundGeometryCache = backgroundGeometry;
  283. }
  284. else
  285. {
  286. BackgroundGeometryCache = null;
  287. }
  288. if (!MathHelper.IsZero(boundRect.Width) && !MathHelper.IsZero(boundRect.Height))
  289. {
  290. var outerRadii = new Radii(radii, borderThickness, true);
  291. var borderGeometry = new StreamGeometry();
  292. using (var ctx = borderGeometry.Open())
  293. {
  294. GenerateGeometry(ctx, boundRect, outerRadii);
  295. if (backgroundGeometry != null)
  296. {
  297. GenerateGeometry(ctx, innerRect, innerRadii);
  298. }
  299. }
  300. borderGeometry.Freeze();
  301. BorderGeometryCache = borderGeometry;
  302. }
  303. else
  304. {
  305. BorderGeometryCache = null;
  306. }
  307. }
  308. else
  309. {
  310. BackgroundGeometryCache = null;
  311. BorderGeometryCache = null;
  312. }
  313. return arrangeSize;
  314. }
  315. protected override void OnRender(DrawingContext drawingContext)
  316. {
  317. var background = Background;
  318. var borderBrush = BorderBrush;
  319. var useLayoutRounding = UseLayoutRounding;
  320. if (_useComplexRenderCodePath)
  321. {
  322. var borderGeometry = BorderGeometryCache;
  323. if (borderGeometry != null && borderBrush != null)
  324. {
  325. var pen = GeometryPenCache;
  326. if (pen == null)
  327. {
  328. pen = new Pen
  329. {
  330. Brush = borderBrush,
  331. Thickness = BorderDashThickness,
  332. DashCap = BorderDashCap,
  333. DashStyle = new DashStyle(BorderDashArray, BorderDashOffset)
  334. };
  335. if (borderBrush.IsFrozen)
  336. {
  337. pen.Freeze();
  338. }
  339. GeometryPenCache = pen;
  340. }
  341. drawingContext.DrawGeometry(null, pen, borderGeometry);
  342. }
  343. var backgroundGeometry = BackgroundGeometryCache;
  344. if (backgroundGeometry != null && background != null)
  345. {
  346. drawingContext.DrawGeometry(background, null, backgroundGeometry);
  347. }
  348. }
  349. else
  350. {
  351. var dpiScaleX = DpiHelper.DeviceDpiX;
  352. var dpiScaleY = DpiHelper.DeviceDpiY;
  353. var borderThickness = BorderThickness;
  354. var cornerRadius = CornerRadius;
  355. var outerCornerRadius = cornerRadius.TopLeft;
  356. var roundedCorners = !MathHelper.IsZero(outerCornerRadius);
  357. if (!borderThickness.IsZero() && borderBrush != null)
  358. {
  359. var pen = LeftPenCache;
  360. if (pen == null)
  361. {
  362. pen = new Pen
  363. {
  364. Brush = borderBrush,
  365. Thickness = useLayoutRounding
  366. ? DpiHelper.RoundLayoutValue(borderThickness.Left, dpiScaleX)
  367. : borderThickness.Left,
  368. DashCap = BorderDashCap,
  369. DashStyle = new DashStyle(BorderDashArray, BorderDashOffset)
  370. };
  371. if (borderBrush.IsFrozen)
  372. {
  373. pen.Freeze();
  374. }
  375. LeftPenCache = pen;
  376. }
  377. double halfThickness;
  378. var renderSize = RenderSize;
  379. if (borderThickness.IsUniform())
  380. {
  381. halfThickness = pen.Thickness * 0.5;
  382. var rect = new Rect(new Point(halfThickness, halfThickness), new Point(renderSize.Width - halfThickness, renderSize.Height - halfThickness));
  383. if (roundedCorners)
  384. {
  385. drawingContext.DrawRoundedRectangle(null, pen, rect, outerCornerRadius, outerCornerRadius);
  386. }
  387. else
  388. {
  389. drawingContext.DrawRectangle(null, pen, rect);
  390. }
  391. }
  392. else
  393. {
  394. if (MathHelper.GreaterThan(borderThickness.Left, 0))
  395. {
  396. halfThickness = pen.Thickness * 0.5;
  397. drawingContext.DrawLine(pen, new Point(halfThickness, 0), new Point(halfThickness, renderSize.Height));
  398. }
  399. if (MathHelper.GreaterThan(borderThickness.Right, 0))
  400. {
  401. pen = RightPenCache;
  402. if (pen == null)
  403. {
  404. pen = new Pen
  405. {
  406. Brush = borderBrush,
  407. Thickness = useLayoutRounding
  408. ? DpiHelper.RoundLayoutValue(borderThickness.Right, dpiScaleX)
  409. : borderThickness.Right,
  410. DashCap = BorderDashCap,
  411. DashStyle = new DashStyle(BorderDashArray, BorderDashOffset)
  412. };
  413. if (borderBrush.IsFrozen)
  414. {
  415. pen.Freeze();
  416. }
  417. RightPenCache = pen;
  418. }
  419. halfThickness = pen.Thickness * 0.5;
  420. drawingContext.DrawLine(pen, new Point(renderSize.Width - halfThickness, 0), new Point(renderSize.Width - halfThickness, renderSize.Height));
  421. }
  422. if (MathHelper.GreaterThan(borderThickness.Top, 0))
  423. {
  424. pen = TopPenCache;
  425. if (pen == null)
  426. {
  427. pen = new Pen
  428. {
  429. Brush = borderBrush,
  430. Thickness = useLayoutRounding
  431. ? DpiHelper.RoundLayoutValue(borderThickness.Top, dpiScaleY)
  432. : borderThickness.Top,
  433. DashCap = BorderDashCap,
  434. DashStyle = new DashStyle(BorderDashArray, BorderDashOffset)
  435. };
  436. if (borderBrush.IsFrozen)
  437. {
  438. pen.Freeze();
  439. }
  440. TopPenCache = pen;
  441. }
  442. halfThickness = pen.Thickness * 0.5;
  443. drawingContext.DrawLine(pen, new Point(0, halfThickness), new Point(renderSize.Width, halfThickness));
  444. }
  445. if (MathHelper.GreaterThan(borderThickness.Bottom, 0))
  446. {
  447. pen = BottomPenCache;
  448. if (pen == null)
  449. {
  450. pen = new Pen
  451. {
  452. Brush = borderBrush,
  453. Thickness = useLayoutRounding
  454. ? DpiHelper.RoundLayoutValue(borderThickness.Bottom, dpiScaleY)
  455. : borderThickness.Bottom,
  456. DashCap = BorderDashCap,
  457. DashStyle = new DashStyle(BorderDashArray, BorderDashOffset)
  458. };
  459. if (borderBrush.IsFrozen)
  460. {
  461. pen.Freeze();
  462. }
  463. BottomPenCache = pen;
  464. }
  465. halfThickness = pen.Thickness * 0.5;
  466. drawingContext.DrawLine(pen, new Point(0, renderSize.Height - halfThickness), new Point(renderSize.Width, renderSize.Height - halfThickness));
  467. }
  468. }
  469. }
  470. if (background != null)
  471. {
  472. Point ptTL, ptBR;
  473. if (useLayoutRounding)
  474. {
  475. ptTL = new Point(DpiHelper.RoundLayoutValue(borderThickness.Left, dpiScaleX), DpiHelper.RoundLayoutValue(borderThickness.Top, dpiScaleY));
  476. ptBR = new Point(RenderSize.Width - DpiHelper.RoundLayoutValue(borderThickness.Right, dpiScaleX),
  477. RenderSize.Height - DpiHelper.RoundLayoutValue(borderThickness.Bottom, dpiScaleY));
  478. }
  479. else
  480. {
  481. ptTL = new Point(borderThickness.Left, borderThickness.Top);
  482. ptBR = new Point(RenderSize.Width - borderThickness.Right, RenderSize.Height - borderThickness.Bottom);
  483. }
  484. if (ptBR.X > ptTL.X && ptBR.Y > ptTL.Y)
  485. {
  486. if (roundedCorners)
  487. {
  488. var innerRadii = new Radii(cornerRadius, borderThickness, false);
  489. var innerCornerRadius = innerRadii.TopLeft;
  490. drawingContext.DrawRoundedRectangle(background, null, new Rect(ptTL, ptBR), innerCornerRadius, innerCornerRadius);
  491. }
  492. else
  493. {
  494. drawingContext.DrawRectangle(background, null, new Rect(ptTL, ptBR));
  495. }
  496. }
  497. }
  498. }
  499. }
  500. private readonly struct Radii
  501. {
  502. internal Radii(CornerRadius radii, Thickness borders, bool outer)
  503. {
  504. var left = 0.5 * borders.Left;
  505. var top = 0.5 * borders.Top;
  506. var right = 0.5 * borders.Right;
  507. var bottom = 0.5 * borders.Bottom;
  508. if (outer)
  509. {
  510. if (MathHelper.IsZero(radii.TopLeft))
  511. {
  512. LeftTop = TopLeft = 0.0;
  513. }
  514. else
  515. {
  516. LeftTop = radii.TopLeft + left;
  517. TopLeft = radii.TopLeft + top;
  518. }
  519. if (MathHelper.IsZero(radii.TopRight))
  520. {
  521. TopRight = RightTop = 0.0;
  522. }
  523. else
  524. {
  525. TopRight = radii.TopRight + top;
  526. RightTop = radii.TopRight + right;
  527. }
  528. if (MathHelper.IsZero(radii.BottomRight))
  529. {
  530. RightBottom = BottomRight = 0.0;
  531. }
  532. else
  533. {
  534. RightBottom = radii.BottomRight + right;
  535. BottomRight = radii.BottomRight + bottom;
  536. }
  537. if (MathHelper.IsZero(radii.BottomLeft))
  538. {
  539. BottomLeft = LeftBottom = 0.0;
  540. }
  541. else
  542. {
  543. BottomLeft = radii.BottomLeft + bottom;
  544. LeftBottom = radii.BottomLeft + left;
  545. }
  546. }
  547. else
  548. {
  549. LeftTop = Math.Max(0.0, radii.TopLeft - left);
  550. TopLeft = Math.Max(0.0, radii.TopLeft - top);
  551. TopRight = Math.Max(0.0, radii.TopRight - top);
  552. RightTop = Math.Max(0.0, radii.TopRight - right);
  553. RightBottom = Math.Max(0.0, radii.BottomRight - right);
  554. BottomRight = Math.Max(0.0, radii.BottomRight - bottom);
  555. BottomLeft = Math.Max(0.0, radii.BottomLeft - bottom);
  556. LeftBottom = Math.Max(0.0, radii.BottomLeft - left);
  557. }
  558. }
  559. internal readonly double LeftTop;
  560. internal readonly double TopLeft;
  561. internal readonly double TopRight;
  562. internal readonly double RightTop;
  563. internal readonly double RightBottom;
  564. internal readonly double BottomRight;
  565. internal readonly double BottomLeft;
  566. internal readonly double LeftBottom;
  567. }
  568. }