Browse Source

Implement and make SharedSizeScopes work :)

pull/2563/head
Jumar Macato 7 years ago
parent
commit
ad45848a29
No known key found for this signature in database GPG Key ID: B19884DAC3A5BF3F
  1. 6
      src/Avalonia.Controls/Grid/ColumnDefinition.cs
  2. 286
      src/Avalonia.Controls/Grid/DefinitionBase.cs
  3. 114
      src/Avalonia.Controls/Grid/Grid.cs
  4. 6
      src/Avalonia.Controls/Grid/RowDefinition.cs
  5. 43
      src/Avalonia.Controls/Grid/SharedSizeScope.cs
  6. 209
      src/Avalonia.Controls/Grid/SharedSizeState.cs

6
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;
}
}

286
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
/// </summary>
public abstract class DefinitionBase : AvaloniaObject
{
/// <summary>
/// Static ctor. Used for static registration of properties.
/// </summary>
static DefinitionBase()
{
SharedSizeGroupProperty.Changed.AddClassHandler<DefinitionBase>(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;
/// <summary>
/// Defines the <see cref="SharedSizeGroup"/> property.
/// </summary>
@ -28,31 +46,182 @@ namespace Avalonia.Controls
get { return GetValue(SharedSizeGroupProperty); }
set { SetValue(SharedSizeGroupProperty, value); }
}
/// <summary>
/// Callback to notify about entering model tree.
/// </summary>
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; }
/// <summary>
/// Internal helper to access up-to-date UserSize property value.
/// Callback to notify about exitting model tree.
/// </summary>
internal abstract GridLength UserSize { get; }
internal void OnExitParentTree()
{
_offset = 0;
if (_sharedState != null)
{
_sharedState.RemoveMember(this);
_sharedState = null;
}
}
/// <summary>
/// Internal helper to access up-to-date UserMinSize property value.
/// Performs action preparing definition to enter layout calculation mode.
/// </summary>
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);
}
}
/// <summary>
/// Internal helper to access up-to-date UserMaxSize property value.
/// Updates min size.
/// </summary>
/// <param name="minSize">New size.</param>
internal void UpdateMinSize(double minSize)
{
_minSize = Math.Max(_minSize, minSize);
}
/// <summary>
/// Sets min size.
/// </summary>
/// <param name="minSize">New size.</param>
internal void SetMinSize(double minSize)
{
_minSize = minSize;
}
/// <remarks>
/// This method needs to be internal to be accessable from derived classes.
/// </remarks>
internal void OnUserSizePropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
_sharedState?.Invalidate();
}
/// <remarks>
/// This method needs to be internal to be accessable from derived classes.
/// </remarks>
internal static bool IsUserMinSizePropertyValueValid(object value)
{
double v = (double)value;
return (!double.IsNaN(v) && v >= 0.0d && !Double.IsPositiveInfinity(v));
}
/// <remarks>
/// This method needs to be internal to be accessable from derived classes.
/// </remarks>
internal static void OnUserMaxSizePropertyChanged(DefinitionBase definition, AvaloniaPropertyChangedEventArgs e)
{
Grid parentGrid = (Grid)definition.Parent;
parentGrid.InvalidateMeasure();
}
/// <remarks>
/// This method needs to be internal to be accessable from derived classes.
/// </remarks>
internal static bool IsUserMaxSizePropertyValueValid(object value)
{
double v = (double)value;
return (!double.IsNaN(v) && v >= 0.0d);
}
/// <summary>
/// Returns <c>true</c> if this definition is a part of shared group.
/// </summary>
internal bool IsShared
{
get { return (_sharedState != null); }
}
/// <summary>
/// Internal accessor to user size field.
/// </summary>
internal GridLength UserSize
{
get { return (_sharedState != null ? _sharedState.UserSize : UserSizeValueCache); }
}
/// <summary>
/// Internal accessor to user min size field.
/// </summary>
internal double UserMinSize
{
get { return (UserMinSizeValueCache); }
}
/// <summary>
/// Internal accessor to user max size field.
/// </summary>
internal abstract double UserMaxSize { get; }
internal double UserMaxSize
{
get { return (UserMaxSizeValueCache); }
}
/// <summary>
/// DefinitionBase's index in the parents collection.
/// </summary>
internal int Index
{
get
{
return (_parentIndex);
}
set
{
Debug.Assert(value >= -1 && _parentIndex != value);
_parentIndex = value;
}
}
/// <summary>
/// Layout-time user size type.
/// </summary>
internal LayoutTimeSizeType SizeType { get; set; }
internal LayoutTimeSizeType SizeType
{
get { return (_sizeType); }
set { _sizeType = value; }
}
/// <summary>
/// Returns or sets measure size for the definition.
/// </summary>
internal double MeasureSize { get; set; }
internal double MeasureSize
{
get { return (_measureSize); }
set { _measureSize = value; }
}
/// <summary>
/// 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);
}
}
/// <summary>
/// Returns or sets size cache for the definition.
/// </summary>
internal double SizeCache { get; set; }
internal double SizeCache
{
get { return (_sizeCache); }
set { _sizeCache = value; }
}
/// <summary>
/// Used during measure and arrange to accumulate size for "Auto" and "Star" DefinitionBase's
/// Returns min size.
/// </summary>
internal double MinSize { get; set; }
internal double MinSize
{
get
{
double minSize = _minSize;
if (UseSharedMinimum
&& _sharedState != null
&& minSize < _sharedState.MinSize)
{
minSize = _sharedState.MinSize;
}
return (minSize);
}
}
/// <summary>
/// Updates min size.
/// Returns min size, always taking into account shared state.
/// </summary>
/// <param name="minSize">New size.</param>
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);
}
}
/// <summary>
/// Offset.
/// </summary>
internal double FinalOffset { get; set; }
internal double FinalOffset
{
get { return _offset; }
set { _offset = value; }
}
/// <summary>
/// Internal helper to access up-to-date UserSize property value.
/// </summary>
internal abstract GridLength UserSizeValueCache { get; }
/// <summary>
/// Internal helper to access up-to-date UserMinSize property value.
/// </summary>
internal abstract double UserMinSizeValueCache { get; }
/// <summary>
/// Internal helper to access up-to-date UserMaxSize property value.
/// </summary>
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);
}
}
}
}
}

