diff --git a/src/Avalonia.Controls/Grid/ColumnDefinition.cs b/src/Avalonia.Controls/Grid/ColumnDefinition.cs
index 56a6d41b7b..015484dbcc 100644
--- a/src/Avalonia.Controls/Grid/ColumnDefinition.cs
+++ b/src/Avalonia.Controls/Grid/ColumnDefinition.cs
@@ -88,10 +88,10 @@ namespace Avalonia.Controls
set { SetValue(WidthProperty, value); }
}
- internal override GridLength UserSize => this.Width;
+ internal override GridLength UserSizeValueCache => this.Width;
- internal override double UserMinSize => this.MinWidth;
+ internal override double UserMinSizeValueCache => this.MinWidth;
- internal override double UserMaxSize => this.MaxWidth;
+ internal override double UserMaxSizeValueCache => this.MaxWidth;
}
}
diff --git a/src/Avalonia.Controls/Grid/DefinitionBase.cs b/src/Avalonia.Controls/Grid/DefinitionBase.cs
index ad2f33a18a..e21d55c1f6 100644
--- a/src/Avalonia.Controls/Grid/DefinitionBase.cs
+++ b/src/Avalonia.Controls/Grid/DefinitionBase.cs
@@ -3,9 +3,7 @@
using System;
using System.Collections;
-using System.Collections.Generic;
using System.Diagnostics;
-using Avalonia.Utilities;
namespace Avalonia.Controls
{
@@ -14,6 +12,26 @@ namespace Avalonia.Controls
///
public abstract class DefinitionBase : AvaloniaObject
{
+ ///
+ /// Static ctor. Used for static registration of properties.
+ ///
+ static DefinitionBase()
+ {
+ SharedSizeGroupProperty.Changed.AddClassHandler(OnSharedSizeGroupPropertyChanged);
+ }
+ internal bool UseSharedMinimum { get; set; }
+ internal bool LayoutWasUpdated { get; set; }
+
+ private int _parentIndex = -1; // this instance's index in parent's children collection
+ private LayoutTimeSizeType _sizeType; // layout-time user size type. it may differ from _userSizeValueCache.UnitType when calculating "to-content"
+ private double _minSize; // used during measure to accumulate size for "Auto" and "Star" DefinitionBase's
+ private double _measureSize; // size, calculated to be the input contstraint size for Child.Measure
+ private double _sizeCache; // cache used for various purposes (sorting, caching, etc) during calculations
+ private double _offset; // offset of the DefinitionBase from left / top corner (assuming LTR case)
+ internal SharedSizeScope _privateSharedSizeScope;
+ private SharedSizeState _sharedState; // reference to shared state object this instance is registered with
+ private bool _successUpdateSharedScope;
+
///
/// Defines the property.
///
@@ -28,31 +46,182 @@ namespace Avalonia.Controls
get { return GetValue(SharedSizeGroupProperty); }
set { SetValue(SharedSizeGroupProperty, value); }
}
+ ///
+ /// Callback to notify about entering model tree.
+ ///
+ internal void OnEnterParentTree(Grid grid, int index)
+ {
+ Parent = grid;
+ _parentIndex = index;
+ }
+
+ internal void UpdateSharedScope()
+ {
+ if (_sharedState == null &
+ SharedSizeGroup != null &
+ Parent?.sharedSizeScope != null &
+ !_successUpdateSharedScope)
+ {
+ _privateSharedSizeScope = Parent.sharedSizeScope;
+ _sharedState = _privateSharedSizeScope.EnsureSharedState(SharedSizeGroup);
+ _sharedState.AddMember(this);
+ _successUpdateSharedScope = true;
+ }
+ }
+
+ internal Grid Parent { get; set; }
///
- /// Internal helper to access up-to-date UserSize property value.
+ /// Callback to notify about exitting model tree.
///
- internal abstract GridLength UserSize { get; }
+ internal void OnExitParentTree()
+ {
+ _offset = 0;
+ if (_sharedState != null)
+ {
+ _sharedState.RemoveMember(this);
+ _sharedState = null;
+ }
+ }
///
- /// Internal helper to access up-to-date UserMinSize property value.
+ /// Performs action preparing definition to enter layout calculation mode.
///
- internal abstract double UserMinSize { get; }
+ internal void OnBeforeLayout(Grid grid)
+ {
+ if (SharedSizeGroup != null)
+ UpdateSharedScope();
+
+ // reset layout state.
+ _minSize = 0;
+ LayoutWasUpdated = true;
+
+ // defer verification for shared definitions
+ if (_sharedState != null)
+ {
+ _sharedState.EnsureDeferredValidation(grid);
+ }
+ }
///
- /// Internal helper to access up-to-date UserMaxSize property value.
+ /// Updates min size.
+ ///
+ /// New size.
+ internal void UpdateMinSize(double minSize)
+ {
+ _minSize = Math.Max(_minSize, minSize);
+ }
+
+ ///
+ /// Sets min size.
+ ///
+ /// New size.
+ internal void SetMinSize(double minSize)
+ {
+ _minSize = minSize;
+ }
+
+ ///
+ /// This method needs to be internal to be accessable from derived classes.
+ ///
+ internal void OnUserSizePropertyChanged(AvaloniaPropertyChangedEventArgs e)
+ {
+ _sharedState?.Invalidate();
+ }
+
+ ///
+ /// This method needs to be internal to be accessable from derived classes.
+ ///
+ internal static bool IsUserMinSizePropertyValueValid(object value)
+ {
+ double v = (double)value;
+ return (!double.IsNaN(v) && v >= 0.0d && !Double.IsPositiveInfinity(v));
+ }
+
+ ///
+ /// This method needs to be internal to be accessable from derived classes.
+ ///
+ internal static void OnUserMaxSizePropertyChanged(DefinitionBase definition, AvaloniaPropertyChangedEventArgs e)
+ {
+ Grid parentGrid = (Grid)definition.Parent;
+ parentGrid.InvalidateMeasure();
+
+ }
+
+ ///
+ /// This method needs to be internal to be accessable from derived classes.
+ ///
+ internal static bool IsUserMaxSizePropertyValueValid(object value)
+ {
+ double v = (double)value;
+ return (!double.IsNaN(v) && v >= 0.0d);
+ }
+
+ ///
+ /// Returns true if this definition is a part of shared group.
+ ///
+ internal bool IsShared
+ {
+ get { return (_sharedState != null); }
+ }
+
+ ///
+ /// Internal accessor to user size field.
+ ///
+ internal GridLength UserSize
+ {
+ get { return (_sharedState != null ? _sharedState.UserSize : UserSizeValueCache); }
+ }
+
+ ///
+ /// Internal accessor to user min size field.
+ ///
+ internal double UserMinSize
+ {
+ get { return (UserMinSizeValueCache); }
+ }
+
+ ///
+ /// Internal accessor to user max size field.
///
- internal abstract double UserMaxSize { get; }
+ internal double UserMaxSize
+ {
+ get { return (UserMaxSizeValueCache); }
+ }
+
+ ///
+ /// DefinitionBase's index in the parents collection.
+ ///
+ internal int Index
+ {
+ get
+ {
+ return (_parentIndex);
+ }
+ set
+ {
+ Debug.Assert(value >= -1 && _parentIndex != value);
+ _parentIndex = value;
+ }
+ }
///
/// Layout-time user size type.
///
- internal LayoutTimeSizeType SizeType { get; set; }
-
+ internal LayoutTimeSizeType SizeType
+ {
+ get { return (_sizeType); }
+ set { _sizeType = value; }
+ }
+
///
/// Returns or sets measure size for the definition.
///
- internal double MeasureSize { get; set; }
+ internal double MeasureSize
+ {
+ get { return (_measureSize); }
+ set { _measureSize = value; }
+ }
///
/// Returns definition's layout time type sensitive preferred size.
@@ -65,37 +234,108 @@ namespace Avalonia.Controls
get
{
double preferredSize = MinSize;
- if (SizeType != LayoutTimeSizeType.Auto
- && preferredSize < MeasureSize)
+ if (_sizeType != LayoutTimeSizeType.Auto
+ && preferredSize < _measureSize)
{
- preferredSize = MeasureSize;
+ preferredSize = _measureSize;
}
- return (preferredSize);
+ return (preferredSize);
}
}
///
/// Returns or sets size cache for the definition.
///
- internal double SizeCache { get; set; }
+ internal double SizeCache
+ {
+ get { return (_sizeCache); }
+ set { _sizeCache = value; }
+ }
///
- /// Used during measure and arrange to accumulate size for "Auto" and "Star" DefinitionBase's
+ /// Returns min size.
///
- internal double MinSize { get; set; }
+ internal double MinSize
+ {
+ get
+ {
+ double minSize = _minSize;
+ if (UseSharedMinimum
+ && _sharedState != null
+ && minSize < _sharedState.MinSize)
+ {
+ minSize = _sharedState.MinSize;
+ }
+ return (minSize);
+ }
+ }
///
- /// Updates min size.
+ /// Returns min size, always taking into account shared state.
///
- /// New size.
- internal void UpdateMinSize(double minSize)
+ internal double MinSizeForArrange
{
- MinSize = Math.Max(MinSize, minSize);
+ get
+ {
+ double minSize = _minSize;
+ if (_sharedState != null
+ && (UseSharedMinimum || !LayoutWasUpdated)
+ && minSize < _sharedState.MinSize)
+ {
+ minSize = _sharedState.MinSize;
+ }
+ return (minSize);
+ }
}
///
/// Offset.
///
- internal double FinalOffset { get; set; }
+ internal double FinalOffset
+ {
+ get { return _offset; }
+ set { _offset = value; }
+ }
+
+ ///
+ /// Internal helper to access up-to-date UserSize property value.
+ ///
+ internal abstract GridLength UserSizeValueCache { get; }
+
+ ///
+ /// Internal helper to access up-to-date UserMinSize property value.
+ ///
+ internal abstract double UserMinSizeValueCache { get; }
+
+ ///
+ /// Internal helper to access up-to-date UserMaxSize property value.
+ ///
+ internal abstract double UserMaxSizeValueCache { get; }
+
+ private static void OnSharedSizeGroupPropertyChanged(DefinitionBase definition, AvaloniaPropertyChangedEventArgs e)
+ {
+ string sharedSizeGroupId = (string)e.NewValue;
+
+ if (definition._sharedState != null)
+ {
+ // if definition is already registered AND shared size group id is changing,
+ // then un-register the definition from the current shared size state object.
+ definition._sharedState.RemoveMember(definition);
+ definition._sharedState = null;
+ }
+
+ if ((definition._sharedState == null) && (sharedSizeGroupId != null))
+ {
+ var privateSharedSizeScope = definition._privateSharedSizeScope;
+ if (privateSharedSizeScope != null)
+ {
+ // if definition is not registered and both: shared size group id AND private shared scope
+ // are available, then register definition.
+ definition._sharedState = privateSharedSizeScope.EnsureSharedState(sharedSizeGroupId);
+ definition._sharedState.AddMember(definition);
+
+ }
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/Avalonia.Controls/Grid/Grid.cs b/src/Avalonia.Controls/Grid/Grid.cs
index 449b92ac52..c08c4396c5 100644
--- a/src/Avalonia.Controls/Grid/Grid.cs
+++ b/src/Avalonia.Controls/Grid/Grid.cs
@@ -86,6 +86,7 @@ namespace Avalonia.Controls
private RowDefinitions _rowDefinitions;
private DefinitionBase[] _definitionsU = new DefinitionBase[1] { new ColumnDefinition() };
private DefinitionBase[] _definitionsV = new DefinitionBase[1] { new RowDefinition() };
+ internal SharedSizeScope sharedSizeScope;
// 5 is an arbitrary constant chosen to end the measure loop
private const int _layoutLoopMaxCount = 5;
@@ -171,6 +172,9 @@ namespace Avalonia.Controls
static Grid()
{
ShowGridLinesProperty.Changed.AddClassHandler(OnShowGridLinesPropertyChanged);
+ IsSharedSizeScopeProperty.Changed.AddClassHandler(IsSharedSizeScopePropertyChanged);
+ BoundsProperty.Changed.AddClassHandler(BoundsPropertyChanged);
+
AffectsParentMeasure(ColumnProperty, ColumnSpanProperty, RowProperty, RowSpanProperty);
_tempDefinitionsDataSlot = Thread.AllocateDataSlot();
@@ -181,6 +185,26 @@ namespace Avalonia.Controls
_starWeightComparer = new StarWeightComparer();
}
+ private static void BoundsPropertyChanged(Grid grid, AvaloniaPropertyChangedEventArgs arg2)
+ {
+ for (int i = 0; i < grid._definitionsU.Length; i++)
+ grid._definitionsU[i].OnUserSizePropertyChanged(arg2);
+ for (int i = 0; i < grid._definitionsV.Length; i++)
+ grid._definitionsV[i].OnUserSizePropertyChanged(arg2);
+ }
+
+ private static void IsSharedSizeScopePropertyChanged(Grid grid, AvaloniaPropertyChangedEventArgs e)
+ {
+ if ((bool)e.NewValue)
+ {
+ grid.sharedSizeScope = new SharedSizeScope();
+ }
+ else
+ {
+ grid.sharedSizeScope = null;
+ }
+ }
+
///
/// Defines the Column attached property.
///
@@ -252,8 +276,12 @@ namespace Avalonia.Controls
if (_columnDefinitions.Count > 0)
_definitionsU = _columnDefinitions.Cast().ToArray();
+ CallEnterParentTree(_definitionsU);
+
_columnDefinitions.CollectionChanged += delegate
{
+ CallExitParentTree(_definitionsU);
+
if (_columnDefinitions.Count == 0)
{
_definitionsU = new DefinitionBase[1] { new ColumnDefinition() };
@@ -263,11 +291,26 @@ namespace Avalonia.Controls
_definitionsU = _columnDefinitions.Cast().ToArray();
DefinitionsDirty = true;
}
+
+ CallEnterParentTree(_definitionsU);
+
Invalidate();
};
}
}
+ private void CallEnterParentTree(DefinitionBase[] definitionsU)
+ {
+ for (int i = 0; i < definitionsU.Length; i++)
+ definitionsU[i].OnEnterParentTree(this, i);
+ }
+
+ private void CallExitParentTree(DefinitionBase[] definitionsU)
+ {
+ for (int i = 0; i < definitionsU.Length; i++)
+ definitionsU[i].OnExitParentTree();
+ }
+
///
/// Gets or sets the row definitions for the grid.
///
@@ -294,6 +337,8 @@ namespace Avalonia.Controls
_rowDefinitions.CollectionChanged += delegate
{
+ CallExitParentTree(_definitionsU);
+
if (_rowDefinitions.Count == 0)
{
_definitionsV = new DefinitionBase[1] { new RowDefinition() };
@@ -303,6 +348,8 @@ namespace Avalonia.Controls
_definitionsV = _rowDefinitions.Cast().ToArray();
DefinitionsDirty = true;
}
+ CallEnterParentTree(_definitionsU);
+
Invalidate();
};
}
@@ -359,6 +406,16 @@ namespace Avalonia.Controls
return element.GetValue(IsSharedSizeScopeProperty);
}
+ ///
+ /// Sets the value of the IsSharedSizeScope attached property for a control.
+ ///
+ /// The control.
+ /// The control's IsSharedSizeScope value.
+ public static void SetIsSharedSizeScope(AvaloniaObject element, bool value)
+ {
+ element.SetValue(IsSharedSizeScopeProperty, value);
+ }
+
///
/// Sets the value of the Column attached property for a control.
///
@@ -659,7 +716,7 @@ namespace Avalonia.Controls
///
/// Invalidates grid caches and makes the grid dirty for measure.
///
- private void Invalidate()
+ internal void Invalidate()
{
CellsStructureDirty = true;
InvalidateMeasure();
@@ -780,8 +837,7 @@ namespace Avalonia.Controls
{
for (int i = 0; i < definitions.Length; ++i)
{
- // Reset minimum size.
- definitions[i].MinSize = 0;
+ definitions[i].OnBeforeLayout(this);
double userMinSize = definitions[i].UserMinSize;
double userMaxSize = definitions[i].UserMaxSize;
@@ -858,11 +914,11 @@ namespace Avalonia.Controls
{
if (isRows)
{
- _definitionsV[i].MinSize = minSizes[i];
+ _definitionsV[i].SetMinSize(minSizes[i]);
}
else
{
- _definitionsU[i].MinSize = minSizes[i];
+ _definitionsU[i].SetMinSize(minSizes[i]);
}
}
}
@@ -1710,7 +1766,7 @@ namespace Avalonia.Controls
if (def.UserSize.IsStar)
{
- // Debug.Assert(!def.IsShared, "*-defs cannot be shared");
+ Debug.Assert(!def.IsShared, "*-defs cannot be shared");
if (def.MeasureSize < 0.0)
{
@@ -1721,14 +1777,14 @@ namespace Avalonia.Controls
double starWeight = StarWeight(def, scale);
totalStarWeight += starWeight;
- if (def.MinSize > 0.0)
+ if (def.MinSizeForArrange > 0.0)
{
// store ratio w/min in MeasureSize (for now)
definitionIndices[minCount++] = i;
- def.MeasureSize = starWeight / def.MinSize;
+ def.MeasureSize = starWeight / def.MinSizeForArrange;
}
- double effectiveMaxSize = Math.Max(def.MinSize, def.UserMaxSize);
+ double effectiveMaxSize = Math.Max(def.MinSizeForArrange, def.UserMaxSize);
if (!double.IsPositiveInfinity(effectiveMaxSize))
{
// store ratio w/max in SizeCache (for now)
@@ -1748,26 +1804,26 @@ namespace Avalonia.Controls
break;
case (GridUnitType.Auto):
- userSize = def.MinSize;
+ userSize = def.MinSizeForArrange;
break;
}
double userMaxSize;
- // if (def.IsShared)
- // {
- // // overriding userMaxSize effectively prevents squishy-ness.
- // // this is a "solution" to avoid shared definitions from been sized to
- // // different final size at arrange time, if / when different grids receive
- // // different final sizes.
- // userMaxSize = userSize;
- // }
- // else
- // {
- userMaxSize = def.UserMaxSize;
- // }
-
- def.SizeCache = Math.Max(def.MinSize, Math.Min(userSize, userMaxSize));
+ if (def.IsShared)
+ {
+ // overriding userMaxSize effectively prevents squishy-ness.
+ // this is a "solution" to avoid shared definitions from been sized to
+ // different final size at arrange time, if / when different grids receive
+ // different final sizes.
+ userMaxSize = userSize;
+ }
+ else
+ {
+ userMaxSize = def.UserMaxSize;
+ }
+
+ def.SizeCache = Math.Max(def.MinSizeForArrange, Math.Min(userSize, userMaxSize));
takenSize += def.SizeCache;
}
}
@@ -1831,14 +1887,14 @@ namespace Avalonia.Controls
{
resolvedIndex = definitionIndices[minCount - 1];
resolvedDef = definitions[resolvedIndex];
- resolvedSize = resolvedDef.MinSize;
+ resolvedSize = resolvedDef.MinSizeForArrange;
--minCount;
}
else
{
resolvedIndex = definitionIndices[defCount + maxCount - 1];
resolvedDef = definitions[resolvedIndex];
- resolvedSize = Math.Max(resolvedDef.MinSize, resolvedDef.UserMaxSize);
+ resolvedSize = Math.Max(resolvedDef.MinSizeForArrange, resolvedDef.UserMaxSize);
--maxCount;
}
@@ -1961,7 +2017,7 @@ namespace Avalonia.Controls
// min and max should have no effect by now, but just in case...
resolvedSize = Math.Min(resolvedSize, def.UserMaxSize);
- resolvedSize = Math.Max(def.MinSize, resolvedSize);
+ resolvedSize = Math.Max(def.MinSizeForArrange, resolvedSize);
// Use the raw (unrounded) sizes to update takenSize, so that
// proportions are computed in the same terms as in phase 3;
@@ -2053,7 +2109,7 @@ namespace Avalonia.Controls
{
DefinitionBase definition = definitions[definitionIndices[i]];
double final = definition.SizeCache - dpiIncrement;
- final = Math.Max(final, definition.MinSize);
+ final = Math.Max(final, definition.MinSizeForArrange);
if (final < definition.SizeCache)
{
adjustedSize -= dpiIncrement;
@@ -2069,7 +2125,7 @@ namespace Avalonia.Controls
{
DefinitionBase definition = definitions[definitionIndices[i]];
double final = definition.SizeCache + dpiIncrement;
- final = Math.Max(final, definition.MinSize);
+ final = Math.Max(final, definition.MinSizeForArrange);
if (final > definition.SizeCache)
{
adjustedSize += dpiIncrement;
diff --git a/src/Avalonia.Controls/Grid/RowDefinition.cs b/src/Avalonia.Controls/Grid/RowDefinition.cs
index d9120b010e..1cb09e16e9 100644
--- a/src/Avalonia.Controls/Grid/RowDefinition.cs
+++ b/src/Avalonia.Controls/Grid/RowDefinition.cs
@@ -89,10 +89,10 @@ namespace Avalonia.Controls
}
- internal override GridLength UserSize => this.Height;
+ internal override GridLength UserSizeValueCache => this.Height;
- internal override double UserMinSize => this.MinHeight;
+ internal override double UserMinSizeValueCache => this.MinHeight;
- internal override double UserMaxSize => this.MaxHeight;
+ internal override double UserMaxSizeValueCache => this.MaxHeight;
}
}
\ No newline at end of file
diff --git a/src/Avalonia.Controls/Grid/SharedSizeScope.cs b/src/Avalonia.Controls/Grid/SharedSizeScope.cs
new file mode 100644
index 0000000000..6835d13132
--- /dev/null
+++ b/src/Avalonia.Controls/Grid/SharedSizeScope.cs
@@ -0,0 +1,43 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections;
+using System.Diagnostics;
+
+namespace Avalonia.Controls
+{
+ ///
+ /// Collection of shared states objects for a single scope
+ ///
+ internal class SharedSizeScope
+ {
+ ///
+ /// Returns SharedSizeState object for a given group.
+ /// Creates a new StatedState object if necessary.
+ ///
+ internal SharedSizeState EnsureSharedState(string sharedSizeGroup)
+ {
+ // check that sharedSizeGroup is not default
+ Debug.Assert(sharedSizeGroup != null);
+
+ SharedSizeState sharedState = _registry[sharedSizeGroup] as SharedSizeState;
+ if (sharedState == null)
+ {
+ sharedState = new SharedSizeState(this, sharedSizeGroup);
+ _registry[sharedSizeGroup] = sharedState;
+ }
+ return (sharedState);
+ }
+
+ ///
+ /// Removes an entry in the registry by the given key.
+ ///
+ internal void Remove(object key)
+ {
+ Debug.Assert(_registry.Contains(key));
+ _registry.Remove(key);
+ }
+
+ private Hashtable _registry = new Hashtable(); // storage for shared state objects
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia.Controls/Grid/SharedSizeState.cs b/src/Avalonia.Controls/Grid/SharedSizeState.cs
new file mode 100644
index 0000000000..2b99c09861
--- /dev/null
+++ b/src/Avalonia.Controls/Grid/SharedSizeState.cs
@@ -0,0 +1,209 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Avalonia.Utilities;
+
+namespace Avalonia.Controls
+{
+ ///
+ /// Implementation of per shared group state object
+ ///
+ internal class SharedSizeState
+ {
+ private readonly SharedSizeScope _sharedSizeScope; // the scope this state belongs to
+ private readonly string _sharedSizeGroupId; // Id of the shared size group this object is servicing
+ private readonly List _registry; // registry of participating definitions
+ private readonly EventHandler _layoutUpdated; // instance event handler for layout updated event
+ private Control _layoutUpdatedHost; // Control for which layout updated event handler is registered
+ private bool _broadcastInvalidation; // "true" when broadcasting of invalidation is needed
+ private bool _userSizeValid; // "true" when _userSize is up to date
+ private GridLength _userSize; // shared state
+ private double _minSize; // shared state
+
+ ///
+ /// Default ctor.
+ ///
+ internal SharedSizeState(SharedSizeScope sharedSizeScope, string sharedSizeGroupId)
+ {
+ Debug.Assert(sharedSizeScope != null && sharedSizeGroupId != null);
+ _sharedSizeScope = sharedSizeScope;
+ _sharedSizeGroupId = sharedSizeGroupId;
+ _registry = new List();
+ _layoutUpdated = new EventHandler(OnLayoutUpdated);
+ _broadcastInvalidation = true;
+ }
+
+ ///
+ /// Adds / registers a definition instance.
+ ///
+ internal void AddMember(DefinitionBase member)
+ {
+ Debug.Assert(!_registry.Contains(member));
+ _registry.Add(member);
+ Invalidate();
+ }
+
+ ///
+ /// Removes / un-registers a definition instance.
+ ///
+ ///
+ /// If the collection of registered definitions becomes empty
+ /// instantiates self removal from owner's collection.
+ ///
+ internal void RemoveMember(DefinitionBase member)
+ {
+ Invalidate();
+ _registry.Remove(member);
+
+ if (_registry.Count == 0)
+ {
+ _sharedSizeScope.Remove(_sharedSizeGroupId);
+ }
+ }
+
+ ///
+ /// Propogates invalidations for all registered definitions.
+ /// Resets its own state.
+ ///
+ internal void Invalidate()
+ {
+ _userSizeValid = false;
+
+ if (_broadcastInvalidation)
+ {
+ for (int i = 0, count = _registry.Count; i < count; ++i)
+ {
+ Grid parentGrid = (Grid)(_registry[i].Parent);
+ parentGrid.Invalidate();
+ }
+ _broadcastInvalidation = false;
+ }
+ }
+
+ ///
+ /// Makes sure that one and only one layout updated handler is registered for this shared state.
+ ///
+ internal void EnsureDeferredValidation(Control layoutUpdatedHost)
+ {
+ if (_layoutUpdatedHost == null)
+ {
+ _layoutUpdatedHost = layoutUpdatedHost;
+ _layoutUpdatedHost.LayoutUpdated += _layoutUpdated;
+ }
+ }
+
+ ///
+ /// DefinitionBase's specific code.
+ ///
+ internal double MinSize
+ {
+ get
+ {
+ if (!_userSizeValid) { EnsureUserSizeValid(); }
+ return (_minSize);
+ }
+ }
+
+ ///
+ /// DefinitionBase's specific code.
+ ///
+ internal GridLength UserSize
+ {
+ get
+ {
+ if (!_userSizeValid) { EnsureUserSizeValid(); }
+ return (_userSize);
+ }
+ }
+
+ private void EnsureUserSizeValid()
+ {
+ _userSize = new GridLength(1, GridUnitType.Auto);
+
+ for (int i = 0, count = _registry.Count; i < count; ++i)
+ {
+ Debug.Assert(_userSize.GridUnitType == GridUnitType.Auto
+ || _userSize.GridUnitType == GridUnitType.Pixel);
+
+ GridLength currentGridLength = _registry[i].UserSizeValueCache;
+ if (currentGridLength.GridUnitType == GridUnitType.Pixel)
+ {
+ if (_userSize.GridUnitType == GridUnitType.Auto)
+ {
+ _userSize = currentGridLength;
+ }
+ else if (_userSize.Value < currentGridLength.Value)
+ {
+ _userSize = currentGridLength;
+ }
+ }
+ }
+ // taking maximum with user size effectively prevents squishy-ness.
+ // this is a "solution" to avoid shared definitions from been sized to
+ // different final size at arrange time, if / when different grids receive
+ // different final sizes.
+ _minSize = _userSize.IsAbsolute ? _userSize.Value : 0.0;
+
+ _userSizeValid = true;
+ }
+
+ ///
+ /// OnLayoutUpdated handler. Validates that all participating definitions
+ /// have updated min size value. Forces another layout update cycle if needed.
+ ///
+ private void OnLayoutUpdated(object sender, EventArgs e)
+ {
+ double sharedMinSize = 0;
+
+ // accumulate min size of all participating definitions
+ for (int i = 0, count = _registry.Count; i < count; ++i)
+ {
+ sharedMinSize = Math.Max(sharedMinSize, _registry[i].MinSize);
+ }
+
+ bool sharedMinSizeChanged = !MathUtilities.AreClose(_minSize, sharedMinSize);
+
+ // compare accumulated min size with min sizes of the individual definitions
+ for (int i = 0, count = _registry.Count; i < count; ++i)
+ {
+ DefinitionBase definitionBase = _registry[i];
+
+ if (sharedMinSizeChanged || definitionBase.LayoutWasUpdated)
+ {
+ // if definition's min size is different, then need to re-measure
+ if (!MathUtilities.AreClose(sharedMinSize, definitionBase.MinSize))
+ {
+ Grid parentGrid = (Grid)definitionBase.Parent;
+ parentGrid.InvalidateMeasure();
+ definitionBase.UseSharedMinimum = true;
+ }
+ else
+ {
+ definitionBase.UseSharedMinimum = false;
+
+ // if measure is valid then also need to check arrange.
+ // Note: definitionBase.SizeCache is volatile but at this point
+ // it contains up-to-date final size
+ if (!MathUtilities.AreClose(sharedMinSize, definitionBase.SizeCache))
+ {
+ Grid parentGrid = (Grid)definitionBase.Parent;
+ parentGrid.InvalidateArrange();
+ }
+ }
+
+ definitionBase.LayoutWasUpdated = false;
+ }
+ }
+
+ _minSize = sharedMinSize;
+
+ _layoutUpdatedHost.LayoutUpdated -= _layoutUpdated;
+ _layoutUpdatedHost = null;
+
+ _broadcastInvalidation = true;
+ }
+ }
+}
\ No newline at end of file