Browse Source

Merge pull request #3157 from donandren/issues/grid

fixed for few major issues with grid
Jumar Macato 6 years ago
parent
commit
1a4bc738c4

+ 8 - 3
src/Avalonia.Controls/ColumnDefinition.cs

@@ -26,6 +26,14 @@ namespace Avalonia.Controls
         public static readonly StyledProperty<GridLength> WidthProperty =
             AvaloniaProperty.Register<ColumnDefinition, GridLength>(nameof(Width), new GridLength(1, GridUnitType.Star));
 
+        /// <summary>
+        /// Initializes static members of the <see cref="ColumnDefinition"/> class.
+        /// </summary>
+        static ColumnDefinition()
+        {
+            AffectsParentMeasure(WidthProperty, MinWidthProperty, MaxWidthProperty);
+        }
+
         /// <summary>
         /// Initializes a new instance of the <see cref="ColumnDefinition"/> class.
         /// </summary>
@@ -68,7 +76,6 @@ namespace Avalonia.Controls
             }
             set
             {
-                Parent?.InvalidateMeasure();
                 SetValue(MaxWidthProperty, value);
             }
         }
@@ -84,7 +91,6 @@ namespace Avalonia.Controls
             }
             set
             {
-                Parent?.InvalidateMeasure();
                 SetValue(MinWidthProperty, value);
             }
         }
@@ -100,7 +106,6 @@ namespace Avalonia.Controls
             }
             set
             {
-                Parent?.InvalidateMeasure();
                 SetValue(WidthProperty, value);
             }
         }

+ 21 - 0
src/Avalonia.Controls/DefinitionBase.cs

@@ -50,6 +50,8 @@ namespace Avalonia.Controls
                     }
                 }
             }
+
+            Parent?.InvalidateMeasure();
         }
 
         /// <summary>
@@ -63,6 +65,8 @@ namespace Avalonia.Controls
                 _sharedState.RemoveMember(this);
                 _sharedState = null;
             }
+
+            Parent?.InvalidateMeasure();
         }
 
         /// <summary>
@@ -730,5 +734,22 @@ namespace Avalonia.Controls
             SharedSizeGroupProperty.Changed.AddClassHandler<DefinitionBase>(OnSharedSizeGroupPropertyChanged);
             PrivateSharedSizeScopeProperty.Changed.AddClassHandler<DefinitionBase>(OnPrivateSharedSizeScopePropertyChanged);
         }
+
+        /// <summary>
+        /// Marks a property on a definition as affecting the parent grid's measurement.
+        /// </summary>
+        /// <param name="properties">The properties.</param>
+        protected static void AffectsParentMeasure(params AvaloniaProperty[] properties)
+        {
+            void Invalidate(AvaloniaPropertyChangedEventArgs e)
+            {
+                (e.Sender as DefinitionBase)?.Parent?.InvalidateMeasure();
+            }
+
+            foreach (var property in properties)
+            {
+                property.Changed.Subscribe(Invalidate);
+            }
+        }
     }
 }

+ 12 - 0
src/Avalonia.Controls/Grid.cs

@@ -6,6 +6,7 @@
 using System;
 using System.Collections;
 using System.Collections.Generic;
+using System.Collections.Specialized;
 using System.Diagnostics;
 using System.Linq;
 using System.Threading;
@@ -178,6 +179,7 @@ namespace Avalonia.Controls
                 if (_data == null) { _data = new ExtendedData(); }
                 _data.ColumnDefinitions = value;
                 _data.ColumnDefinitions.Parent = this;
+                InvalidateMeasure();
             }
         }
 
@@ -198,6 +200,7 @@ namespace Avalonia.Controls
                 if (_data == null) { _data = new ExtendedData(); }
                 _data.RowDefinitions = value;
                 _data.RowDefinitions.Parent = this;
+                InvalidateMeasure();
             }
         }
 
@@ -569,6 +572,15 @@ namespace Avalonia.Controls
             return (arrangeSize);
         }
 
+        /// <summary>
+        /// <see cref="Panel.ChildrenChanged"/>
+        /// </summary>
+        protected override void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e)
+        {
+            CellsStructureDirty = true;
+            base.ChildrenChanged(sender, e);
+        }
+
         /// <summary>
         /// Invalidates grid caches and makes the grid dirty for measure.
         /// </summary>