114
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<Grid>(OnShowGridLinesPropertyChanged);
IsSharedSizeScopeProperty.Changed.AddClassHandler<Grid>(IsSharedSizeScopePropertyChanged);
BoundsProperty.Changed.AddClassHandler<Grid>(BoundsPropertyChanged);
AffectsParentMeasure<Grid>(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;
}
}
/// <summary>
/// Defines the Column attached property.
/// </summary>
@ -252,8 +276,12 @@ namespace Avalonia.Controls
if (_columnDefinitions.Count > 0)
_definitionsU = _columnDefinitions.Cast<DefinitionBase>().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<DefinitionBase>().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();
}
/// <summary>
/// Gets or sets the row definitions for the grid.
/// </summary>
@ -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<DefinitionBase>().ToArray();
DefinitionsDirty = true;
}
CallEnterParentTree(_definitionsU);
Invalidate();
};
}
@ -359,6 +406,16 @@ namespace Avalonia.Controls
return element.GetValue(IsSharedSizeScopeProperty);
}
/// <summary>
/// Sets the value of the IsSharedSizeScope attached property for a control.
/// </summary>
/// <param name="element">The control.</param>
/// <returns>The control's IsSharedSizeScope value.</returns>
public static void SetIsSharedSizeScope(AvaloniaObject element, bool value)
{
element.SetValue(IsSharedSizeScopeProperty, value);
}
/// <summary>
/// Sets the value of the Column attached property for a control.
/// </summary>
@ -659,7 +716,7 @@ namespace Avalonia.Controls
/// <summary>
/// Invalidates grid caches and makes the grid dirty for measure.
/// </summary>
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;

6
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;
}
}

43
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
{
/// <summary>
/// Collection of shared states objects for a single scope
/// </summary>
internal class SharedSizeScope
{
/// <summary>
/// Returns SharedSizeState object for a given group.
/// Creates a new StatedState object if necessary.
/// </summary>
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);
}
/// <summary>
/// Removes an entry in the registry by the given key.
/// </summary>
internal void Remove(object key)
{
Debug.Assert(_registry.Contains(key));
_registry.Remove(key);
}
private Hashtable _registry = new Hashtable(); // storage for shared state objects
}
}

209
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
{
/// <summary>
/// Implementation of per shared group state object
/// </summary>
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<DefinitionBase> _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
/// <summary>
/// Default ctor.
/// </summary>
internal SharedSizeState(SharedSizeScope sharedSizeScope, string sharedSizeGroupId)
{
Debug.Assert(sharedSizeScope != null && sharedSizeGroupId != null);
_sharedSizeScope = sharedSizeScope;
_sharedSizeGroupId = sharedSizeGroupId;
_registry = new List<DefinitionBase>();
_layoutUpdated = new EventHandler(OnLayoutUpdated);
_broadcastInvalidation = true;
}
/// <summary>
/// Adds / registers a definition instance.
/// </summary>
internal void AddMember(DefinitionBase member)
{
Debug.Assert(!_registry.Contains(member));
_registry.Add(member);
Invalidate();
}
/// <summary>
/// Removes / un-registers a definition instance.
/// </summary>
/// <remarks>
/// If the collection of registered definitions becomes empty
/// instantiates self removal from owner's collection.
/// </remarks>
internal void RemoveMember(DefinitionBase member)
{
Invalidate();
_registry.Remove(member);
if (_registry.Count == 0)
{
_sharedSizeScope.Remove(_sharedSizeGroupId);
}
}
/// <summary>
/// Propogates invalidations for all registered definitions.
/// Resets its own state.
/// </summary>
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;
}
}
/// <summary>
/// Makes sure that one and only one layout updated handler is registered for this shared state.
/// </summary>
internal void EnsureDeferredValidation(Control layoutUpdatedHost)
{
if (_layoutUpdatedHost == null)
{
_layoutUpdatedHost = layoutUpdatedHost;
_layoutUpdatedHost.LayoutUpdated += _layoutUpdated;
}
}
/// <summary>
/// DefinitionBase's specific code.
/// </summary>
internal double MinSize
{
get
{
if (!_userSizeValid) { EnsureUserSizeValid(); }
return (_minSize);
}
}
/// <summary>
/// DefinitionBase's specific code.
/// </summary>
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;
}
/// <summary>
/// OnLayoutUpdated handler. Validates that all participating definitions
/// have updated min size value. Forces another layout update cycle if needed.
/// </summary>
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;
}
}
}
Loading…
Cancel
Save