DefinitionBase.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755
  1. // This source file is adapted from the Windows Presentation Foundation project.
  2. // (https://github.com/dotnet/wpf/)
  3. //
  4. // Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
  5. using System;
  6. using System.Collections;
  7. using System.Collections.Generic;
  8. using System.Diagnostics;
  9. using Avalonia;
  10. using Avalonia.Collections;
  11. using Avalonia.Utilities;
  12. namespace Avalonia.Controls
  13. {
  14. /// <summary>
  15. /// DefinitionBase provides core functionality used internally by Grid
  16. /// and ColumnDefinitionCollection / RowDefinitionCollection
  17. /// </summary>
  18. public abstract class DefinitionBase : AvaloniaObject
  19. {
  20. /// <summary>
  21. /// SharedSizeGroup property.
  22. /// </summary>
  23. public string SharedSizeGroup
  24. {
  25. get { return (string)GetValue(SharedSizeGroupProperty); }
  26. set { SetValue(SharedSizeGroupProperty, value); }
  27. }
  28. /// <summary>
  29. /// Callback to notify about entering model tree.
  30. /// </summary>
  31. internal void OnEnterParentTree()
  32. {
  33. this.InheritanceParent = Parent;
  34. if (_sharedState == null)
  35. {
  36. // start with getting SharedSizeGroup value.
  37. // this property is NOT inhereted which should result in better overall perf.
  38. string sharedSizeGroupId = SharedSizeGroup;
  39. if (sharedSizeGroupId != null)
  40. {
  41. SharedSizeScope privateSharedSizeScope = PrivateSharedSizeScope;
  42. if (privateSharedSizeScope != null)
  43. {
  44. _sharedState = privateSharedSizeScope.EnsureSharedState(sharedSizeGroupId);
  45. _sharedState.AddMember(this);
  46. }
  47. }
  48. }
  49. Parent?.InvalidateMeasure();
  50. }
  51. /// <summary>
  52. /// Callback to notify about exitting model tree.
  53. /// </summary>
  54. internal void OnExitParentTree()
  55. {
  56. _offset = 0;
  57. if (_sharedState != null)
  58. {
  59. _sharedState.RemoveMember(this);
  60. _sharedState = null;
  61. }
  62. Parent?.InvalidateMeasure();
  63. }
  64. /// <summary>
  65. /// Performs action preparing definition to enter layout calculation mode.
  66. /// </summary>
  67. internal void OnBeforeLayout(Grid grid)
  68. {
  69. // reset layout state.
  70. _minSize = 0;
  71. LayoutWasUpdated = true;
  72. // defer verification for shared definitions
  73. if (_sharedState != null) { _sharedState.EnsureDeferredValidation(grid); }
  74. }
  75. /// <summary>
  76. /// Updates min size.
  77. /// </summary>
  78. /// <param name="minSize">New size.</param>
  79. internal void UpdateMinSize(double minSize)
  80. {
  81. _minSize = Math.Max(_minSize, minSize);
  82. }
  83. /// <summary>
  84. /// Sets min size.
  85. /// </summary>
  86. /// <param name="minSize">New size.</param>
  87. internal void SetMinSize(double minSize)
  88. {
  89. _minSize = minSize;
  90. }
  91. /// <remarks>
  92. /// This method reflects Grid.SharedScopeProperty state by setting / clearing
  93. /// dynamic property PrivateSharedSizeScopeProperty. Value of PrivateSharedSizeScopeProperty
  94. /// is a collection of SharedSizeState objects for the scope.
  95. /// </remarks>
  96. internal static void OnIsSharedSizeScopePropertyChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e)
  97. {
  98. if ((bool)e.NewValue)
  99. {
  100. SharedSizeScope sharedStatesCollection = new SharedSizeScope();
  101. d.SetValue(PrivateSharedSizeScopeProperty, sharedStatesCollection);
  102. }
  103. else
  104. {
  105. d.ClearValue(PrivateSharedSizeScopeProperty);
  106. }
  107. }
  108. /// <summary>
  109. /// Returns <c>true</c> if this definition is a part of shared group.
  110. /// </summary>
  111. internal bool IsShared
  112. {
  113. get { return (_sharedState != null); }
  114. }
  115. /// <summary>
  116. /// Internal accessor to user size field.
  117. /// </summary>
  118. internal GridLength UserSize
  119. {
  120. get { return (_sharedState != null ? _sharedState.UserSize : UserSizeValueCache); }
  121. }
  122. /// <summary>
  123. /// Internal accessor to user min size field.
  124. /// </summary>
  125. internal double UserMinSize
  126. {
  127. get { return (UserMinSizeValueCache); }
  128. }
  129. /// <summary>
  130. /// Internal accessor to user max size field.
  131. /// </summary>
  132. internal double UserMaxSize
  133. {
  134. get { return (UserMaxSizeValueCache); }
  135. }
  136. /// <summary>
  137. /// DefinitionBase's index in the parents collection.
  138. /// </summary>
  139. internal int Index
  140. {
  141. get
  142. {
  143. return (_parentIndex);
  144. }
  145. set
  146. {
  147. Debug.Assert(value >= -1);
  148. _parentIndex = value;
  149. }
  150. }
  151. /// <summary>
  152. /// Layout-time user size type.
  153. /// </summary>
  154. internal Grid.LayoutTimeSizeType SizeType
  155. {
  156. get { return (_sizeType); }
  157. set { _sizeType = value; }
  158. }
  159. /// <summary>
  160. /// Returns or sets measure size for the definition.
  161. /// </summary>
  162. internal double MeasureSize
  163. {
  164. get { return (_measureSize); }
  165. set { _measureSize = value; }
  166. }
  167. /// <summary>
  168. /// Returns definition's layout time type sensitive preferred size.
  169. /// </summary>
  170. /// <remarks>
  171. /// Returned value is guaranteed to be true preferred size.
  172. /// </remarks>
  173. internal double PreferredSize
  174. {
  175. get
  176. {
  177. double preferredSize = MinSize;
  178. if (_sizeType != Grid.LayoutTimeSizeType.Auto
  179. && preferredSize < _measureSize)
  180. {
  181. preferredSize = _measureSize;
  182. }
  183. return (preferredSize);
  184. }
  185. }
  186. /// <summary>
  187. /// Returns or sets size cache for the definition.
  188. /// </summary>
  189. internal double SizeCache
  190. {
  191. get { return (_sizeCache); }
  192. set { _sizeCache = value; }
  193. }
  194. /// <summary>
  195. /// Returns min size.
  196. /// </summary>
  197. internal double MinSize
  198. {
  199. get
  200. {
  201. double minSize = _minSize;
  202. if (UseSharedMinimum
  203. && _sharedState != null
  204. && minSize < _sharedState.MinSize)
  205. {
  206. minSize = _sharedState.MinSize;
  207. }
  208. return (minSize);
  209. }
  210. }
  211. /// <summary>
  212. /// Returns min size, always taking into account shared state.
  213. /// </summary>
  214. internal double MinSizeForArrange
  215. {
  216. get
  217. {
  218. double minSize = _minSize;
  219. if (_sharedState != null
  220. && (UseSharedMinimum || !LayoutWasUpdated)
  221. && minSize < _sharedState.MinSize)
  222. {
  223. minSize = _sharedState.MinSize;
  224. }
  225. return (minSize);
  226. }
  227. }
  228. /// <summary>
  229. /// Offset.
  230. /// </summary>
  231. internal double FinalOffset
  232. {
  233. get { return _offset; }
  234. set { _offset = value; }
  235. }
  236. /// <summary>
  237. /// Internal helper to access up-to-date UserSize property value.
  238. /// </summary>
  239. internal abstract GridLength UserSizeValueCache { get; }
  240. /// <summary>
  241. /// Internal helper to access up-to-date UserMinSize property value.
  242. /// </summary>
  243. internal abstract double UserMinSizeValueCache { get; }
  244. /// <summary>
  245. /// Internal helper to access up-to-date UserMaxSize property value.
  246. /// </summary>
  247. internal abstract double UserMaxSizeValueCache { get; }
  248. internal Grid Parent { get; set; }
  249. /// <summary>
  250. /// SetFlags is used to set or unset one or multiple
  251. /// flags on the object.
  252. /// </summary>
  253. private void SetFlags(bool value, Flags flags)
  254. {
  255. _flags = value ? (_flags | flags) : (_flags & (~flags));
  256. }
  257. /// <summary>
  258. /// CheckFlagsAnd returns <c>true</c> if all the flags in the
  259. /// given bitmask are set on the object.
  260. /// </summary>
  261. private bool CheckFlagsAnd(Flags flags)
  262. {
  263. return ((_flags & flags) == flags);
  264. }
  265. private static void OnSharedSizeGroupPropertyChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e)
  266. {
  267. DefinitionBase definition = (DefinitionBase)d;
  268. if (definition.Parent != null)
  269. {
  270. string sharedSizeGroupId = (string)e.NewValue;
  271. if (definition._sharedState != null)
  272. {
  273. // if definition is already registered AND shared size group id is changing,
  274. // then un-register the definition from the current shared size state object.
  275. definition._sharedState.RemoveMember(definition);
  276. definition._sharedState = null;
  277. }
  278. if ((definition._sharedState == null) && (sharedSizeGroupId != null))
  279. {
  280. SharedSizeScope privateSharedSizeScope = definition.PrivateSharedSizeScope;
  281. if (privateSharedSizeScope != null)
  282. {
  283. // if definition is not registered and both: shared size group id AND private shared scope
  284. // are available, then register definition.
  285. definition._sharedState = privateSharedSizeScope.EnsureSharedState(sharedSizeGroupId);
  286. definition._sharedState.AddMember(definition);
  287. }
  288. }
  289. }
  290. }
  291. /// <remarks>
  292. /// Verifies that Shared Size Group Property string
  293. /// a) not empty.
  294. /// b) contains only letters, digits and underscore ('_').
  295. /// c) does not start with a digit.
  296. /// </remarks>
  297. private static string SharedSizeGroupPropertyValueValid(Control _, string value)
  298. {
  299. Contract.Requires<ArgumentNullException>(value != null);
  300. string id = (string)value;
  301. if (id != string.Empty)
  302. {
  303. int i = -1;
  304. while (++i < id.Length)
  305. {
  306. bool isDigit = Char.IsDigit(id[i]);
  307. if ((i == 0 && isDigit)
  308. || !(isDigit
  309. || Char.IsLetter(id[i])
  310. || '_' == id[i]))
  311. {
  312. break;
  313. }
  314. }
  315. if (i == id.Length)
  316. {
  317. return value;
  318. }
  319. }
  320. throw new ArgumentException("Invalid SharedSizeGroup string.");
  321. }
  322. /// <remark>
  323. /// OnPrivateSharedSizeScopePropertyChanged is called when new scope enters or
  324. /// existing scope just left. In both cases if the DefinitionBase object is already registered
  325. /// in SharedSizeState, it should un-register and register itself in a new one.
  326. /// </remark>
  327. private static void OnPrivateSharedSizeScopePropertyChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e)
  328. {
  329. DefinitionBase definition = (DefinitionBase)d;
  330. if (definition.Parent != null)
  331. {
  332. SharedSizeScope privateSharedSizeScope = (SharedSizeScope)e.NewValue;
  333. if (definition._sharedState != null)
  334. {
  335. // if definition is already registered And shared size scope is changing,
  336. // then un-register the definition from the current shared size state object.
  337. definition._sharedState.RemoveMember(definition);
  338. definition._sharedState = null;
  339. }
  340. if ((definition._sharedState == null) && (privateSharedSizeScope != null))
  341. {
  342. string sharedSizeGroup = definition.SharedSizeGroup;
  343. if (sharedSizeGroup != null)
  344. {
  345. // if definition is not registered and both: shared size group id AND private shared scope
  346. // are available, then register definition.
  347. definition._sharedState = privateSharedSizeScope.EnsureSharedState(definition.SharedSizeGroup);
  348. definition._sharedState.AddMember(definition);
  349. }
  350. }
  351. }
  352. }
  353. /// <summary>
  354. /// Private getter of shared state collection dynamic property.
  355. /// </summary>
  356. private SharedSizeScope PrivateSharedSizeScope
  357. {
  358. get { return (SharedSizeScope)GetValue(PrivateSharedSizeScopeProperty); }
  359. }
  360. /// <summary>
  361. /// Convenience accessor to UseSharedMinimum flag
  362. /// </summary>
  363. private bool UseSharedMinimum
  364. {
  365. get { return (CheckFlagsAnd(Flags.UseSharedMinimum)); }
  366. set { SetFlags(value, Flags.UseSharedMinimum); }
  367. }
  368. /// <summary>
  369. /// Convenience accessor to LayoutWasUpdated flag
  370. /// </summary>
  371. private bool LayoutWasUpdated
  372. {
  373. get { return (CheckFlagsAnd(Flags.LayoutWasUpdated)); }
  374. set { SetFlags(value, Flags.LayoutWasUpdated); }
  375. }
  376. private Flags _flags; // flags reflecting various aspects of internal state
  377. internal int _parentIndex = -1; // this instance's index in parent's children collection
  378. private Grid.LayoutTimeSizeType _sizeType; // layout-time user size type. it may differ from _userSizeValueCache.UnitType when calculating "to-content"
  379. private double _minSize; // used during measure to accumulate size for "Auto" and "Star" DefinitionBase's
  380. private double _measureSize; // size, calculated to be the input contstraint size for Child.Measure
  381. private double _sizeCache; // cache used for various purposes (sorting, caching, etc) during calculations
  382. private double _offset; // offset of the DefinitionBase from left / top corner (assuming LTR case)
  383. private SharedSizeState _sharedState; // reference to shared state object this instance is registered with
  384. [System.Flags]
  385. private enum Flags : byte
  386. {
  387. //
  388. // bool flags
  389. //
  390. UseSharedMinimum = 0x00000020, // when "1", definition will take into account shared state's minimum
  391. LayoutWasUpdated = 0x00000040, // set to "1" every time the parent grid is measured
  392. }
  393. /// <summary>
  394. /// Collection of shared states objects for a single scope
  395. /// </summary>
  396. internal class SharedSizeScope
  397. {
  398. /// <summary>
  399. /// Returns SharedSizeState object for a given group.
  400. /// Creates a new StatedState object if necessary.
  401. /// </summary>
  402. internal SharedSizeState EnsureSharedState(string sharedSizeGroup)
  403. {
  404. // check that sharedSizeGroup is not default
  405. Debug.Assert(sharedSizeGroup != null);
  406. SharedSizeState sharedState = _registry[sharedSizeGroup] as SharedSizeState;
  407. if (sharedState == null)
  408. {
  409. sharedState = new SharedSizeState(this, sharedSizeGroup);
  410. _registry[sharedSizeGroup] = sharedState;
  411. }
  412. return (sharedState);
  413. }
  414. /// <summary>
  415. /// Removes an entry in the registry by the given key.
  416. /// </summary>
  417. internal void Remove(object key)
  418. {
  419. Debug.Assert(_registry.Contains(key));
  420. _registry.Remove(key);
  421. }
  422. private Hashtable _registry = new Hashtable(); // storage for shared state objects
  423. }
  424. /// <summary>
  425. /// Implementation of per shared group state object
  426. /// </summary>
  427. internal class SharedSizeState
  428. {
  429. /// <summary>
  430. /// Default ctor.
  431. /// </summary>
  432. internal SharedSizeState(SharedSizeScope sharedSizeScope, string sharedSizeGroupId)
  433. {
  434. Debug.Assert(sharedSizeScope != null && sharedSizeGroupId != null);
  435. _sharedSizeScope = sharedSizeScope;
  436. _sharedSizeGroupId = sharedSizeGroupId;
  437. _registry = new List<DefinitionBase>();
  438. _layoutUpdated = new EventHandler(OnLayoutUpdated);
  439. _broadcastInvalidation = true;
  440. }
  441. /// <summary>
  442. /// Adds / registers a definition instance.
  443. /// </summary>
  444. internal void AddMember(DefinitionBase member)
  445. {
  446. Debug.Assert(!_registry.Contains(member));
  447. _registry.Add(member);
  448. Invalidate();
  449. }
  450. /// <summary>
  451. /// Removes / un-registers a definition instance.
  452. /// </summary>
  453. /// <remarks>
  454. /// If the collection of registered definitions becomes empty
  455. /// instantiates self removal from owner's collection.
  456. /// </remarks>
  457. internal void RemoveMember(DefinitionBase member)
  458. {
  459. Invalidate();
  460. _registry.Remove(member);
  461. if (_registry.Count == 0)
  462. {
  463. _sharedSizeScope.Remove(_sharedSizeGroupId);
  464. }
  465. }
  466. /// <summary>
  467. /// Propogates invalidations for all registered definitions.
  468. /// Resets its own state.
  469. /// </summary>
  470. internal void Invalidate()
  471. {
  472. _userSizeValid = false;
  473. if (_broadcastInvalidation)
  474. {
  475. for (int i = 0, count = _registry.Count; i < count; ++i)
  476. {
  477. Grid parentGrid = (Grid)(_registry[i].Parent);
  478. parentGrid.Invalidate();
  479. }
  480. _broadcastInvalidation = false;
  481. }
  482. }
  483. /// <summary>
  484. /// Makes sure that one and only one layout updated handler is registered for this shared state.
  485. /// </summary>
  486. internal void EnsureDeferredValidation(Control layoutUpdatedHost)
  487. {
  488. if (_layoutUpdatedHost == null)
  489. {
  490. _layoutUpdatedHost = layoutUpdatedHost;
  491. _layoutUpdatedHost.LayoutUpdated += _layoutUpdated;
  492. }
  493. }
  494. /// <summary>
  495. /// DefinitionBase's specific code.
  496. /// </summary>
  497. internal double MinSize
  498. {
  499. get
  500. {
  501. if (!_userSizeValid) { EnsureUserSizeValid(); }
  502. return (_minSize);
  503. }
  504. }
  505. /// <summary>
  506. /// DefinitionBase's specific code.
  507. /// </summary>
  508. internal GridLength UserSize
  509. {
  510. get
  511. {
  512. if (!_userSizeValid) { EnsureUserSizeValid(); }
  513. return (_userSize);
  514. }
  515. }
  516. private void EnsureUserSizeValid()
  517. {
  518. _userSize = new GridLength(1, GridUnitType.Auto);
  519. for (int i = 0, count = _registry.Count; i < count; ++i)
  520. {
  521. Debug.Assert(_userSize.GridUnitType == GridUnitType.Auto
  522. || _userSize.GridUnitType == GridUnitType.Pixel);
  523. GridLength currentGridLength = _registry[i].UserSizeValueCache;
  524. if (currentGridLength.GridUnitType == GridUnitType.Pixel)
  525. {
  526. if (_userSize.GridUnitType == GridUnitType.Auto)
  527. {
  528. _userSize = currentGridLength;
  529. }
  530. else if (_userSize.Value < currentGridLength.Value)
  531. {
  532. _userSize = currentGridLength;
  533. }
  534. }
  535. }
  536. // taking maximum with user size effectively prevents squishy-ness.
  537. // this is a "solution" to avoid shared definitions from been sized to
  538. // different final size at arrange time, if / when different grids receive
  539. // different final sizes.
  540. _minSize = _userSize.IsAbsolute ? _userSize.Value : 0.0;
  541. _userSizeValid = true;
  542. }
  543. /// <summary>
  544. /// OnLayoutUpdated handler. Validates that all participating definitions
  545. /// have updated min size value. Forces another layout update cycle if needed.
  546. /// </summary>
  547. private void OnLayoutUpdated(object sender, EventArgs e)
  548. {
  549. double sharedMinSize = 0;
  550. // accumulate min size of all participating definitions
  551. for (int i = 0, count = _registry.Count; i < count; ++i)
  552. {
  553. sharedMinSize = Math.Max(sharedMinSize, _registry[i].MinSize);
  554. }
  555. bool sharedMinSizeChanged = !MathUtilities.AreClose(_minSize, sharedMinSize);
  556. // compare accumulated min size with min sizes of the individual definitions
  557. for (int i = 0, count = _registry.Count; i < count; ++i)
  558. {
  559. DefinitionBase definitionBase = _registry[i];
  560. if (sharedMinSizeChanged || definitionBase.LayoutWasUpdated)
  561. {
  562. // if definition's min size is different, then need to re-measure
  563. if (!MathUtilities.AreClose(sharedMinSize, definitionBase.MinSize))
  564. {
  565. Grid parentGrid = (Grid)definitionBase.Parent;
  566. parentGrid.InvalidateMeasure();
  567. definitionBase.UseSharedMinimum = true;
  568. }
  569. else
  570. {
  571. definitionBase.UseSharedMinimum = false;
  572. // if measure is valid then also need to check arrange.
  573. // Note: definitionBase.SizeCache is volatile but at this point
  574. // it contains up-to-date final size
  575. if (!MathUtilities.AreClose(sharedMinSize, definitionBase.SizeCache))
  576. {
  577. Grid parentGrid = (Grid)definitionBase.Parent;
  578. parentGrid.InvalidateArrange();
  579. }
  580. }
  581. definitionBase.LayoutWasUpdated = false;
  582. }
  583. }
  584. _minSize = sharedMinSize;
  585. _layoutUpdatedHost.LayoutUpdated -= _layoutUpdated;
  586. _layoutUpdatedHost = null;
  587. _broadcastInvalidation = true;
  588. }
  589. // the scope this state belongs to
  590. private readonly SharedSizeScope _sharedSizeScope;
  591. // Id of the shared size group this object is servicing
  592. private readonly string _sharedSizeGroupId;
  593. // Registry of participating definitions
  594. private readonly List<DefinitionBase> _registry;
  595. // Instance event handler for layout updated event
  596. private readonly EventHandler _layoutUpdated;
  597. // Control for which layout updated event handler is registered
  598. private Control _layoutUpdatedHost;
  599. // "true" when broadcasting of invalidation is needed
  600. private bool _broadcastInvalidation;
  601. // "true" when _userSize is up to date
  602. private bool _userSizeValid;
  603. // shared state
  604. private GridLength _userSize;
  605. // shared state
  606. private double _minSize;
  607. }
  608. /// <summary>
  609. /// Private shared size scope property holds a collection of shared state objects for the a given shared size scope.
  610. /// <see cref="OnIsSharedSizeScopePropertyChanged"/>
  611. /// </summary>
  612. internal static readonly AttachedProperty<SharedSizeScope> PrivateSharedSizeScopeProperty =
  613. AvaloniaProperty.RegisterAttached<DefinitionBase, Control, SharedSizeScope>(
  614. "PrivateSharedSizeScope",
  615. defaultValue: null,
  616. inherits: true);
  617. /// <summary>
  618. /// Shared size group property marks column / row definition as belonging to a group "Foo" or "Bar".
  619. /// </summary>
  620. /// <remarks>
  621. /// Value of the Shared Size Group Property must satisfy the following rules:
  622. /// <list type="bullet">
  623. /// <item><description>
  624. /// String must not be empty.
  625. /// </description></item>
  626. /// <item><description>
  627. /// String must consist of letters, digits and underscore ('_') only.
  628. /// </description></item>
  629. /// <item><description>
  630. /// String must not start with a digit.
  631. /// </description></item>
  632. /// </list>
  633. /// </remarks>
  634. public static readonly AttachedProperty<string> SharedSizeGroupProperty =
  635. AvaloniaProperty.RegisterAttached<DefinitionBase, Control, string>(
  636. "SharedSizeGroup",
  637. validate: SharedSizeGroupPropertyValueValid);
  638. /// <summary>
  639. /// Static ctor. Used for static registration of properties.
  640. /// </summary>
  641. static DefinitionBase()
  642. {
  643. SharedSizeGroupProperty.Changed.AddClassHandler<DefinitionBase>(OnSharedSizeGroupPropertyChanged);
  644. PrivateSharedSizeScopeProperty.Changed.AddClassHandler<DefinitionBase>(OnPrivateSharedSizeScopePropertyChanged);
  645. }
  646. /// <summary>
  647. /// Marks a property on a definition as affecting the parent grid's measurement.
  648. /// </summary>
  649. /// <param name="properties">The properties.</param>
  650. protected static void AffectsParentMeasure(params AvaloniaProperty[] properties)
  651. {
  652. void Invalidate(AvaloniaPropertyChangedEventArgs e)
  653. {
  654. (e.Sender as DefinitionBase)?.Parent?.InvalidateMeasure();
  655. }
  656. foreach (var property in properties)
  657. {
  658. property.Changed.Subscribe(Invalidate);
  659. }
  660. }
  661. }
  662. }