+ 8 - 3
src/Avalonia.Controls/RowDefinition.cs

@@ -26,6 +26,14 @@ namespace Avalonia.Controls
         public static readonly StyledProperty<GridLength> HeightProperty =
             AvaloniaProperty.Register<RowDefinition, GridLength>(nameof(Height), new GridLength(1, GridUnitType.Star));
 
+        /// <summary>
+        /// Initializes static members of the <see cref="RowDefinition"/> class.
+        /// </summary>
+        static RowDefinition()
+        {
+            AffectsParentMeasure(HeightProperty, MaxHeightProperty, MinHeightProperty);
+        }
+
         /// <summary>
         /// Initializes a new instance of the <see cref="RowDefinition"/> class.
         /// </summary>
@@ -68,7 +76,6 @@ namespace Avalonia.Controls
             }
             set
             {
-                Parent?.InvalidateMeasure();
                 SetValue(MaxHeightProperty, value);
             }
         }
@@ -84,7 +91,6 @@ namespace Avalonia.Controls
             }
             set
             {
-                Parent?.InvalidateMeasure();
                 SetValue(MinHeightProperty, value);
             }
         }
@@ -100,7 +106,6 @@ namespace Avalonia.Controls
             }
             set
             {
-                Parent?.InvalidateMeasure();
                 SetValue(HeightProperty, value);
             }
         }

+ 242 - 9
tests/Avalonia.Controls.UnitTests/GridTests.cs

@@ -1,11 +1,6 @@
+using System;
 using System.Collections.Generic;
 using System.Linq;
-using Avalonia.Controls.Primitives;
-using Avalonia.Input;
-using Avalonia.Platform;
-using Avalonia.UnitTests;
-
-using Moq;
 using Xunit;
 using Xunit.Abstractions;
 
