1
0
Эх сурвалжийг харах

Part 4 of n
Removed more legacy stuff
Added Math Functions needed from WPF's DoubleUtil.

Jumar Macato 6 жил өмнө
parent
commit
9f1d70e592

+ 123 - 0
src/Avalonia.Base/Utilities/MathUtilities.cs

@@ -1,6 +1,8 @@
 // Copyright (c) The Avalonia Project. All rights reserved.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
+using System;
+
 namespace Avalonia.Utilities
 {
     /// <summary>
@@ -8,6 +10,127 @@ namespace Avalonia.Utilities
     /// </summary>
     public static class MathUtilities
     {
+        /// <summary>
+        /// AreClose - Returns whether or not two doubles are "close".  That is, whether or 
+        /// not they are within epsilon of each other.  Note that this epsilon is proportional
+        /// to the numbers themselves to that AreClose survives scalar multiplication.
+        /// There are plenty of ways for this to return false even for numbers which
+        /// are theoretically identical, so no code calling this should fail to work if this 
+        /// returns false.  This is important enough to repeat:
+        /// NB: NO CODE CALLING THIS FUNCTION SHOULD DEPEND ON ACCURATE RESULTS - this should be
+        /// used for optimizations *only*.
+        /// </summary>
+        /// <returns>
+        /// bool - the result of the AreClose comparision.
+        /// </returns>
+        /// <param name="value1"> The first double to compare. </param>
+        /// <param name="value2"> The second double to compare. </param>
+        public static bool AreClose(double value1, double value2)
+        {
+            //in case they are Infinities (then epsilon check does not work)
+            if (value1 == value2) return true;
+            double eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * double.Epsilon;
+            double delta = value1 - value2;
+            return (-eps < delta) && (eps > delta);
+        }
+
+        /// <summary>
+        /// LessThan - Returns whether or not the first double is less than the second double.
+        /// That is, whether or not the first is strictly less than *and* not within epsilon of
+        /// the other number.  Note that this epsilon is proportional to the numbers themselves
+        /// to that AreClose survives scalar multiplication.  Note,
+        /// There are plenty of ways for this to return false even for numbers which
+        /// are theoretically identical, so no code calling this should fail to work if this 
+        /// returns false.  This is important enough to repeat:
+        /// NB: NO CODE CALLING THIS FUNCTION SHOULD DEPEND ON ACCURATE RESULTS - this should be
+        /// used for optimizations *only*.
+        /// </summary>
+        /// <returns>
+        /// bool - the result of the LessThan comparision.
+        /// </returns>
+        /// <param name="value1"> The first double to compare. </param>
+        /// <param name="value2"> The second double to compare. </param>
+        public static bool LessThan(double value1, double value2)
+        {
+            return (value1 < value2) && !AreClose(value1, value2);
+        }
+
+
+        /// <summary>
+        /// GreaterThan - Returns whether or not the first double is greater than the second double.
+        /// That is, whether or not the first is strictly greater than *and* not within epsilon of
+        /// the other number.  Note that this epsilon is proportional to the numbers themselves
+        /// to that AreClose survives scalar multiplication.
+        /// </summary>
+        /// <returns> - the result of the GreaterThan comparision.
+        /// </returns>
+        /// <param name="value1"> The first double to compare. </param>
+        /// <param name="value2"> The second double to compare. </param>
+        public static bool GreaterThan(double value1, double value2)
+        {
+            return (value1 > value2) && !AreClose(value1, value2);
+        }
+
+        /// <summary>
+        /// LessThanOrClose - Returns whether or not the first double is less than or close to
+        /// the second double.  That is, whether or not the first is strictly less than or within
+        /// epsilon of the other number.  Note that this epsilon is proportional to the numbers 
+        /// themselves to that AreClose survives scalar multiplication.  Note,
+        /// There are plenty of ways for this to return false even for numbers which
+        /// are theoretically identical, so no code calling this should fail to work if this 
+        /// returns false.  This is important enough to repeat:
+        /// NB: NO CODE CALLING THIS FUNCTION SHOULD DEPEND ON ACCURATE RESULTS - this should be
+        /// used for optimizations *only*.
+        /// </summary>
+        /// <returns>
+        /// bool - the result of the LessThanOrClose comparision.
+        /// </returns>
+        /// <param name="value1"> The first double to compare. </param>
+        /// <param name="value2"> The second double to compare. </param>
+        public static bool LessThanOrClose(double value1, double value2)
+        {
+            return (value1 < value2) || AreClose(value1, value2);
+        }
+
+        /// <summary>
+        /// GreaterThanOrClose - Returns whether or not the first double is greater than or close to
+        /// the second double.  That is, whether or not the first is strictly greater than or within
+        /// epsilon of the other number.
+        /// </summary>
+        /// <param name="value1"> The first double to compare. </param>
+        /// <param name="value2"> The second double to compare. </param>
+        /// <returns>the result of the GreaterThanOrClose comparision.</returns>
+        public static bool GreaterThanOrClose(double value1, double value2)
+        {
+            return (value1 > value2) || AreClose(value1, value2);
+        }
+
+        /// <summary>
+        /// IsOne - Returns whether or not the double is "close" to 1.  Same as AreClose(double, 1),
+        /// but this is faster.
+        /// </summary>
+        /// <returns>
+        /// bool - the result of the AreClose comparision.
+        /// </returns>
+        /// <param name="value"> The double to compare to 1. </param>
+        public static bool IsOne(double value)
+        {
+            return Math.Abs(value - 1.0) < 10.0 * double.Epsilon;
+        }
+
+        /// <summary>
+        /// IsZero - Returns whether or not the double is "close" to 0.  Same as AreClose(double, 0),
+        /// but this is faster.
+        /// </summary>
+        /// <returns>
+        /// bool - the result of the AreClose comparision.
+        /// </returns>
+        /// <param name="value"> The double to compare to 0. </param>
+        public static bool IsZero(double value)
+        {
+            return Math.Abs(value) < 10.0 * double.Epsilon;
+        }
+
         /// <summary>
         /// Clamps a value between a minimum and maximum value.
         /// </summary>

+ 892 - 2
src/Avalonia.Controls/DefinitionBase.cs

@@ -1,18 +1,24 @@
 // Copyright (c) The Avalonia Project. All rights reserved.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+
 namespace Avalonia.Controls
 {
+
     /// <summary>
     /// Base class for <see cref="ColumnDefinition"/> and <see cref="RowDefinition"/>.
     /// </summary>
     public class DefinitionBase : AvaloniaObject
     {
+
         /// <summary>
         /// Defines the <see cref="SharedSizeGroup"/> property.
         /// </summary>
         public static readonly StyledProperty<string> SharedSizeGroupProperty =
-            AvaloniaProperty.Register<DefinitionBase, string>(nameof(SharedSizeGroup), inherits: true);
+            AvaloniaProperty.Register<DefinitionBase, string>(nameof(SharedSizeGroup), inherits: true, validate: SharedSizeGroupPropertyValueValid);
 
         /// <summary>
         /// Gets or sets the name of the shared size group of the column or row.
@@ -22,5 +28,889 @@ namespace Avalonia.Controls
             get { return GetValue(SharedSizeGroupProperty); }
             set { SetValue(SharedSizeGroupProperty, value); }
         }
+        //------------------------------------------------------
+        //
+        //  Constructors
+        //
+        //------------------------------------------------------
+
+
+        internal DefinitionBase(bool isColumnDefinition)
+        {
+            _isColumnDefinition = isColumnDefinition;
+            _parentIndex = -1;
+        }
+
+
+
+        //------------------------------------------------------
+        //
+        //  Internal Methods
+        //
+        //------------------------------------------------------
+
+        #region Internal Methods
+
+        /// <summary>
+        /// Callback to notify about entering model tree.
+        /// </summary>
+        internal void OnEnterParentTree()
+        {
+            if (_sharedState == null)
+            {
+                //  start with getting SharedSizeGroup value. 
+                //  this property is NOT inhereted which should result in better overall perf.
+                string sharedSizeGroupId = SharedSizeGroup;
+                if (sharedSizeGroupId != null)
+                {
+                    SharedSizeScope privateSharedSizeScope = PrivateSharedSizeScope;
+                    if (privateSharedSizeScope != null)
+                    {
+                        _sharedState = privateSharedSizeScope.EnsureSharedState(sharedSizeGroupId);
+                        _sharedState.AddMember(this);
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Callback to notify about exitting model tree.
+        /// </summary>
+        internal void OnExitParentTree()
+        {
+            _offset = 0;
+            if (_sharedState != null)
+            {
+                _sharedState.RemoveMember(this);
+                _sharedState = null;
+            }
+        }
+
+        /// <summary>
+        /// Performs action preparing definition to enter layout calculation mode.
+        /// </summary>
+        internal void OnBeforeLayout(Grid grid)
+        {
+            //  reset layout state.
+            _minSize = 0;
+            LayoutWasUpdated = true;
+
+            //  defer verification for shared definitions
+            if (_sharedState != null) { _sharedState.EnsureDeferredValidation(grid); }
+        }
+
+        /// <summary>
+        /// 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;
+        }
+
+        /// <summary>
+        /// <see cref="PropertyMetadata.PropertyChangedCallback"/>
+        /// </summary>
+        /// <remarks>
+        /// This method needs to be internal to be accessable from derived classes.
+        /// </remarks>
+        internal static void OnUserSizePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+        {
+            DefinitionBase definition = (DefinitionBase)d;
+
+            if (definition.InParentLogicalTree)
+            {
+                if (definition._sharedState != null)
+                {
+                    definition._sharedState.Invalidate();
+                }
+                else
+                {
+                    Grid parentGrid = (Grid)definition.Parent;
+
+                    if (((GridLength)e.OldValue).GridUnitType != ((GridLength)e.NewValue).GridUnitType)
+                    {
+                        parentGrid.Invalidate();
+                    }
+                    else
+                    {
+                        parentGrid.InvalidateMeasure();
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// <see cref="DependencyProperty.ValidateValueCallback"/>
+        /// </summary>
+        /// <remarks>
+        /// This method needs to be internal to be accessable from derived classes.
+        /// </remarks>
+        internal static bool IsUserSizePropertyValueValid(object value)
+        {
+            return (((GridLength)value).Value >= 0);
+        }
+
+        /// <summary>
+        /// <see cref="PropertyMetadata.PropertyChangedCallback"/>
+        /// </summary>
+        /// <remarks>
+        /// This method needs to be internal to be accessable from derived classes.
+        /// </remarks>
+        internal static void OnUserMinSizePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+        {
+            DefinitionBase definition = (DefinitionBase)d;
+
+            if (definition.InParentLogicalTree)
+            {
+                Grid parentGrid = (Grid)definition.Parent;
+                parentGrid.InvalidateMeasure();
+            }
+        }
+
+        /// <summary>
+        /// <see cref="DependencyProperty.ValidateValueCallback"/>
+        /// </summary>
+        /// <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 (!DoubleUtil.IsNaN(v) && v >= 0.0d && !Double.IsPositiveInfinity(v));
+        }
+
+        /// <summary>
+        /// <see cref="PropertyMetadata.PropertyChangedCallback"/>
+        /// </summary>
+        /// <remarks>
+        /// This method needs to be internal to be accessable from derived classes.
+        /// </remarks>
+        internal static void OnUserMaxSizePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+        {
+            DefinitionBase definition = (DefinitionBase)d;
+
+            if (definition.InParentLogicalTree)
+            {
+                Grid parentGrid = (Grid)definition.Parent;
+                parentGrid.InvalidateMeasure();
+            }
+        }
+
+        /// <summary>
+        /// <see cref="DependencyProperty.ValidateValueCallback"/>
+        /// </summary>
+        /// <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 (!DoubleUtil.IsNaN(v) && v >= 0.0d);
+        }
+
+        /// <summary>
+        /// <see cref="PropertyMetadata.PropertyChangedCallback"/>
+        /// </summary>
+        /// <remarks>
+        /// This method reflects Grid.SharedScopeProperty state by setting / clearing
+        /// dynamic property PrivateSharedSizeScopeProperty. Value of PrivateSharedSizeScopeProperty
+        /// is a collection of SharedSizeState objects for the scope.
+        /// Also PrivateSharedSizeScopeProperty is FrameworkPropertyMetadataOptions.Inherits property. So that all children
+        /// elements belonging to a certain scope can easily access SharedSizeState collection. As well
+        /// as been norified about enter / exit a scope.
+        /// </remarks>
+        internal static void OnIsSharedSizeScopePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+        {
+            //  is it possible to optimize here something like this:
+            //  if ((bool)d.GetValue(Grid.IsSharedSizeScopeProperty) == (d.GetLocalValue(PrivateSharedSizeScopeProperty) != null)
+            //  { /* do nothing */ }
+            if ((bool)e.NewValue)
+            {
+                SharedSizeScope sharedStatesCollection = new SharedSizeScope();
+                d.SetValue(PrivateSharedSizeScopeProperty, sharedStatesCollection);
+            }
+            else
+            {
+                d.ClearValue(PrivateSharedSizeScopeProperty);
+            }
+        }
+
+        #endregion Internal Methods
+
+        //------------------------------------------------------
+        //
+        //  Internal Properties
+        //
+        //------------------------------------------------------
+
+        #region Internal Properties
+
+        /// <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 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 Grid.LayoutTimeSizeType SizeType
+        {
+            get { return (_sizeType); }
+            set { _sizeType = value; }
+        }
+
+        /// <summary>
+        /// Returns or sets measure size for the definition.
+        /// </summary>
+        internal double MeasureSize
+        {
+            get { return (_measureSize); }
+            set { _measureSize = value; }
+        }
+
+        /// <summary>
+        /// Returns definition's layout time type sensitive preferred size.
+        /// </summary>
+        /// <remarks>
+        /// Returned value is guaranteed to be true preferred size.
+        /// </remarks>
+        internal double PreferredSize
+        {
+            get
+            {
+                double preferredSize = MinSize;
+                if (_sizeType != Grid.LayoutTimeSizeType.Auto
+                    && preferredSize < _measureSize)
+                {
+                    preferredSize = _measureSize;
+                }
+                return (preferredSize);
+            }
+        }
+
+        /// <summary>
+        /// Returns or sets size cache for the definition.
+        /// </summary>
+        internal double SizeCache
+        {
+            get { return (_sizeCache); }
+            set { _sizeCache = value; }
+        }
+
+        /// <summary>
+        /// Returns min size.
+        /// </summary>
+        internal double MinSize
+        {
+            get
+            {
+                double minSize = _minSize;
+                if (UseSharedMinimum
+                    && _sharedState != null
+                    && minSize < _sharedState.MinSize)
+                {
+                    minSize = _sharedState.MinSize;
+                }
+                return (minSize);
+            }
+        }
+
+        /// <summary>
+        /// Returns min size, always taking into account shared state.
+        /// </summary>
+        internal double MinSizeForArrange
+        {
+            get
+            {
+                double minSize = _minSize;
+                if (_sharedState != null
+                    && (UseSharedMinimum || !LayoutWasUpdated)
+                    && minSize < _sharedState.MinSize)
+                {
+                    minSize = _sharedState.MinSize;
+                }
+                return (minSize);
+            }
+        }
+
+        /// <summary>
+        /// Offset.
+        /// </summary>
+        internal double FinalOffset
+        {
+            get { return _offset; }
+            set { _offset = value; }
+        }
+
+        /// <summary>
+        /// Internal helper to access up-to-date UserSize property value.
+        /// </summary>
+        internal GridLength UserSizeValueCache
+        {
+            get
+            {
+                return (GridLength)GetValue(
+                        _isColumnDefinition ?
+                        ColumnDefinition.WidthProperty :
+                        RowDefinition.HeightProperty);
+            }
+        }
+
+        /// <summary>
+        /// Internal helper to access up-to-date UserMinSize property value.
+        /// </summary>
+        internal double UserMinSizeValueCache
+        {
+            get
+            {
+                return (double)GetValue(
+                        _isColumnDefinition ?
+                        ColumnDefinition.MinWidthProperty :
+                        RowDefinition.MinHeightProperty);
+            }
+        }
+
+        /// <summary>
+        /// Internal helper to access up-to-date UserMaxSize property value.
+        /// </summary>
+        internal double UserMaxSizeValueCache
+        {
+            get
+            {
+                return (double)GetValue(
+                        _isColumnDefinition ?
+                        ColumnDefinition.MaxWidthProperty :
+                        RowDefinition.MaxHeightProperty);
+            }
+        }
+
+        /// <summary>
+        /// Protected. Returns <c>true</c> if this DefinitionBase instance is in parent's logical tree.
+        /// </summary>
+        internal bool InParentLogicalTree
+        {
+            get { return (_parentIndex != -1); }
+        }
+
+        #endregion Internal Properties
+
+        //------------------------------------------------------
+        //
+        //  Private Methods
+        //
+        //------------------------------------------------------
+
+        #region Private Methods
+
+        /// <summary>
+        /// SetFlags is used to set or unset one or multiple
+        /// flags on the object.
+        /// </summary>
+        private void SetFlags(bool value, Flags flags)
+        {
+            _flags = value ? (_flags | flags) : (_flags & (~flags));
+        }
+
+        /// <summary>
+        /// CheckFlagsAnd returns <c>true</c> if all the flags in the
+        /// given bitmask are set on the object.
+        /// </summary>
+        private bool CheckFlagsAnd(Flags flags)
+        {
+            return ((_flags & flags) == flags);
+        }
+
+        /// <summary>
+        /// <see cref="PropertyMetadata.PropertyChangedCallback"/>
+        /// </summary>
+        private static void OnSharedSizeGroupPropertyChanged(AvaloniaPropertyChangedEventArgs e)
+        {
+            DefinitionBase definition = (DefinitionBase)e.Sender;
+
+            if (definition.InParentLogicalTree)
+            {
+                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))
+                {
+                    SharedSizeScope 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);
+                    }
+                }
+            }
+        }
+
+        /// <remarks>
+        /// Verifies that Shared Size Group Property string
+        /// a) not empty.
+        /// b) contains only letters, digits and underscore ('_').
+        /// c) does not start with a digit.
+        /// </remarks>
+        private static bool SharedSizeGroupPropertyValueValid(string value)
+        {
+            //  null is default value
+            if (value == null)
+            {
+                return (true);
+            }
+
+            string id = (string)value;
+
+            if (id != string.Empty)
+            {
+                int i = -1;
+                while (++i < id.Length)
+                {
+                    bool isDigit = Char.IsDigit(id[i]);
+
+                    if ((i == 0 && isDigit)
+                        || !(isDigit
+                            || Char.IsLetter(id[i])
+                            || '_' == id[i]))
+                    {
+                        break;
+                    }
+                }
+
+                if (i == id.Length)
+                {
+                    return (true);
+                }
+            }
+
+            return (false);
+        }
+
+        /// <summary>
+        /// <see cref="PropertyMetadata.PropertyChangedCallback"/>
+        /// </summary>
+        /// <remark>
+        /// OnPrivateSharedSizeScopePropertyChanged is called when new scope enters or
+        /// existing scope just left. In both cases if the DefinitionBase object is already registered
+        /// in SharedSizeState, it should un-register and register itself in a new one.
+        /// </remark>
+        private static void OnPrivateSharedSizeScopePropertyChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e)
+        {
+            DefinitionBase definition = (DefinitionBase)d;
+
+            if (definition.InParentLogicalTree)
+            {
+                SharedSizeScope privateSharedSizeScope = (SharedSizeScope)e.NewValue;
+
+                if (definition._sharedState != null)
+                {
+                    //  if definition is already registered And shared size scope 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) && (privateSharedSizeScope != null))
+                {
+                    string sharedSizeGroup = definition.SharedSizeGroup;
+                    if (sharedSizeGroup != 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(definition.SharedSizeGroup);
+                        definition._sharedState.AddMember(definition);
+                    }
+                }
+            }
+        }
+
+        #endregion Private Methods
+
+        //------------------------------------------------------
+        //
+        //  Private Properties
+        //
+        //------------------------------------------------------
+
+        #region Private Properties
+
+        /// <summary>
+        /// Private getter of shared state collection dynamic property.
+        /// </summary>
+        private SharedSizeScope PrivateSharedSizeScope
+        {
+            get { return (SharedSizeScope)GetValue(PrivateSharedSizeScopeProperty); }
+        }
+
+        /// <summary>
+        /// Convenience accessor to UseSharedMinimum flag
+        /// </summary>
+        private bool UseSharedMinimum
+        {
+            get { return (CheckFlagsAnd(Flags.UseSharedMinimum)); }
+            set { SetFlags(value, Flags.UseSharedMinimum); }
+        }
+
+        /// <summary>
+        /// Convenience accessor to LayoutWasUpdated flag
+        /// </summary>
+        private bool LayoutWasUpdated
+        {
+            get { return (CheckFlagsAnd(Flags.LayoutWasUpdated)); }
+            set { SetFlags(value, Flags.LayoutWasUpdated); }
+        }
+
+        #endregion Private Properties
+
+        //------------------------------------------------------
+        //
+        //  Private Fields
+        //
+        //------------------------------------------------------
+
+        #region Private Fields
+        private readonly bool _isColumnDefinition;      //  when "true", this is a ColumnDefinition; when "false" this is a RowDefinition (faster than a type check)
+        private Flags _flags;                           //  flags reflecting various aspects of internal state
+        private int _parentIndex;                       //  this instance's index in parent's children collection
+
+        private Grid.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)
+
+        private SharedSizeState _sharedState;           //  reference to shared state object this instance is registered with
+
+        internal const bool ThisIsColumnDefinition = true;
+        internal const bool ThisIsRowDefinition = false;
+
+        #endregion Private Fields
+
+        //------------------------------------------------------
+        //
+        //  Private Structures / Classes
+        //
+        //------------------------------------------------------
+
+        #region Private Structures Classes
+
+        [System.Flags]
+        private enum Flags : byte
+        {
+            //
+            //  bool flags
+            //
+            UseSharedMinimum = 0x00000020,     //  when "1", definition will take into account shared state's minimum
+            LayoutWasUpdated = 0x00000040,     //  set to "1" every time the parent grid is measured
+        }
+
+        /// <summary>
+        /// Collection of shared states objects for a single scope
+        /// </summary>
+        private 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
+        }
+
+        /// <summary>
+        /// Implementation of per shared group state object
+        /// </summary>
+        private class SharedSizeState
+        {
+            /// <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(IControl 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 = !DoubleUtil.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 (!DoubleUtil.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 (!DoubleUtil.AreClose(sharedMinSize, definitionBase.SizeCache))
+                            {
+                                Grid parentGrid = (Grid)definitionBase.Parent;
+                                parentGrid.InvalidateArrange();
+                            }
+                        }
+
+                        definitionBase.LayoutWasUpdated = false;
+                    }
+                }
+
+                _minSize = sharedMinSize;
+
+                _layoutUpdatedHost.LayoutUpdated -= _layoutUpdated;
+                _layoutUpdatedHost = null;
+
+                _broadcastInvalidation = true;
+            }
+
+            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 IControl _layoutUpdatedHost;               //  IControl 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>
+        /// Static ctor. Used for static registration of properties.
+        /// </summary>
+        static DefinitionBase()
+        {
+            SharedSizeGroupProperty.Changed.AddClassHandler<DefinitionBase>(OnSharedSizeGroupPropertyChanged);
+        }
+
+        #endregion Properties
     }
