RelativePanel.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. //reference doc : https://docs.microsoft.com/en-us/uwp/api/Windows.UI.Xaml.Controls.RelativePanel
  2. using System;
  3. using System.Collections.Generic;
  4. using System.ComponentModel;
  5. using System.Linq;
  6. using System.Windows;
  7. using System.Windows.Controls;
  8. using System.Windows.Markup;
  9. using HandyControl.Data;
  10. using HandyControl.Tools.Extension;
  11. namespace HandyControl.Controls;
  12. public class RelativePanel : Panel
  13. {
  14. private readonly Graph _childGraph;
  15. public RelativePanel() => _childGraph = new Graph();
  16. #region Panel alignment
  17. public static readonly DependencyProperty AlignLeftWithPanelProperty = DependencyProperty.RegisterAttached(
  18. "AlignLeftWithPanel", typeof(bool), typeof(RelativePanel), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.AffectsRender));
  19. public static void SetAlignLeftWithPanel(DependencyObject element, bool value)
  20. => element.SetValue(AlignLeftWithPanelProperty, ValueBoxes.BooleanBox(value));
  21. public static bool GetAlignLeftWithPanel(DependencyObject element)
  22. => (bool) element.GetValue(AlignLeftWithPanelProperty);
  23. public static readonly DependencyProperty AlignTopWithPanelProperty = DependencyProperty.RegisterAttached(
  24. "AlignTopWithPanel", typeof(bool), typeof(RelativePanel), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.AffectsRender));
  25. public static void SetAlignTopWithPanel(DependencyObject element, bool value)
  26. => element.SetValue(AlignTopWithPanelProperty, ValueBoxes.BooleanBox(value));
  27. public static bool GetAlignTopWithPanel(DependencyObject element)
  28. => (bool) element.GetValue(AlignTopWithPanelProperty);
  29. public static readonly DependencyProperty AlignRightWithPanelProperty = DependencyProperty.RegisterAttached(
  30. "AlignRightWithPanel", typeof(bool), typeof(RelativePanel), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.AffectsRender));
  31. public static void SetAlignRightWithPanel(DependencyObject element, bool value)
  32. => element.SetValue(AlignRightWithPanelProperty, ValueBoxes.BooleanBox(value));
  33. public static bool GetAlignRightWithPanel(DependencyObject element)
  34. => (bool) element.GetValue(AlignRightWithPanelProperty);
  35. public static readonly DependencyProperty AlignBottomWithPanelProperty = DependencyProperty.RegisterAttached(
  36. "AlignBottomWithPanel", typeof(bool), typeof(RelativePanel), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.AffectsRender));
  37. public static void SetAlignBottomWithPanel(DependencyObject element, bool value)
  38. => element.SetValue(AlignBottomWithPanelProperty, ValueBoxes.BooleanBox(value));
  39. public static bool GetAlignBottomWithPanel(DependencyObject element)
  40. => (bool) element.GetValue(AlignBottomWithPanelProperty);
  41. #endregion
  42. #region Sibling alignment
  43. public static readonly DependencyProperty AlignLeftWithProperty = DependencyProperty.RegisterAttached(
  44. "AlignLeftWith", typeof(UIElement), typeof(RelativePanel), new FrameworkPropertyMetadata(default(UIElement), FrameworkPropertyMetadataOptions.AffectsRender));
  45. public static void SetAlignLeftWith(DependencyObject element, UIElement value)
  46. => element.SetValue(AlignLeftWithProperty, value);
  47. [TypeConverter(typeof(NameReferenceConverter))]
  48. public static UIElement GetAlignLeftWith(DependencyObject element)
  49. => (UIElement) element.GetValue(AlignLeftWithProperty);
  50. public static readonly DependencyProperty AlignTopWithProperty = DependencyProperty.RegisterAttached(
  51. "AlignTopWith", typeof(UIElement), typeof(RelativePanel), new FrameworkPropertyMetadata(default(UIElement), FrameworkPropertyMetadataOptions.AffectsRender));
  52. public static void SetAlignTopWith(DependencyObject element, UIElement value)
  53. => element.SetValue(AlignTopWithProperty, value);
  54. [TypeConverter(typeof(NameReferenceConverter))]
  55. public static UIElement GetAlignTopWith(DependencyObject element)
  56. => (UIElement) element.GetValue(AlignTopWithProperty);
  57. public static readonly DependencyProperty AlignRightWithProperty = DependencyProperty.RegisterAttached(
  58. "AlignRightWith", typeof(UIElement), typeof(RelativePanel), new FrameworkPropertyMetadata(default(UIElement), FrameworkPropertyMetadataOptions.AffectsRender));
  59. public static void SetAlignRightWith(DependencyObject element, UIElement value)
  60. => element.SetValue(AlignRightWithProperty, value);
  61. [TypeConverter(typeof(NameReferenceConverter))]
  62. public static UIElement GetAlignRightWith(DependencyObject element)
  63. => (UIElement) element.GetValue(AlignRightWithProperty);
  64. public static readonly DependencyProperty AlignBottomWithProperty = DependencyProperty.RegisterAttached(
  65. "AlignBottomWith", typeof(UIElement), typeof(RelativePanel), new FrameworkPropertyMetadata(default(UIElement), FrameworkPropertyMetadataOptions.AffectsRender));
  66. public static void SetAlignBottomWith(DependencyObject element, UIElement value)
  67. => element.SetValue(AlignBottomWithProperty, value);
  68. [TypeConverter(typeof(NameReferenceConverter))]
  69. public static UIElement GetAlignBottomWith(DependencyObject element)
  70. => (UIElement) element.GetValue(AlignBottomWithProperty);
  71. #endregion
  72. #region Sibling positional
  73. public static readonly DependencyProperty LeftOfProperty = DependencyProperty.RegisterAttached(
  74. "LeftOf", typeof(UIElement), typeof(RelativePanel), new FrameworkPropertyMetadata(default(UIElement), FrameworkPropertyMetadataOptions.AffectsRender));
  75. public static void SetLeftOf(DependencyObject element, UIElement value)
  76. => element.SetValue(LeftOfProperty, value);
  77. [TypeConverter(typeof(NameReferenceConverter))]
  78. public static UIElement GetLeftOf(DependencyObject element)
  79. => (UIElement) element.GetValue(LeftOfProperty);
  80. public static readonly DependencyProperty AboveProperty = DependencyProperty.RegisterAttached(
  81. "Above", typeof(UIElement), typeof(RelativePanel), new FrameworkPropertyMetadata(default(UIElement), FrameworkPropertyMetadataOptions.AffectsRender));
  82. public static void SetAbove(DependencyObject element, UIElement value)
  83. => element.SetValue(AboveProperty, value);
  84. [TypeConverter(typeof(NameReferenceConverter))]
  85. public static UIElement GetAbove(DependencyObject element)
  86. => (UIElement) element.GetValue(AboveProperty);
  87. public static readonly DependencyProperty RightOfProperty = DependencyProperty.RegisterAttached(
  88. "RightOf", typeof(UIElement), typeof(RelativePanel), new FrameworkPropertyMetadata(default(UIElement), FrameworkPropertyMetadataOptions.AffectsRender));
  89. public static void SetRightOf(DependencyObject element, UIElement value)
  90. => element.SetValue(RightOfProperty, value);
  91. [TypeConverter(typeof(NameReferenceConverter))]
  92. public static UIElement GetRightOf(DependencyObject element)
  93. => (UIElement) element.GetValue(RightOfProperty);
  94. public static readonly DependencyProperty BelowProperty = DependencyProperty.RegisterAttached(
  95. "Below", typeof(UIElement), typeof(RelativePanel), new FrameworkPropertyMetadata(default(UIElement), FrameworkPropertyMetadataOptions.AffectsRender));
  96. public static void SetBelow(DependencyObject element, UIElement value)
  97. => element.SetValue(BelowProperty, value);
  98. [TypeConverter(typeof(NameReferenceConverter))]
  99. public static UIElement GetBelow(DependencyObject element)
  100. => (UIElement) element.GetValue(BelowProperty);
  101. #endregion
  102. #region Center alignment
  103. public static readonly DependencyProperty AlignHorizontalCenterWithPanelProperty = DependencyProperty.RegisterAttached(
  104. "AlignHorizontalCenterWithPanel", typeof(bool), typeof(RelativePanel), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.AffectsRender));
  105. public static void SetAlignHorizontalCenterWithPanel(DependencyObject element, bool value)
  106. => element.SetValue(AlignHorizontalCenterWithPanelProperty, ValueBoxes.BooleanBox(value));
  107. public static bool GetAlignHorizontalCenterWithPanel(DependencyObject element)
  108. => (bool) element.GetValue(AlignHorizontalCenterWithPanelProperty);
  109. public static readonly DependencyProperty AlignVerticalCenterWithPanelProperty = DependencyProperty.RegisterAttached(
  110. "AlignVerticalCenterWithPanel", typeof(bool), typeof(RelativePanel), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.AffectsRender));
  111. public static void SetAlignVerticalCenterWithPanel(DependencyObject element, bool value)
  112. => element.SetValue(AlignVerticalCenterWithPanelProperty, ValueBoxes.BooleanBox(value));
  113. public static bool GetAlignVerticalCenterWithPanel(DependencyObject element)
  114. => (bool) element.GetValue(AlignVerticalCenterWithPanelProperty);
  115. public static readonly DependencyProperty AlignHorizontalCenterWithProperty = DependencyProperty.RegisterAttached(
  116. "AlignHorizontalCenterWith", typeof(UIElement), typeof(RelativePanel), new FrameworkPropertyMetadata(default(UIElement), FrameworkPropertyMetadataOptions.AffectsRender));
  117. public static void SetAlignHorizontalCenterWith(DependencyObject element, UIElement value)
  118. => element.SetValue(AlignHorizontalCenterWithProperty, value);
  119. [TypeConverter(typeof(NameReferenceConverter))]
  120. public static UIElement GetAlignHorizontalCenterWith(DependencyObject element)
  121. => (UIElement) element.GetValue(AlignHorizontalCenterWithProperty);
  122. public static readonly DependencyProperty AlignVerticalCenterWithProperty = DependencyProperty.RegisterAttached(
  123. "AlignVerticalCenterWith", typeof(UIElement), typeof(RelativePanel), new FrameworkPropertyMetadata(default(UIElement), FrameworkPropertyMetadataOptions.AffectsRender));
  124. public static void SetAlignVerticalCenterWith(DependencyObject element, UIElement value)
  125. => element.SetValue(AlignVerticalCenterWithProperty, value);
  126. [TypeConverter(typeof(NameReferenceConverter))]
  127. public static UIElement GetAlignVerticalCenterWith(DependencyObject element)
  128. => (UIElement) element.GetValue(AlignVerticalCenterWithProperty);
  129. #endregion
  130. protected override Size MeasureOverride(Size availableSize)
  131. {
  132. #region Calc DesiredSize
  133. _childGraph.Clear();
  134. foreach (UIElement child in InternalChildren)
  135. {
  136. if (child == null) continue;
  137. var node = _childGraph.AddNode(child);
  138. node.AlignLeftWithNode = _childGraph.AddLink(node, GetAlignLeftWith(child));
  139. node.AlignTopWithNode = _childGraph.AddLink(node, GetAlignTopWith(child));
  140. node.AlignRightWithNode = _childGraph.AddLink(node, GetAlignRightWith(child));
  141. node.AlignBottomWithNode = _childGraph.AddLink(node, GetAlignBottomWith(child));
  142. node.LeftOfNode = _childGraph.AddLink(node, GetLeftOf(child));
  143. node.AboveNode = _childGraph.AddLink(node, GetAbove(child));
  144. node.RightOfNode = _childGraph.AddLink(node, GetRightOf(child));
  145. node.BelowNode = _childGraph.AddLink(node, GetBelow(child));
  146. node.AlignHorizontalCenterWith = _childGraph.AddLink(node, GetAlignHorizontalCenterWith(child));
  147. node.AlignVerticalCenterWith = _childGraph.AddLink(node, GetAlignVerticalCenterWith(child));
  148. }
  149. _childGraph.Measure(availableSize);
  150. #endregion
  151. #region Calc AvailableSize
  152. _childGraph.Reset(false);
  153. var calcWidth = Width.IsNaN() && HorizontalAlignment != HorizontalAlignment.Stretch;
  154. var calcHeight = Height.IsNaN() && VerticalAlignment != VerticalAlignment.Stretch;
  155. var boundingSize = _childGraph.GetBoundingSize(calcWidth, calcHeight);
  156. _childGraph.Reset();
  157. _childGraph.Measure(boundingSize);
  158. return boundingSize;
  159. #endregion
  160. }
  161. protected override Size ArrangeOverride(Size arrangeSize)
  162. {
  163. _childGraph.GetNodes().Do(node => node.Arrange(arrangeSize));
  164. return arrangeSize;
  165. }
  166. private class GraphNode
  167. {
  168. public bool Measured { get; set; }
  169. public UIElement Element { get; }
  170. private bool HorizontalOffsetFlag { get; set; }
  171. private bool VerticalOffsetFlag { get; set; }
  172. private Size BoundingSize { get; set; }
  173. public Size OriginDesiredSize { get; set; }
  174. public double Left { get; set; } = double.NaN;
  175. public double Top { get; set; } = double.NaN;
  176. public double Right { get; set; } = double.NaN;
  177. public double Bottom { get; set; } = double.NaN;
  178. public HashSet<GraphNode> OutgoingNodes { get; }
  179. public GraphNode AlignLeftWithNode { get; set; }
  180. public GraphNode AlignTopWithNode { get; set; }
  181. public GraphNode AlignRightWithNode { get; set; }
  182. public GraphNode AlignBottomWithNode { get; set; }
  183. public GraphNode LeftOfNode { get; set; }
  184. public GraphNode AboveNode { get; set; }
  185. public GraphNode RightOfNode { get; set; }
  186. public GraphNode BelowNode { get; set; }
  187. public GraphNode AlignHorizontalCenterWith { get; set; }
  188. public GraphNode AlignVerticalCenterWith { get; set; }
  189. public GraphNode(UIElement element)
  190. {
  191. OutgoingNodes = new HashSet<GraphNode>();
  192. Element = element;
  193. }
  194. public void Arrange(Size arrangeSize) => Element.Arrange(new Rect(Left, Top, Math.Max(arrangeSize.Width - Left - Right, 0), Math.Max(arrangeSize.Height - Top - Bottom, 0)));
  195. public void Reset(bool clearPos)
  196. {
  197. if (clearPos)
  198. {
  199. Left = double.NaN;
  200. Top = double.NaN;
  201. Right = double.NaN;
  202. Bottom = double.NaN;
  203. }
  204. Measured = false;
  205. }
  206. public Size GetBoundingSize()
  207. {
  208. if (Left < 0 || Top < 0) return default;
  209. if (Measured) return BoundingSize;
  210. if (!OutgoingNodes.Any())
  211. {
  212. BoundingSize = Element.DesiredSize;
  213. Measured = true;
  214. }
  215. else
  216. {
  217. BoundingSize = GetBoundingSize(this, Element.DesiredSize, OutgoingNodes);
  218. Measured = true;
  219. }
  220. return BoundingSize;
  221. }
  222. private static Size GetBoundingSize(GraphNode prevNode, Size prevSize, IEnumerable<GraphNode> nodes)
  223. {
  224. foreach (var node in nodes)
  225. {
  226. if (node.Measured || !node.OutgoingNodes.Any())
  227. {
  228. if (prevNode.LeftOfNode != null && prevNode.LeftOfNode == node ||
  229. prevNode.RightOfNode != null && prevNode.RightOfNode == node)
  230. {
  231. prevSize.Width += node.BoundingSize.Width;
  232. if (GetAlignHorizontalCenterWithPanel(node.Element) || node.HorizontalOffsetFlag)
  233. {
  234. prevSize.Width += prevNode.OriginDesiredSize.Width;
  235. prevNode.HorizontalOffsetFlag = true;
  236. }
  237. if (node.VerticalOffsetFlag)
  238. {
  239. prevNode.VerticalOffsetFlag = true;
  240. }
  241. }
  242. if (prevNode.AboveNode != null && prevNode.AboveNode == node ||
  243. prevNode.BelowNode != null && prevNode.BelowNode == node)
  244. {
  245. prevSize.Height += node.BoundingSize.Height;
  246. if (GetAlignVerticalCenterWithPanel(node.Element) || node.VerticalOffsetFlag)
  247. {
  248. prevSize.Height += prevNode.OriginDesiredSize.Height;
  249. prevNode.VerticalOffsetFlag = true;
  250. }
  251. if (node.HorizontalOffsetFlag)
  252. {
  253. prevNode.HorizontalOffsetFlag = true;
  254. }
  255. }
  256. }
  257. else
  258. {
  259. return GetBoundingSize(node, prevSize, node.OutgoingNodes);
  260. }
  261. }
  262. return prevSize;
  263. }
  264. }
  265. private class Graph
  266. {
  267. private readonly Dictionary<DependencyObject, GraphNode> _nodeDic;
  268. private Size AvailableSize { get; set; }
  269. public Graph() => _nodeDic = new Dictionary<DependencyObject, GraphNode>();
  270. public IEnumerable<GraphNode> GetNodes() => _nodeDic.Values;
  271. public void Clear()
  272. {
  273. AvailableSize = new Size();
  274. _nodeDic.Clear();
  275. }
  276. public void Reset(bool clearPos = true) => _nodeDic.Values.Do(node => node.Reset(clearPos));
  277. public GraphNode AddLink(GraphNode from, UIElement to)
  278. {
  279. if (to == null) return null;
  280. GraphNode nodeTo;
  281. if (_nodeDic.ContainsKey(to))
  282. {
  283. nodeTo = _nodeDic[to];
  284. }
  285. else
  286. {
  287. nodeTo = new GraphNode(to);
  288. _nodeDic[to] = nodeTo;
  289. }
  290. from.OutgoingNodes.Add(nodeTo);
  291. return nodeTo;
  292. }
  293. public GraphNode AddNode(UIElement value)
  294. {
  295. if (!_nodeDic.ContainsKey(value))
  296. {
  297. var node = new GraphNode(value);
  298. _nodeDic.Add(value, node);
  299. return node;
  300. }
  301. return _nodeDic[value];
  302. }
  303. public void Measure(Size availableSize)
  304. {
  305. AvailableSize = EnsureValidSize(availableSize);
  306. Measure(_nodeDic.Values, null);
  307. }
  308. private static Size EnsureValidSize(Size size)
  309. {
  310. var width = double.IsInfinity(size.Width) ? 0 : size.Width;
  311. var height = double.IsInfinity(size.Height) ? 0 : size.Height;
  312. return new Size(width, height);
  313. }
  314. private void Measure(IEnumerable<GraphNode> nodes, HashSet<DependencyObject> set)
  315. {
  316. set ??= new HashSet<DependencyObject>();
  317. foreach (var node in nodes)
  318. {
  319. /*
  320. * 该节点无任何依赖,所以从这里开始计算元素位置。
  321. * 因为无任何依赖,所以忽略同级元素
  322. */
  323. if (!node.Measured && !node.OutgoingNodes.Any())
  324. {
  325. MeasureChild(node);
  326. continue;
  327. }
  328. // 判断依赖元素是否全部排列完毕
  329. if (node.OutgoingNodes.All(item => item.Measured))
  330. {
  331. MeasureChild(node);
  332. continue;
  333. }
  334. // 判断是否有循环
  335. if (!set.Add(node.Element)) throw new Exception("RelativePanel error: Circular dependency detected. Layout could not complete.");
  336. // 没有循环,且有依赖,则继续往下
  337. Measure(node.OutgoingNodes, set);
  338. if (!node.Measured)
  339. {
  340. MeasureChild(node);
  341. }
  342. }
  343. }
  344. private void MeasureChild(GraphNode node)
  345. {
  346. var child = node.Element;
  347. child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
  348. node.OriginDesiredSize = child.DesiredSize;
  349. var alignLeftWithPanel = GetAlignLeftWithPanel(child);
  350. var alignTopWithPanel = GetAlignTopWithPanel(child);
  351. var alignRightWithPanel = GetAlignRightWithPanel(child);
  352. var alignBottomWithPanel = GetAlignBottomWithPanel(child);
  353. #region Panel alignment
  354. if (alignLeftWithPanel) node.Left = 0;
  355. if (alignTopWithPanel) node.Top = 0;
  356. if (alignRightWithPanel) node.Right = 0;
  357. if (alignBottomWithPanel) node.Bottom = 0;
  358. #endregion
  359. #region Sibling alignment
  360. if (node.AlignLeftWithNode != null)
  361. {
  362. node.Left = node.Left.IsNaN() ? node.AlignLeftWithNode.Left : node.AlignLeftWithNode.Left * 0.5;
  363. }
  364. if (node.AlignTopWithNode != null)
  365. {
  366. node.Top = node.Top.IsNaN() ? node.AlignTopWithNode.Top : node.AlignTopWithNode.Top * 0.5;
  367. }
  368. if (node.AlignRightWithNode != null)
  369. {
  370. node.Right = node.Right.IsNaN()
  371. ? node.AlignRightWithNode.Right
  372. : node.AlignRightWithNode.Right * 0.5;
  373. }
  374. if (node.AlignBottomWithNode != null)
  375. {
  376. node.Bottom = node.Bottom.IsNaN()
  377. ? node.AlignBottomWithNode.Bottom
  378. : node.AlignBottomWithNode.Bottom * 0.5;
  379. }
  380. #endregion
  381. #region Measure
  382. var availableHeight = AvailableSize.Height - node.Top - node.Bottom;
  383. if (availableHeight.IsNaN())
  384. {
  385. availableHeight = AvailableSize.Height;
  386. if (!node.Top.IsNaN() && node.Bottom.IsNaN())
  387. {
  388. availableHeight -= node.Top;
  389. }
  390. else if (node.Top.IsNaN() && !node.Bottom.IsNaN())
  391. {
  392. availableHeight -= node.Bottom;
  393. }
  394. }
  395. var availableWidth = AvailableSize.Width - node.Left - node.Right;
  396. if (availableWidth.IsNaN())
  397. {
  398. availableWidth = AvailableSize.Width;
  399. if (!node.Left.IsNaN() && node.Right.IsNaN())
  400. {
  401. availableWidth -= node.Left;
  402. }
  403. else if (node.Left.IsNaN() && !node.Right.IsNaN())
  404. {
  405. availableWidth -= node.Right;
  406. }
  407. }
  408. child.Measure(new Size(Math.Max(availableWidth, 0), Math.Max(availableHeight, 0)));
  409. var childSize = child.DesiredSize;
  410. #endregion
  411. #region Sibling positional
  412. if (node.LeftOfNode != null && node.Left.IsNaN())
  413. {
  414. node.Left = node.LeftOfNode.Left - childSize.Width;
  415. }
  416. if (node.AboveNode != null && node.Top.IsNaN())
  417. {
  418. node.Top = node.AboveNode.Top - childSize.Height;
  419. }
  420. if (node.RightOfNode != null)
  421. {
  422. if (node.Right.IsNaN())
  423. {
  424. node.Right = node.RightOfNode.Right - childSize.Width;
  425. }
  426. if (node.Left.IsNaN())
  427. {
  428. node.Left = AvailableSize.Width - node.RightOfNode.Right;
  429. }
  430. }
  431. if (node.BelowNode != null)
  432. {
  433. if (node.Bottom.IsNaN())
  434. {
  435. node.Bottom = node.BelowNode.Bottom - childSize.Height;
  436. }
  437. if (node.Top.IsNaN())
  438. {
  439. node.Top = AvailableSize.Height - node.BelowNode.Bottom;
  440. }
  441. }
  442. #endregion
  443. #region Sibling-center alignment
  444. if (node.AlignHorizontalCenterWith != null)
  445. {
  446. var halfWidthLeft = (AvailableSize.Width + node.AlignHorizontalCenterWith.Left - node.AlignHorizontalCenterWith.Right - childSize.Width) * 0.5;
  447. var halfWidthRight = (AvailableSize.Width - node.AlignHorizontalCenterWith.Left + node.AlignHorizontalCenterWith.Right - childSize.Width) * 0.5;
  448. if (node.Left.IsNaN()) node.Left = halfWidthLeft;
  449. else node.Left = (node.Left + halfWidthLeft) * 0.5;
  450. if (node.Right.IsNaN()) node.Right = halfWidthRight;
  451. else node.Right = (node.Right + halfWidthRight) * 0.5;
  452. }
  453. if (node.AlignVerticalCenterWith != null)
  454. {
  455. var halfHeightTop = (AvailableSize.Height + node.AlignVerticalCenterWith.Top - node.AlignVerticalCenterWith.Bottom - childSize.Height) * 0.5;
  456. var halfHeightBottom = (AvailableSize.Height - node.AlignVerticalCenterWith.Top + node.AlignVerticalCenterWith.Bottom - childSize.Height) * 0.5;
  457. if (node.Top.IsNaN()) node.Top = halfHeightTop;
  458. else node.Top = (node.Top + halfHeightTop) * 0.5;
  459. if (node.Bottom.IsNaN()) node.Bottom = halfHeightBottom;
  460. else node.Bottom = (node.Bottom + halfHeightBottom) * 0.5;
  461. }
  462. #endregion
  463. #region Panel-center alignment
  464. if (GetAlignHorizontalCenterWithPanel(child))
  465. {
  466. var halfSubWidth = (AvailableSize.Width - childSize.Width) * 0.5;
  467. if (node.Left.IsNaN()) node.Left = halfSubWidth;
  468. else node.Left = (node.Left + halfSubWidth) * 0.5;
  469. if (node.Right.IsNaN()) node.Right = halfSubWidth;
  470. else node.Right = (node.Right + halfSubWidth) * 0.5;
  471. }
  472. if (GetAlignVerticalCenterWithPanel(child))
  473. {
  474. var halfSubHeight = (AvailableSize.Height - childSize.Height) * 0.5;
  475. if (node.Top.IsNaN()) node.Top = halfSubHeight;
  476. else node.Top = (node.Top + halfSubHeight) * 0.5;
  477. if (node.Bottom.IsNaN()) node.Bottom = halfSubHeight;
  478. else node.Bottom = (node.Bottom + halfSubHeight) * 0.5;
  479. }
  480. #endregion
  481. if (node.Left.IsNaN())
  482. {
  483. if (!node.Right.IsNaN())
  484. node.Left = AvailableSize.Width - node.Right - childSize.Width;
  485. else
  486. {
  487. node.Left = 0;
  488. node.Right = AvailableSize.Width - childSize.Width;
  489. }
  490. }
  491. else if (!node.Left.IsNaN() && node.Right.IsNaN())
  492. {
  493. node.Right = AvailableSize.Width - node.Left - childSize.Width;
  494. }
  495. if (node.Top.IsNaN())
  496. {
  497. if (!node.Bottom.IsNaN())
  498. node.Top = AvailableSize.Height - node.Bottom - childSize.Height;
  499. else
  500. {
  501. node.Top = 0;
  502. node.Bottom = AvailableSize.Height - childSize.Height;
  503. }
  504. }
  505. else if (!node.Top.IsNaN() && node.Bottom.IsNaN())
  506. {
  507. node.Bottom = AvailableSize.Height - node.Top - childSize.Height;
  508. }
  509. node.Measured = true;
  510. }
  511. public Size GetBoundingSize(bool calcWidth, bool calcHeight)
  512. {
  513. var boundingSize = new Size();
  514. foreach (var node in _nodeDic.Values)
  515. {
  516. var size = node.GetBoundingSize();
  517. boundingSize.Width = Math.Max(boundingSize.Width, size.Width);
  518. boundingSize.Height = Math.Max(boundingSize.Height, size.Height);
  519. }
  520. boundingSize.Width = calcWidth ? boundingSize.Width : AvailableSize.Width;
  521. boundingSize.Height = calcHeight ? boundingSize.Height : AvailableSize.Height;
  522. return boundingSize;
  523. }
  524. }
  525. }