@@ -34,7 +29,6 @@ namespace Avalonia.Controls.UnitTests
 
         private Grid CreateGrid(params (string name, GridLength width, double minWidth, double maxWidth)[] columns)
         {
-
             var grid = new Grid();
             foreach (var k in columns.Select(c => new ColumnDefinition
             {
@@ -1270,11 +1264,11 @@ namespace Avalonia.Controls.UnitTests
             // grid.Measure(new Size(100, 100));
             // grid.Arrange(new Rect(new Point(), new Point(100, 100)));
             // PrintColumnDefinitions(grid);
-            
+
             // NOTE: THIS IS BROKEN IN WPF
             // all in group are equal to width (MinWidth) of the sizer in the second column
             // Assert.All(grid.ColumnDefinitions.Where(cd => cd.SharedSizeGroup == "A"), cd => Assert.Equal(6 + 1 * 6, cd.ActualWidth));
-            
+
             // NOTE: THIS IS BROKEN IN WPF
             // grid.ColumnDefinitions[2].SharedSizeGroup = null;
 
@@ -1382,6 +1376,245 @@ namespace Avalonia.Controls.UnitTests
             Assert.Equal(new Size(100, 100), grid.Bounds.Size);
         }
 
+        [Theory]
+        [InlineData(true)]
+        [InlineData(false)]
+        public void Changing_Column_Width_Should_Invalidate_Grid(bool setUsingAvaloniaProperty)
+        {
+            var grid = new Grid { ColumnDefinitions = ColumnDefinitions.Parse("1*,1*") };
+
+            Change_Property_And_Verify_Measure_Requested(grid, () =>
+            {
+                if (setUsingAvaloniaProperty)
+                    grid.ColumnDefinitions[0][ColumnDefinition.WidthProperty] = new GridLength(5);
+                else
+                    grid.ColumnDefinitions[0].Width = new GridLength(5);
+            });
+        }
+
+        [Theory]
+        [InlineData(true)]
+        [InlineData(false)]
+        public void Changing_Column_MinWidth_Should_Invalidate_Grid(bool setUsingAvaloniaProperty)
+        {
+            var grid = new Grid { ColumnDefinitions = ColumnDefinitions.Parse("1*,1*") };
+
+            Change_Property_And_Verify_Measure_Requested(grid, () =>
+            {
+                if (setUsingAvaloniaProperty)
+                    grid.ColumnDefinitions[0][ColumnDefinition.MinWidthProperty] = 5;
+                else
+                    grid.ColumnDefinitions[0].MinWidth = 5;
+            });
+        }
+
+        [Theory]
+        [InlineData(true)]
+        [InlineData(false)]
+        public void Changing_Column_MaxWidth_Should_Invalidate_Grid(bool setUsingAvaloniaProperty)
+        {
+            var grid = new Grid { ColumnDefinitions = ColumnDefinitions.Parse("1*,1*") };
+
+            Change_Property_And_Verify_Measure_Requested(grid, () =>
+            {
+                if (setUsingAvaloniaProperty)
+                    grid.ColumnDefinitions[0][ColumnDefinition.MaxWidthProperty] = 5;
+                else
+                    grid.ColumnDefinitions[0].MaxWidth = 5;
+            });
+        }
+
+        [Theory]
+        [InlineData(true)]
+        [InlineData(false)]
+        public void Changing_Row_Height_Should_Invalidate_Grid(bool setUsingAvaloniaProperty)
+        {
+            var grid = new Grid { RowDefinitions = RowDefinitions.Parse("1*,1*") };
+
+            Change_Property_And_Verify_Measure_Requested(grid, () =>
+            {
+                if (setUsingAvaloniaProperty)
+                    grid.RowDefinitions[0][RowDefinition.HeightProperty] = new GridLength(5);
+                else
+                    grid.RowDefinitions[0].Height = new GridLength(5);
+            });
+        }
+
+        [Theory]
+        [InlineData(true)]
+        [InlineData(false)]
+        public void Changing_Row_MinHeight_Should_Invalidate_Grid(bool setUsingAvaloniaProperty)
+        {
+            var grid = new Grid { RowDefinitions = RowDefinitions.Parse("1*,1*") };
+
+            Change_Property_And_Verify_Measure_Requested(grid, () =>
+            {
+                if (setUsingAvaloniaProperty)
+                    grid.RowDefinitions[0][RowDefinition.MinHeightProperty] = 5;
+                else
+                    grid.RowDefinitions[0].MinHeight = 5;
+            });
+        }
+
+        [Theory]
+        [InlineData(true)]
+        [InlineData(false)]
+        public void Changing_Row_MaxHeight_Should_Invalidate_Grid(bool setUsingAvaloniaProperty)
+        {
+            var grid = new Grid { RowDefinitions = RowDefinitions.Parse("1*,1*") };
+
+            Change_Property_And_Verify_Measure_Requested(grid, () =>
+            {
+                if (setUsingAvaloniaProperty)
+                    grid.RowDefinitions[0][RowDefinition.MaxHeightProperty] = 5;
+                else
+                    grid.RowDefinitions[0].MaxHeight = 5;
+            });
+        }
+
+        [Fact]
+        public void Adding_Column_Should_Invalidate_Grid()
+        {
+            var grid = new Grid { ColumnDefinitions = ColumnDefinitions.Parse("1*,1*") };
+
+            Change_Property_And_Verify_Measure_Requested(grid, () =>
+            {
+                grid.ColumnDefinitions.Add(new ColumnDefinition(new GridLength(5)));
+            });
+        }
+
+        [Fact]
+        public void Adding_Row_Should_Invalidate_Grid()
+        {
+            var grid = new Grid { RowDefinitions = RowDefinitions.Parse("1*,1*") };
+
+            Change_Property_And_Verify_Measure_Requested(grid, () =>
+            {
+                grid.RowDefinitions.Add(new RowDefinition(new GridLength(5)));
+            });
+        }
+
+        [Fact]
+        public void Replacing_Columns_Should_Invalidate_Grid()
+        {
+            var grid = new Grid { ColumnDefinitions = ColumnDefinitions.Parse("1*,1*") };
+
+            Change_Property_And_Verify_Measure_Requested(grid, () =>
+            {
+                grid.ColumnDefinitions = ColumnDefinitions.Parse("2*,1*");
+            });
+        }
+
+        [Fact]
+        public void Replacing_Rows_Should_Invalidate_Grid()
+        {
+            var grid = new Grid { RowDefinitions = RowDefinitions.Parse("1*,1*") };
+
+            Change_Property_And_Verify_Measure_Requested(grid, () =>
+            {
+                grid.RowDefinitions = RowDefinitions.Parse("2*,1*");
+            });
+        }
+
+        [Fact]
+        public void Removing_Column_Should_Invalidate_Grid()
+        {
+            var grid = new Grid { ColumnDefinitions = ColumnDefinitions.Parse("1*,1*") };
+
+            Change_Property_And_Verify_Measure_Requested(grid, () =>
+            {
+                grid.ColumnDefinitions.RemoveAt(0);
+            });
+        }
+
+        [Fact]
+        public void Removing_Row_Should_Invalidate_Grid()
+        {
+            var grid = new Grid { RowDefinitions = RowDefinitions.Parse("1*,1*") };
+
+            Change_Property_And_Verify_Measure_Requested(grid, () =>
+            {
+                grid.RowDefinitions.RemoveAt(0);
+            });
+        }
+
+        [Fact]
+        public void Removing_Child_Should_Invalidate_Grid_And_Be_Operational()
+        {
+            var grid = new Grid { ColumnDefinitions = ColumnDefinitions.Parse("*,Auto") };
+
+            grid.Children.Add(new Decorator() { [Grid.ColumnProperty] = 0 });
+            grid.Children.Add(new Decorator() { Width = 10, Height = 10, [Grid.ColumnProperty] = 1 });
+
+            var size = new Size(100, 100);
+            grid.Measure(size);
+            grid.Arrange(new Rect(size));
+
+            Assert.True(grid.IsMeasureValid);
+            Assert.True(grid.IsArrangeValid);
+
+            Assert.Equal(90, grid.Children[0].Bounds.Width);
+            Assert.Equal(10, grid.Children[1].Bounds.Width);
+
+            grid.Children.RemoveAt(1);
+
+            Assert.False(grid.IsMeasureValid);
+            Assert.False(grid.IsArrangeValid);
+
+            grid.Measure(size);
+            grid.Arrange(new Rect(size));
+
+            Assert.True(grid.IsMeasureValid);
+            Assert.True(grid.IsArrangeValid);
+
+            Assert.Equal(100, grid.Children[0].Bounds.Width);
+        }
+
+        [Fact]
+        public void Adding_Child_Should_Invalidate_Grid_And_Be_Operational()
+        {
+            var grid = new Grid { ColumnDefinitions = ColumnDefinitions.Parse("*,Auto") };
+
+            grid.Children.Add(new Decorator() { [Grid.ColumnProperty] = 0 });
+
+            var size = new Size(100, 100);
+            grid.Measure(size);
+            grid.Arrange(new Rect(size));
+
+            Assert.True(grid.IsMeasureValid);
+            Assert.True(grid.IsArrangeValid);
+
+            Assert.Equal(100, grid.Children[0].Bounds.Width);
+
+            grid.Children.Add(new Decorator() { Width = 10, Height = 10, [Grid.ColumnProperty] = 1 });
+
+            Assert.False(grid.IsMeasureValid);
+            Assert.False(grid.IsArrangeValid);
+
+            grid.Measure(size);
+            grid.Arrange(new Rect(size));
+
+            Assert.True(grid.IsMeasureValid);
+            Assert.True(grid.IsArrangeValid);
+
+            Assert.Equal(90, grid.Children[0].Bounds.Width);
+            Assert.Equal(10, grid.Children[1].Bounds.Width);
+        }
+
+        private static void Change_Property_And_Verify_Measure_Requested(Grid grid, Action change)
+        {
+            grid.Measure(new Size(100, 100));
+            grid.Arrange(new Rect(grid.DesiredSize));
+
+            Assert.True(grid.IsMeasureValid);
+            Assert.True(grid.IsArrangeValid);
+
+            change();
+
+            Assert.False(grid.IsMeasureValid);
+            Assert.False(grid.IsArrangeValid);
+        }
+
         private class TestControl : Control
         {
             public Size MeasureSize { get; set; }