TrackerControl.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. // --------------------------------------------------------------------------------------------------------------------
  2. // <copyright file="TrackerControl.cs" company="OxyPlot">
  3. // Copyright (c) 2020 OxyPlot contributors
  4. // </copyright>
  5. // <summary>
  6. // The tracker control.
  7. // </summary>
  8. // --------------------------------------------------------------------------------------------------------------------
  9. namespace OxyPlot.Wpf
  10. {
  11. using System;
  12. using System.Windows;
  13. using System.Windows.Controls;
  14. using System.Windows.Media;
  15. using System.Windows.Shapes;
  16. /// <summary>
  17. /// The tracker control.
  18. /// </summary>
  19. public class TrackerControl : ContentControl
  20. {
  21. /// <summary>
  22. /// Identifies the <see cref="HorizontalLineVisibility"/> dependency property.
  23. /// </summary>
  24. public static readonly DependencyProperty HorizontalLineVisibilityProperty =
  25. DependencyProperty.Register(
  26. nameof(HorizontalLineVisibility),
  27. typeof(Visibility),
  28. typeof(TrackerControl),
  29. new PropertyMetadata(Visibility.Visible));
  30. /// <summary>
  31. /// Identifies the <see cref="VerticalLineVisibility"/> dependency property.
  32. /// </summary>
  33. public static readonly DependencyProperty VerticalLineVisibilityProperty =
  34. DependencyProperty.Register(
  35. nameof(VerticalLineVisibility),
  36. typeof(Visibility),
  37. typeof(TrackerControl),
  38. new PropertyMetadata(Visibility.Visible));
  39. /// <summary>
  40. /// Identifies the <see cref="LineThickness"/> dependency property.
  41. /// </summary>
  42. public static readonly DependencyProperty LineThicknessProperty = DependencyProperty.Register(
  43. nameof(LineThickness), typeof(double), typeof(TrackerControl), new PropertyMetadata(1.0));
  44. /// <summary>
  45. /// Identifies the <see cref="LineStroke"/> dependency property.
  46. /// </summary>
  47. public static readonly DependencyProperty LineStrokeProperty = DependencyProperty.Register(
  48. nameof(LineStroke), typeof(Brush), typeof(TrackerControl), new PropertyMetadata(null));
  49. /// <summary>
  50. /// Identifies the <see cref="LineExtents"/> dependency property.
  51. /// </summary>
  52. public static readonly DependencyProperty LineExtentsProperty = DependencyProperty.Register(
  53. nameof(LineExtents), typeof(OxyRect), typeof(TrackerControl), new PropertyMetadata(new OxyRect()));
  54. /// <summary>
  55. /// Identifies the <see cref="LineDashArray"/> dependency property.
  56. /// </summary>
  57. public static readonly DependencyProperty LineDashArrayProperty = DependencyProperty.Register(
  58. nameof(LineDashArray), typeof(DoubleCollection), typeof(TrackerControl), new PropertyMetadata(null));
  59. /// <summary>
  60. /// Identifies the <see cref="BorderEdgeMode"/> dependency property.
  61. /// </summary>
  62. public static readonly DependencyProperty BorderEdgeModeProperty = DependencyProperty.Register(
  63. nameof(BorderEdgeMode), typeof(EdgeMode), typeof(TrackerControl));
  64. /// <summary>
  65. /// Identifies the <see cref="ShowPointer"/> dependency property.
  66. /// </summary>
  67. public static readonly DependencyProperty ShowPointerProperty = DependencyProperty.Register(
  68. nameof(ShowPointer), typeof(bool), typeof(TrackerControl), new PropertyMetadata(true));
  69. /// <summary>
  70. /// Identifies the <see cref="CornerRadius"/> dependency property.
  71. /// </summary>
  72. public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register(
  73. nameof(CornerRadius), typeof(double), typeof(TrackerControl), new PropertyMetadata(0.0));
  74. /// <summary>
  75. /// Identifies the <see cref="Distance"/> dependency property.
  76. /// </summary>
  77. public static readonly DependencyProperty DistanceProperty = DependencyProperty.Register(
  78. nameof(Distance), typeof(double), typeof(TrackerControl), new PropertyMetadata(7.0));
  79. /// <summary>
  80. /// Identifies the <see cref="CanCenterHorizontally"/> dependency property.
  81. /// </summary>
  82. public static readonly DependencyProperty CanCenterHorizontallyProperty =
  83. DependencyProperty.Register(
  84. nameof(CanCenterHorizontally), typeof(bool), typeof(TrackerControl), new PropertyMetadata(true));
  85. /// <summary>
  86. /// Identifies the <see cref="CanCenterVertically"/> dependency property.
  87. /// </summary>
  88. public static readonly DependencyProperty CanCenterVerticallyProperty =
  89. DependencyProperty.Register(
  90. nameof(CanCenterVertically), typeof(bool), typeof(TrackerControl), new PropertyMetadata(true));
  91. /// <summary>
  92. /// Identifies the <see cref="Position"/> dependency property.
  93. /// </summary>
  94. public static readonly DependencyProperty PositionProperty = DependencyProperty.Register(
  95. nameof(Position),
  96. typeof(ScreenPoint),
  97. typeof(TrackerControl),
  98. new PropertyMetadata(new ScreenPoint(), PositionChanged));
  99. /// <summary>
  100. /// The path part string.
  101. /// </summary>
  102. private const string PartPath = "PART_Path";
  103. /// <summary>
  104. /// The content part string.
  105. /// </summary>
  106. private const string PartContent = "PART_Content";
  107. /// <summary>
  108. /// The content container part string.
  109. /// </summary>
  110. private const string PartContentcontainer = "PART_ContentContainer";
  111. /// <summary>
  112. /// The horizontal line part string.
  113. /// </summary>
  114. private const string PartHorizontalline = "PART_HorizontalLine";
  115. /// <summary>
  116. /// The vertical line part string.
  117. /// </summary>
  118. private const string PartVerticalline = "PART_VerticalLine";
  119. /// <summary>
  120. /// The content.
  121. /// </summary>
  122. private ContentPresenter content;
  123. /// <summary>
  124. /// The horizontal line.
  125. /// </summary>
  126. private Line horizontalLine;
  127. /// <summary>
  128. /// The path.
  129. /// </summary>
  130. private Path path;
  131. /// <summary>
  132. /// The content container.
  133. /// </summary>
  134. private Grid contentContainer;
  135. /// <summary>
  136. /// The vertical line.
  137. /// </summary>
  138. private Line verticalLine;
  139. /// <summary>
  140. /// Initializes static members of the <see cref = "TrackerControl" /> class.
  141. /// </summary>
  142. static TrackerControl()
  143. {
  144. DefaultStyleKeyProperty.OverrideMetadata(
  145. typeof(TrackerControl), new FrameworkPropertyMetadata(typeof(TrackerControl)));
  146. }
  147. /// <summary>
  148. /// Gets or sets BorderEdgeMode.
  149. /// </summary>
  150. public EdgeMode BorderEdgeMode
  151. {
  152. get => (EdgeMode)this.GetValue(BorderEdgeModeProperty);
  153. set => this.SetValue(BorderEdgeModeProperty, value);
  154. }
  155. /// <summary>
  156. /// Gets or sets HorizontalLineVisibility.
  157. /// </summary>
  158. public Visibility HorizontalLineVisibility
  159. {
  160. get => (Visibility)this.GetValue(HorizontalLineVisibilityProperty);
  161. set => this.SetValue(HorizontalLineVisibilityProperty, value);
  162. }
  163. /// <summary>
  164. /// Gets or sets VerticalLineVisibility.
  165. /// </summary>
  166. public Visibility VerticalLineVisibility
  167. {
  168. get => (Visibility)this.GetValue(VerticalLineVisibilityProperty);
  169. set => this.SetValue(VerticalLineVisibilityProperty, value);
  170. }
  171. /// <summary>
  172. /// Gets or sets LineThickness.
  173. /// </summary>
  174. public double LineThickness
  175. {
  176. get => (double)this.GetValue(LineThicknessProperty);
  177. set => this.SetValue(LineThicknessProperty, value);
  178. }
  179. /// <summary>
  180. /// Gets or sets LineStroke.
  181. /// </summary>
  182. public Brush LineStroke
  183. {
  184. get => (Brush)this.GetValue(LineStrokeProperty);
  185. set => this.SetValue(LineStrokeProperty, value);
  186. }
  187. /// <summary>
  188. /// Gets or sets LineExtents.
  189. /// </summary>
  190. public OxyRect LineExtents
  191. {
  192. get => (OxyRect)this.GetValue(LineExtentsProperty);
  193. set => this.SetValue(LineExtentsProperty, value);
  194. }
  195. /// <summary>
  196. /// Gets or sets LineDashArray.
  197. /// </summary>
  198. public DoubleCollection LineDashArray
  199. {
  200. get => (DoubleCollection)this.GetValue(LineDashArrayProperty);
  201. set => this.SetValue(LineDashArrayProperty, value);
  202. }
  203. /// <summary>
  204. /// Gets or sets a value indicating whether to show a 'pointer' on the border.
  205. /// </summary>
  206. public bool ShowPointer
  207. {
  208. get => (bool)this.GetValue(ShowPointerProperty);
  209. set => this.SetValue(ShowPointerProperty, value);
  210. }
  211. /// <summary>
  212. /// Gets or sets the corner radius (only used when ShowPoint=<c>false</c>).
  213. /// </summary>
  214. public double CornerRadius
  215. {
  216. get => (double)this.GetValue(CornerRadiusProperty);
  217. set => this.SetValue(CornerRadiusProperty, value);
  218. }
  219. /// <summary>
  220. /// Gets or sets the distance of the content container from the trackers Position.
  221. /// </summary>
  222. public double Distance
  223. {
  224. get => (double)this.GetValue(DistanceProperty);
  225. set => this.SetValue(DistanceProperty, value);
  226. }
  227. /// <summary>
  228. /// Gets or sets a value indicating whether the tracker can center its content box horizontally.
  229. /// </summary>
  230. public bool CanCenterHorizontally
  231. {
  232. get => (bool)this.GetValue(CanCenterHorizontallyProperty);
  233. set => this.SetValue(CanCenterHorizontallyProperty, value);
  234. }
  235. /// <summary>
  236. /// Gets or sets a value indicating whether the tracker can center its content box vertically.
  237. /// </summary>
  238. public bool CanCenterVertically
  239. {
  240. get => (bool)this.GetValue(CanCenterVerticallyProperty);
  241. set => this.SetValue(CanCenterVerticallyProperty, value);
  242. }
  243. /// <summary>
  244. /// Gets or sets Position of the tracker.
  245. /// </summary>
  246. public ScreenPoint Position
  247. {
  248. get => (ScreenPoint)this.GetValue(PositionProperty);
  249. set => this.SetValue(PositionProperty, value);
  250. }
  251. /// <summary>
  252. /// When overridden in a derived class, is invoked whenever application code or internal processes call <see cref="M:System.Windows.FrameworkElement.ApplyTemplate" />.
  253. /// </summary>
  254. public override void OnApplyTemplate()
  255. {
  256. base.OnApplyTemplate();
  257. this.path = this.GetTemplateChild(PartPath) as Path;
  258. this.content = this.GetTemplateChild(PartContent) as ContentPresenter;
  259. this.contentContainer = this.GetTemplateChild(PartContentcontainer) as Grid;
  260. this.horizontalLine = this.GetTemplateChild(PartHorizontalline) as Line;
  261. this.verticalLine = this.GetTemplateChild(PartVerticalline) as Line;
  262. if (this.contentContainer == null)
  263. {
  264. throw new InvalidOperationException($"The TrackerControl template must contain a content container with name +'{PartContentcontainer}'");
  265. }
  266. if (this.path == null)
  267. {
  268. throw new InvalidOperationException($"The TrackerControl template must contain a Path with name +'{PartPath}'");
  269. }
  270. if (this.content == null)
  271. {
  272. throw new InvalidOperationException($"The TrackerControl template must contain a ContentPresenter with name +'{PartContent}'");
  273. }
  274. this.UpdatePositionAndBorder();
  275. }
  276. /// <summary>
  277. /// Called when the position is changed.
  278. /// </summary>
  279. /// <param name="sender">The sender.</param>
  280. /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs" /> instance containing the event data.</param>
  281. private static void PositionChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
  282. {
  283. ((TrackerControl)sender).OnPositionChanged(e);
  284. }
  285. /// <summary>
  286. /// Called when the position is changed.
  287. /// </summary>
  288. /// <param name="dependencyPropertyChangedEventArgs">The dependency property changed event args.</param>
  289. // ReSharper disable once UnusedParameter.Local
  290. private void OnPositionChanged(DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
  291. {
  292. this.UpdatePositionAndBorder();
  293. }
  294. /// <summary>
  295. /// Update the position and border of the tracker.
  296. /// </summary>
  297. private void UpdatePositionAndBorder()
  298. {
  299. if (this.contentContainer == null)
  300. {
  301. return;
  302. }
  303. Canvas.SetLeft(this.contentContainer, this.Position.X);
  304. Canvas.SetTop(this.contentContainer, this.Position.Y);
  305. FrameworkElement parent = this;
  306. while (!(parent is Canvas) && parent != null)
  307. {
  308. parent = VisualTreeHelper.GetParent(parent) as FrameworkElement;
  309. }
  310. if (parent == null)
  311. {
  312. return;
  313. }
  314. // throw new InvalidOperationException("The TrackerControl must have a Canvas parent.");
  315. double canvasWidth = parent.ActualWidth;
  316. double canvasHeight = parent.ActualHeight;
  317. this.content.Measure(new Size(canvasWidth, canvasHeight));
  318. this.content.Arrange(new Rect(0, 0, this.content.DesiredSize.Width, this.content.DesiredSize.Height));
  319. double contentWidth = this.content.DesiredSize.Width;
  320. double contentHeight = this.content.DesiredSize.Height;
  321. // Minimum allowed margins around the tracker
  322. const double MarginLimit = 10;
  323. var ha = HorizontalAlignment.Center;
  324. if (this.CanCenterHorizontally)
  325. {
  326. if (this.Position.X - (contentWidth / 2) < MarginLimit)
  327. {
  328. ha = HorizontalAlignment.Left;
  329. }
  330. if (this.Position.X + (contentWidth / 2) > canvasWidth - MarginLimit)
  331. {
  332. ha = HorizontalAlignment.Right;
  333. }
  334. }
  335. else
  336. {
  337. ha = this.Position.X < canvasWidth / 2 ? HorizontalAlignment.Left : HorizontalAlignment.Right;
  338. }
  339. var va = VerticalAlignment.Center;
  340. if (this.CanCenterVertically)
  341. {
  342. if (this.Position.Y - (contentHeight / 2) < MarginLimit)
  343. {
  344. va = VerticalAlignment.Top;
  345. }
  346. if (ha == HorizontalAlignment.Center)
  347. {
  348. va = VerticalAlignment.Bottom;
  349. if (this.Position.Y - contentHeight < MarginLimit)
  350. {
  351. va = VerticalAlignment.Top;
  352. }
  353. }
  354. if (va == VerticalAlignment.Center && this.Position.Y + (contentHeight / 2) > canvasHeight - MarginLimit)
  355. {
  356. va = VerticalAlignment.Bottom;
  357. }
  358. if (va == VerticalAlignment.Top && this.Position.Y + contentHeight > canvasHeight - MarginLimit)
  359. {
  360. va = VerticalAlignment.Bottom;
  361. }
  362. }
  363. else
  364. {
  365. va = this.Position.Y < canvasHeight / 2 ? VerticalAlignment.Top : VerticalAlignment.Bottom;
  366. }
  367. double dx = ha == HorizontalAlignment.Center ? -0.5 : ha == HorizontalAlignment.Left ? 0 : -1;
  368. double dy = va == VerticalAlignment.Center ? -0.5 : va == VerticalAlignment.Top ? 0 : -1;
  369. this.path.Data = this.ShowPointer
  370. ? this.CreatePointerBorderGeometry(ha, va, contentWidth, contentHeight, out Thickness margin)
  371. : this.CreateBorderGeometry(ha, va, contentWidth, contentHeight, out margin);
  372. this.content.Margin = margin;
  373. this.contentContainer.Measure(new Size(canvasWidth, canvasHeight));
  374. var contentSize = this.contentContainer.DesiredSize;
  375. this.contentContainer.RenderTransform = new TranslateTransform
  376. {
  377. X = dx * contentSize.Width,
  378. Y = dy * contentSize.Height
  379. };
  380. var pos = this.Position;
  381. if (this.horizontalLine != null)
  382. {
  383. if (this.LineExtents.Width > 0)
  384. {
  385. this.horizontalLine.X1 = this.LineExtents.Left;
  386. this.horizontalLine.X2 = this.LineExtents.Right;
  387. }
  388. else
  389. {
  390. this.horizontalLine.X1 = 0;
  391. this.horizontalLine.X2 = canvasWidth;
  392. }
  393. this.horizontalLine.Y1 = pos.Y;
  394. this.horizontalLine.Y2 = pos.Y;
  395. }
  396. if (this.verticalLine != null)
  397. {
  398. if (this.LineExtents.Width > 0)
  399. {
  400. this.verticalLine.Y1 = this.LineExtents.Top;
  401. this.verticalLine.Y2 = this.LineExtents.Bottom;
  402. }
  403. else
  404. {
  405. this.verticalLine.Y1 = 0;
  406. this.verticalLine.Y2 = canvasHeight;
  407. }
  408. this.verticalLine.X1 = pos.X;
  409. this.verticalLine.X2 = pos.X;
  410. }
  411. }
  412. /// <summary>
  413. /// Create the border geometry.
  414. /// </summary>
  415. /// <param name="ha">The horizontal alignment.</param>
  416. /// <param name="va">The vertical alignment.</param>
  417. /// <param name="width">The width.</param>
  418. /// <param name="height">The height.</param>
  419. /// <param name="margin">The margin.</param>
  420. /// <returns>The border geometry.</returns>
  421. private Geometry CreateBorderGeometry(
  422. HorizontalAlignment ha, VerticalAlignment va, double width, double height, out Thickness margin)
  423. {
  424. double m = this.Distance;
  425. var rect = new Rect(
  426. ha == HorizontalAlignment.Left ? m : 0, va == VerticalAlignment.Top ? m : 0, width, height);
  427. margin = new Thickness(
  428. ha == HorizontalAlignment.Left ? m : 0,
  429. va == VerticalAlignment.Top ? m : 0,
  430. ha == HorizontalAlignment.Right ? m : 0,
  431. va == VerticalAlignment.Bottom ? m : 0);
  432. return new RectangleGeometry { Rect = rect, RadiusX = this.CornerRadius, RadiusY = this.CornerRadius };
  433. }
  434. /// <summary>
  435. /// Create a border geometry with a 'pointer'.
  436. /// </summary>
  437. /// <param name="ha">The horizontal alignment.</param>
  438. /// <param name="va">The vertical alignment.</param>
  439. /// <param name="width">The width.</param>
  440. /// <param name="height">The height.</param>
  441. /// <param name="margin">The margin.</param>
  442. /// <returns>The border geometry.</returns>
  443. private Geometry CreatePointerBorderGeometry(
  444. HorizontalAlignment ha, VerticalAlignment va, double width, double height, out Thickness margin)
  445. {
  446. Point[] points = null;
  447. double m = this.Distance;
  448. margin = new Thickness();
  449. if (ha == HorizontalAlignment.Center && va == VerticalAlignment.Bottom)
  450. {
  451. double x0 = 0;
  452. double x1 = width;
  453. double x2 = (x0 + x1) / 2;
  454. double y0 = 0;
  455. double y1 = height;
  456. margin = new Thickness(0, 0, 0, m);
  457. points = new[]
  458. {
  459. new Point(x0, y0), new Point(x1, y0), new Point(x1, y1), new Point(x2 + (m / 2), y1),
  460. new Point(x2, y1 + m), new Point(x2 - (m / 2), y1), new Point(x0, y1)
  461. };
  462. }
  463. if (ha == HorizontalAlignment.Center && va == VerticalAlignment.Top)
  464. {
  465. double x0 = 0;
  466. double x1 = width;
  467. double x2 = (x0 + x1) / 2;
  468. double y0 = m;
  469. double y1 = m + height;
  470. margin = new Thickness(0, m, 0, 0);
  471. points = new[]
  472. {
  473. new Point(x0, y0), new Point(x2 - (m / 2), y0), new Point(x2, 0), new Point(x2 + (m / 2), y0),
  474. new Point(x1, y0), new Point(x1, y1), new Point(x0, y1)
  475. };
  476. }
  477. if (ha == HorizontalAlignment.Left && va == VerticalAlignment.Center)
  478. {
  479. double x0 = m;
  480. double x1 = m + width;
  481. double y0 = 0;
  482. double y1 = height;
  483. double y2 = (y0 + y1) / 2;
  484. margin = new Thickness(m, 0, 0, 0);
  485. points = new[]
  486. {
  487. new Point(0, y2), new Point(x0, y2 - (m / 2)), new Point(x0, y0), new Point(x1, y0),
  488. new Point(x1, y1), new Point(x0, y1), new Point(x0, y2 + (m / 2))
  489. };
  490. }
  491. if (ha == HorizontalAlignment.Right && va == VerticalAlignment.Center)
  492. {
  493. double x0 = 0;
  494. double x1 = width;
  495. double y0 = 0;
  496. double y1 = height;
  497. double y2 = (y0 + y1) / 2;
  498. margin = new Thickness(0, 0, m, 0);
  499. points = new[]
  500. {
  501. new Point(x1 + m, y2), new Point(x1, y2 + (m / 2)), new Point(x1, y1), new Point(x0, y1),
  502. new Point(x0, y0), new Point(x1, y0), new Point(x1, y2 - (m / 2))
  503. };
  504. }
  505. if (ha == HorizontalAlignment.Left && va == VerticalAlignment.Top)
  506. {
  507. m *= 0.67;
  508. double x0 = m;
  509. double x1 = m + width;
  510. double y0 = m;
  511. double y1 = m + height;
  512. margin = new Thickness(m, m, 0, 0);
  513. points = new[]
  514. {
  515. new Point(0, 0), new Point(m * 2, y0), new Point(x1, y0), new Point(x1, y1), new Point(x0, y1),
  516. new Point(x0, m * 2)
  517. };
  518. }
  519. if (ha == HorizontalAlignment.Right && va == VerticalAlignment.Top)
  520. {
  521. m *= 0.67;
  522. double x0 = 0;
  523. double x1 = width;
  524. double y0 = m;
  525. double y1 = m + height;
  526. margin = new Thickness(0, m, m, 0);
  527. points = new[]
  528. {
  529. new Point(x1 + m, 0), new Point(x1, y0 + m), new Point(x1, y1), new Point(x0, y1),
  530. new Point(x0, y0), new Point(x1 - m, y0)
  531. };
  532. }
  533. if (ha == HorizontalAlignment.Left && va == VerticalAlignment.Bottom)
  534. {
  535. m *= 0.67;
  536. double x0 = m;
  537. double x1 = m + width;
  538. double y0 = 0;
  539. double y1 = height;
  540. margin = new Thickness(m, 0, 0, m);
  541. points = new[]
  542. {
  543. new Point(0, y1 + m), new Point(x0, y1 - m), new Point(x0, y0), new Point(x1, y0),
  544. new Point(x1, y1), new Point(x0 + m, y1)
  545. };
  546. }
  547. if (ha == HorizontalAlignment.Right && va == VerticalAlignment.Bottom)
  548. {
  549. m *= 0.67;
  550. double x0 = 0;
  551. double x1 = width;
  552. double y0 = 0;
  553. double y1 = height;
  554. margin = new Thickness(0, 0, m, m);
  555. points = new[]
  556. {
  557. new Point(x1 + m, y1 + m), new Point(x1 - m, y1), new Point(x0, y1), new Point(x0, y0),
  558. new Point(x1, y0), new Point(x1, y1 - m)
  559. };
  560. }
  561. if (points == null)
  562. {
  563. return null;
  564. }
  565. var pc = new PointCollection(points.Length);
  566. foreach (var p in points)
  567. {
  568. pc.Add(p);
  569. }
  570. var segments = new PathSegmentCollection { new PolyLineSegment { Points = pc } };
  571. var pf = new PathFigure { StartPoint = points[0], Segments = segments, IsClosed = true };
  572. return new PathGeometry { Figures = new PathFigureCollection { pf } };
  573. }
  574. }
  575. }