-}
+}
+
+

+ 16 - 452
src/Avalonia.Controls/GridWPF.cs

@@ -868,7 +868,7 @@ namespace Avalonia.Controls
         {
             for (int i = 0; i < minSizes.Length; i++)
             {
-                if (DoubleUtil.GreaterThanOrClose(minSizes[i], 0))
+                if (MathUtilities.GreaterThanOrClose(minSizes[i], 0))
                 {
                     if (isRows)
                     {
@@ -926,7 +926,7 @@ namespace Avalonia.Controls
 
                 MeasureCell(i, forceInfinityV);
 
-                hasDesiredSizeUChanged |= !DoubleUtil.AreClose(oldWidth, Children[i].DesiredSize.Width);
+                hasDesiredSizeUChanged |= !MathUtilities.AreClose(oldWidth, Children[i].DesiredSize.Width);
 
                 if (!ignoreDesiredSizeU)
                 {
@@ -1284,10 +1284,10 @@ namespace Avalonia.Controls
 
                             //  sanity check: totalRemainingSize and sizeToDistribute must be real positive numbers
                             Debug.Assert(!double.IsInfinity(totalRemainingSize)
-                                        && !DoubleUtil.IsNaN(totalRemainingSize)
+                                        && !MathUtilities.IsNaN(totalRemainingSize)
                                         && totalRemainingSize > 0
                                         && !double.IsInfinity(sizeToDistribute)
-                                        && !DoubleUtil.IsNaN(sizeToDistribute)
+                                        && !MathUtilities.IsNaN(sizeToDistribute)
                                         && sizeToDistribute > 0);
 
                             for (int i = 0; i < count; ++i)
@@ -1320,107 +1320,6 @@ namespace Avalonia.Controls
         /// <remarks>
         /// Must initialize LayoutSize for all Star entries in given array of definitions.
         /// </remarks>
-        private void ResolveStar(
-            DefinitionBase[] definitions,
-            double availableSize)
-        {
-            if (FrameworkAppContextSwitches.GridStarDefinitionsCanExceedAvailableSpace)
-            {
-                ResolveStarLegacy(definitions, availableSize);
-            }
-            else
-            {
-                ResolveStarMaxDiscrepancy(definitions, availableSize);
-            }
-        }
-
-        // original implementation, used from 3.0 through 4.6.2
-        private void ResolveStarLegacy(
-            DefinitionBase[] definitions,
-            double availableSize)
-        {
-            DefinitionBase[] tempDefinitions = TempDefinitions;
-            int starDefinitionsCount = 0;
-            double takenSize = 0;
-
-            for (int i = 0; i < definitions.Length; ++i)
-            {
-                switch (definitions[i].SizeType)
-                {
-                    case (LayoutTimeSizeType.Auto):
-                        takenSize += definitions[i].MinSize;
-                        break;
-                    case (LayoutTimeSizeType.Pixel):
-                        takenSize += definitions[i].MeasureSize;
-                        break;
-                    case (LayoutTimeSizeType.Star):
-                        {
-                            tempDefinitions[starDefinitionsCount++] = definitions[i];
-
-                            double starValue = definitions[i].UserSize.Value;
-
-                            if (_IsZero(starValue))
-                            {
-                                definitions[i].MeasureSize = 0;
-                                definitions[i].SizeCache = 0;
-                            }
-                            else
-                            {
-                                //  clipping by c_starClip guarantees that sum of even a very big number of max'ed out star values
-                                //  can be summed up without overflow
-                                starValue = Math.Min(starValue, c_starClip);
-
-                                //  Note: normalized star value is temporary cached into MeasureSize
-                                definitions[i].MeasureSize = starValue;
-                                double maxSize = Math.Max(definitions[i].MinSize, definitions[i].UserMaxSize);
-                                maxSize = Math.Min(maxSize, c_starClip);
-                                definitions[i].SizeCache = maxSize / starValue;
-                            }
-                        }
-                        break;
-                }
-            }
-
-            if (starDefinitionsCount > 0)
-            {
-                Array.Sort(tempDefinitions, 0, starDefinitionsCount, s_starDistributionOrderComparer);
-
-                //  the 'do {} while' loop below calculates sum of star weights in order to avoid fp overflow...
-                //  partial sum value is stored in each definition's SizeCache member.
-                //  this way the algorithm guarantees (starValue <= definition.SizeCache) and thus
-                //  (starValue / definition.SizeCache) will never overflow due to sum of star weights becoming zero.
-                //  this is an important change from previous implementation where the following was possible:
-                //  ((BigValueStar + SmallValueStar) - BigValueStar) resulting in 0...
-                double allStarWeights = 0;
-                int i = starDefinitionsCount - 1;
-                do
-                {
-                    allStarWeights += tempDefinitions[i].MeasureSize;
-                    tempDefinitions[i].SizeCache = allStarWeights;
-                } while (--i >= 0);
-
-                i = 0;
-                do
-                {
-                    double resolvedSize;
-                    double starValue = tempDefinitions[i].MeasureSize;
-
-                    if (_IsZero(starValue))
-                    {
-                        resolvedSize = tempDefinitions[i].MinSize;
-                    }
-                    else
-                    {
-                        double userSize = Math.Max(availableSize - takenSize, 0.0) * (starValue / tempDefinitions[i].SizeCache);
-                        resolvedSize = Math.Min(userSize, tempDefinitions[i].UserMaxSize);
-                        resolvedSize = Math.Max(tempDefinitions[i].MinSize, resolvedSize);
-                    }
-
-                    tempDefinitions[i].MeasureSize = resolvedSize;
-                    takenSize += resolvedSize;
-                } while (++i < starDefinitionsCount);
-            }
-        }
 
         // new implementation as of 4.7.  Several improvements:
         // 1. Allocate to *-defs hitting their min or max constraints, before allocating
@@ -1435,7 +1334,8 @@ namespace Avalonia.Controls
         //      discrepancy (defined below).   This avoids discontinuities - small
         //      change in available space resulting in large change to one def's allocation.
         // 3. Correct handling of large *-values, including Infinity.
-        private void ResolveStarMaxDiscrepancy(
+    
+        private void ResolveStar(
             DefinitionBase[] definitions,
             double availableSize)
         {
@@ -1755,272 +1655,7 @@ namespace Avalonia.Controls
             DefinitionBase[] definitions,
             double finalSize,
             bool columns)
-        {
-            if (FrameworkAppContextSwitches.GridStarDefinitionsCanExceedAvailableSpace)
-            {
-                SetFinalSizeLegacy(definitions, finalSize, columns);
-            }
-            else
-            {
-                SetFinalSizeMaxDiscrepancy(definitions, finalSize, columns);
-            }
-        }
-
-        // original implementation, used from 3.0 through 4.6.2
-        private void SetFinalSizeLegacy(
-            DefinitionBase[] definitions,
-            double finalSize,
-            bool columns)
-        {
-            int starDefinitionsCount = 0;                       //  traverses form the first entry up
-            int nonStarIndex = definitions.Length;              //  traverses from the last entry down
-            double allPreferredArrangeSize = 0;
-            bool useLayoutRounding = this.UseLayoutRounding;
-            int[] definitionIndices = DefinitionIndices;
-            double[] roundingErrors = null;
-
-            // If using layout rounding, check whether rounding needs to compensate for high DPI
-            double dpi = 1.0;
-
-            if (useLayoutRounding)
-            {
-                DpiScale dpiScale = GetDpi();
-                dpi = columns ? dpiScale.DpiScaleX : dpiScale.DpiScaleY;
-                roundingErrors = RoundingErrors;
-            }
-
-            for (int i = 0; i < definitions.Length; ++i)
-            {
-                //  if definition is shared then is cannot be star
-                Debug.Assert(!definitions[i].IsShared || !definitions[i].UserSize.IsStar);
-
-                if (definitions[i].UserSize.IsStar)
-                {
-                    double starValue = definitions[i].UserSize.Value;
-
-                    if (_IsZero(starValue))
-                    {
-                        //  cach normilized star value temporary into MeasureSize
-                        definitions[i].MeasureSize = 0;
-                        definitions[i].SizeCache = 0;
-                    }
-                    else
-                    {
-                        //  clipping by c_starClip guarantees that sum of even a very big number of max'ed out star values
-                        //  can be summed up without overflow
-                        starValue = Math.Min(starValue, c_starClip);
-
-                        //  Note: normalized star value is temporary cached into MeasureSize
-                        definitions[i].MeasureSize = starValue;
-                        double maxSize = Math.Max(definitions[i].MinSizeForArrange, definitions[i].UserMaxSize);
-                        maxSize = Math.Min(maxSize, c_starClip);
-                        definitions[i].SizeCache = maxSize / starValue;
-                        if (useLayoutRounding)
-                        {
-                            roundingErrors[i] = definitions[i].SizeCache;
-                            definitions[i].SizeCache = IControl.RoundLayoutValue(definitions[i].SizeCache, dpi);
-                        }
-                    }
-                    definitionIndices[starDefinitionsCount++] = i;
-                }
-                else
-                {
-                    double userSize = 0;
-
-                    switch (definitions[i].UserSize.GridUnitType)
-                    {
-                        case (GridUnitType.Pixel):
-                            userSize = definitions[i].UserSize.Value;
-                            break;
-
-                        case (GridUnitType.Auto):
-                            userSize = definitions[i].MinSizeForArrange;
-                            break;
-                    }
-
-                    double userMaxSize;
-
-                    if (definitions[i].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 = definitions[i].UserMaxSize;
-                    }
-
-                    definitions[i].SizeCache = Math.Max(definitions[i].MinSizeForArrange, Math.Min(userSize, userMaxSize));
-                    if (useLayoutRounding)
-                    {
-                        roundingErrors[i] = definitions[i].SizeCache;
-                        definitions[i].SizeCache = IControl.RoundLayoutValue(definitions[i].SizeCache, dpi);
-                    }
-
-                    allPreferredArrangeSize += definitions[i].SizeCache;
-                    definitionIndices[--nonStarIndex] = i;
-                }
-            }
-
-            //  indices should meet
-            Debug.Assert(nonStarIndex == starDefinitionsCount);
-
-            if (starDefinitionsCount > 0)
-            {
-                StarDistributionOrderIndexComparer starDistributionOrderIndexComparer = new StarDistributionOrderIndexComparer(definitions);
-                Array.Sort(definitionIndices, 0, starDefinitionsCount, starDistributionOrderIndexComparer);
-
-                //  the 'do {} while' loop below calculates sum of star weights in order to avoid fp overflow...
-                //  partial sum value is stored in each definition's SizeCache member.
-                //  this way the algorithm guarantees (starValue <= definition.SizeCache) and thus
-                //  (starValue / definition.SizeCache) will never overflow due to sum of star weights becoming zero.
-                //  this is an important change from previous implementation where the following was possible:
-                //  ((BigValueStar + SmallValueStar) - BigValueStar) resulting in 0...
-                double allStarWeights = 0;
-                int i = starDefinitionsCount - 1;
-                do
-                {
-                    allStarWeights += definitions[definitionIndices[i]].MeasureSize;
-                    definitions[definitionIndices[i]].SizeCache = allStarWeights;
-                } while (--i >= 0);
-
-                i = 0;
-                do
-                {
-                    double resolvedSize;
-                    double starValue = definitions[definitionIndices[i]].MeasureSize;
-
-                    if (_IsZero(starValue))
-                    {
-                        resolvedSize = definitions[definitionIndices[i]].MinSizeForArrange;
-                    }
-                    else
-                    {
-                        double userSize = Math.Max(finalSize - allPreferredArrangeSize, 0.0) * (starValue / definitions[definitionIndices[i]].SizeCache);
-                        resolvedSize = Math.Min(userSize, definitions[definitionIndices[i]].UserMaxSize);
-                        resolvedSize = Math.Max(definitions[definitionIndices[i]].MinSizeForArrange, resolvedSize);
-                    }
-
-                    definitions[definitionIndices[i]].SizeCache = resolvedSize;
-                    if (useLayoutRounding)
-                    {
-                        roundingErrors[definitionIndices[i]] = definitions[definitionIndices[i]].SizeCache;
-                        definitions[definitionIndices[i]].SizeCache = IControl.RoundLayoutValue(definitions[definitionIndices[i]].SizeCache, dpi);
-                    }
-
-                    allPreferredArrangeSize += definitions[definitionIndices[i]].SizeCache;
-                } while (++i < starDefinitionsCount);
-            }
-
-            if (allPreferredArrangeSize > finalSize
-                && !_AreClose(allPreferredArrangeSize, finalSize))
-            {
-                DistributionOrderIndexComparer distributionOrderIndexComparer = new DistributionOrderIndexComparer(definitions);
-                Array.Sort(definitionIndices, 0, definitions.Length, distributionOrderIndexComparer);
-                double sizeToDistribute = finalSize - allPreferredArrangeSize;
-
-                for (int i = 0; i < definitions.Length; ++i)
-                {
-                    int definitionIndex = definitionIndices[i];
-                    double final = definitions[definitionIndex].SizeCache + (sizeToDistribute / (definitions.Length - i));
-                    double finalOld = final;
-                    final = Math.Max(final, definitions[definitionIndex].MinSizeForArrange);
-                    final = Math.Min(final, definitions[definitionIndex].SizeCache);
-
-                    if (useLayoutRounding)
-                    {
-                        roundingErrors[definitionIndex] = final;
-                        final = IControl.RoundLayoutValue(finalOld, dpi);
-                        final = Math.Max(final, definitions[definitionIndex].MinSizeForArrange);
-                        final = Math.Min(final, definitions[definitionIndex].SizeCache);
-                    }
-
-                    sizeToDistribute -= (final - definitions[definitionIndex].SizeCache);
-                    definitions[definitionIndex].SizeCache = final;
-                }
-
-                allPreferredArrangeSize = finalSize - sizeToDistribute;
-            }
-
-            if (useLayoutRounding)
-            {
-                if (!_AreClose(allPreferredArrangeSize, finalSize))
-                {
-                    // Compute deltas
-                    for (int i = 0; i < definitions.Length; ++i)
-                    {
-                        roundingErrors[i] = roundingErrors[i] - definitions[i].SizeCache;
-                        definitionIndices[i] = i;
-                    }
-
-                    // Sort rounding errors
-                    RoundingErrorIndexComparer roundingErrorIndexComparer = new RoundingErrorIndexComparer(roundingErrors);
-                    Array.Sort(definitionIndices, 0, definitions.Length, roundingErrorIndexComparer);
-                    double adjustedSize = allPreferredArrangeSize;
-                    double dpiIncrement = IControl.RoundLayoutValue(1.0, dpi);
-
-                    if (allPreferredArrangeSize > finalSize)
-                    {
-                        int i = definitions.Length - 1;
-                        while ((adjustedSize > finalSize && !_AreClose(adjustedSize, finalSize)) && i >= 0)
-                        {
-                            DefinitionBase definition = definitions[definitionIndices[i]];
-                            double final = definition.SizeCache - dpiIncrement;
-                            final = Math.Max(final, definition.MinSizeForArrange);
-                            if (final < definition.SizeCache)
-                            {
-                                adjustedSize -= dpiIncrement;
-                            }
-                            definition.SizeCache = final;
-                            i--;
-                        }
-                    }
-                    else if (allPreferredArrangeSize < finalSize)
-                    {
-                        int i = 0;
-                        while ((adjustedSize < finalSize && !_AreClose(adjustedSize, finalSize)) && i < definitions.Length)
-                        {
-                            DefinitionBase definition = definitions[definitionIndices[i]];
-                            double final = definition.SizeCache + dpiIncrement;
-                            final = Math.Max(final, definition.MinSizeForArrange);
-                            if (final > definition.SizeCache)
-                            {
-                                adjustedSize += dpiIncrement;
-                            }
-                            definition.SizeCache = final;
-                            i++;
-                        }
-                    }
-                }
-            }
-
-            definitions[0].FinalOffset = 0.0;
-            for (int i = 0; i < definitions.Length; ++i)
-            {
-                definitions[(i + 1) % definitions.Length].FinalOffset = definitions[i].FinalOffset + definitions[i].SizeCache;
-            }
-        }
-
-        // new implementation, as of 4.7.  This incorporates the same algorithm
-        // as in ResolveStarMaxDiscrepancy.  It differs in the same way that SetFinalSizeLegacy
-        // differs from ResolveStarLegacy, namely (a) leaves results in def.SizeCache
-        // instead of def.MeasureSize, (b) implements LayoutRounding if requested,
-        // (c) stores intermediate results differently.
-        // The LayoutRounding logic is improved:
-        // 1. Use pre-rounded values during proportional allocation.  This avoids the
-        //      same kind of problems arising from interaction with min/max that
-        //      motivated the new algorithm in the first place.
-        // 2. Use correct "nudge" amount when distributing roundoff space.   This
-        //      comes into play at high DPI - greater than 134.
-        // 3. Applies rounding only to real pixel values (not to ratios)
-        private void SetFinalSizeMaxDiscrepancy(
-            DefinitionBase[] definitions,
-            double finalSize,
-            bool columns)
-        {
+        {         
             int defCount = definitions.Length;
             int[] definitionIndices = DefinitionIndices;
             int minCount = 0, maxCount = 0;
@@ -2359,7 +1994,7 @@ namespace Avalonia.Controls
                 for (int i = 0; i < definitions.Length; ++i)
                 {
                     DefinitionBase def = definitions[i];
-                    double roundedSize = IControl.RoundLayoutValue(def.SizeCache, dpi);
+                    double roundedSize = Control.RoundLayoutValue(def.SizeCache, dpi);
                     roundingErrors[i] = (roundedSize - def.SizeCache);
                     def.SizeCache = roundedSize;
                     roundedTakenSize += roundedSize;
@@ -2561,9 +2196,6 @@ namespace Avalonia.Controls
             ExtendedData extData = ExtData;
             if (extData != null)
             {
-                //                for (int i = 0; i < PrivateColumnCount; ++i) DefinitionsU[i].SetValid ();
-                //                for (int i = 0; i < PrivateRowCount; ++i) DefinitionsV[i].SetValid ();
-
                 if (extData.TempDefinitions != null)
                 {
                     //  TempDefinitions has to be cleared to avoid "memory leaks"
@@ -3403,7 +3035,7 @@ namespace Avalonia.Controls
 
             internal StarDistributionOrderIndexComparer(DefinitionBase[] definitions)
             {
-                Invariant.Assert(definitions != null);
+                Contract.Requires<NullReferenceException>(definitions != null);
                 this.definitions = definitions;
             }
 
@@ -3444,7 +3076,7 @@ namespace Avalonia.Controls
 
             internal DistributionOrderIndexComparer(DefinitionBase[] definitions)
             {
-                Invariant.Assert(definitions != null);
+                Contract.Requires<NullReferenceException>(definitions != null);
                 this.definitions = definitions;
             }
 
@@ -3487,7 +3119,7 @@ namespace Avalonia.Controls
 
             internal RoundingErrorIndexComparer(double[] errors)
             {
-                Invariant.Assert(errors != null);
+                Contract.Requires<NullReferenceException>(errors != null);
                 this.errors = errors;
             }
 
@@ -3586,7 +3218,7 @@ namespace Avalonia.Controls
 
             internal MinRatioIndexComparer(DefinitionBase[] definitions)
             {
-                Invariant.Assert(definitions != null);
+                Contract.Requires<NullReferenceException>(definitions != null);
                 this.definitions = definitions;
             }
 
@@ -3627,7 +3259,7 @@ namespace Avalonia.Controls
 
             internal MaxRatioIndexComparer(DefinitionBase[] definitions)
             {
-                Invariant.Assert(definitions != null);
+                Contract.Requires<NullReferenceException>(definitions != null);
                 this.definitions = definitions;
             }
 
@@ -3668,7 +3300,7 @@ namespace Avalonia.Controls
 
             internal StarWeightIndexComparer(DefinitionBase[] definitions)
             {
-                Invariant.Assert(definitions != null);
+                Contract.Requires<NullReferenceException>(definitions != null);
                 this.definitions = definitions;
             }
 
@@ -3699,79 +3331,11 @@ namespace Avalonia.Controls
                 return result;
             }
         }
-
-        /// <summary>
-        /// Implementation of a simple enumerator of grid's logical Children
-        /// </summary>
-        private class GridChildrenCollectionEnumeratorSimple : IEnumerator
-        {
-            internal GridChildrenCollectionEnumeratorSimple(Grid grid, bool includeChildren)
-            {
-                Debug.Assert(grid != null);
-                _currentEnumerator = -1;
-                _enumerator0 = new ColumnDefinitions.Enumerator(grid.ExtData != null ? grid.ExtData.ColumnDefinitions : null);
-                _enumerator1 = new RowDefinitions.Enumerator(grid.ExtData != null ? grid.ExtData.RowDefinitions : null);
-                // GridLineRenderer is NOT included into this enumerator.
-                _enumerator2Index = 0;
-                if (includeChildren)
-                {
-                    _enumerator2Collection = grid.Children;
-                    _enumerator2Count = _enumerator2Collection.Count;
-                }
-                else
-                {
-                    _enumerator2Collection = null;
-                    _enumerator2Count = 0;
-                }
-            }
-
-            public bool MoveNext()
-            {
-                while (_currentEnumerator < 3)
-                {
-                    if (_currentEnumerator >= 0)
-                    {
-                        switch (_currentEnumerator)
-                        {
-                            case (0): if (_enumerator0.MoveNext()) { _currentChild = _enumerator0.Current; return (true); } break;
-                            case (1): if (_enumerator1.MoveNext()) { _currentChild = _enumerator1.Current; return (true); } break;
-                            case (2):
-                                if (_enumerator2Index < _enumerator2Count)
-                                {
-                                    _currentChild = _enumerator2Collection[_enumerator2Index];
-                                    _enumerator2Index++;
-                                    return (true);
-                                }
-                                break;
-                        }
-                    }
-                    _currentEnumerator++;
-                }
-                return (false);
-            }
-
-            public void Reset()
-            {
-                _currentEnumerator = -1;
-                _currentChild = null;
-                _enumerator0.Reset();
-                _enumerator1.Reset();
-                _enumerator2Index = 0;
-            }
-
-            private int _currentEnumerator;
-            private Object _currentChild;
-            private ColumnDefinitions.Enumerator _enumerator0;
-            private RowDefinitions.Enumerator _enumerator1;
-            private Controls _enumerator2Collection;
-            private int _enumerator2Index;
-            private int _enumerator2Count;
-        }
-
+ 
         /// <summary>
         /// Helper to render grid lines.
         /// </summary>
-        internal class GridLinesRenderer : DrawingVisual
+        internal class GridLinesRenderer : Visual
         {
             /// <summary>
             /// Static initialization