diff --git a/src/Avalonia.Controls/Grid.cs b/src/Avalonia.Controls/Grid.cs index 176c8cdb89..b51583d8b3 100644 --- a/src/Avalonia.Controls/Grid.cs +++ b/src/Avalonia.Controls/Grid.cs @@ -105,6 +105,7 @@ namespace Avalonia.Controls _columnDefinitions = value; _columnDefinitions.TrackItemPropertyChanged(_ => InvalidateMeasure()); + _columnDefinitions.CollectionChanged += (_, __) => InvalidateMeasure(); } } @@ -132,6 +133,7 @@ namespace Avalonia.Controls _rowDefinitions = value; _rowDefinitions.TrackItemPropertyChanged(_ => InvalidateMeasure()); + _rowDefinitions.CollectionChanged += (_, __) => InvalidateMeasure(); } } diff --git a/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs b/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs index ec9c0b3eca..8553165e4b 100644 --- a/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs +++ b/src/Avalonia.Controls/Utils/SharedSizeScopeHost.cs @@ -52,6 +52,7 @@ namespace Avalonia.Controls grid.RowDefinitions.CollectionChanged += DefinitionsCollectionChanged; grid.ColumnDefinitions.CollectionChanged += DefinitionsCollectionChanged; + _subscriptions = new CompositeDisposable( Disposable.Create(() => grid.RowDefinitions.CollectionChanged -= DefinitionsCollectionChanged), Disposable.Create(() => grid.ColumnDefinitions.CollectionChanged -= DefinitionsCollectionChanged), @@ -60,6 +61,13 @@ namespace Avalonia.Controls } + // method to be hooked up once RowDefinitions/ColumnDefinitions collections can be replaced on a grid + private void DefinitionsChanged(object sender, AvaloniaPropertyChangedEventArgs e) + { + // route to collection changed as a Reset. + DefinitionsCollectionChanged(null, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + private void DefinitionPropertyChanged(Tuple propertyChanged) { if (propertyChanged.Item2.PropertyName == nameof(DefinitionBase.SharedSizeGroup)) @@ -78,7 +86,9 @@ namespace Avalonia.Controls offset = Grid.RowDefinitions.Count; var newItems = e.NewItems?.OfType().Select(db => new MeasurementResult(Grid, db)).ToList() ?? new List(); - var oldItems = Results.GetRange(e.OldStartingIndex + offset, e.OldItems?.Count ?? 0); + var oldItems = e.OldStartingIndex >= 0 + ? Results.GetRange(e.OldStartingIndex + offset, e.OldItems.Count) + : new List(); void NotifyNewItems() { diff --git a/tests/Avalonia.Controls.UnitTests/SharedSizeScopeTests.cs b/tests/Avalonia.Controls.UnitTests/SharedSizeScopeTests.cs index 0d0b9c2891..715e9da880 100644 --- a/tests/Avalonia.Controls.UnitTests/SharedSizeScopeTests.cs +++ b/tests/Avalonia.Controls.UnitTests/SharedSizeScopeTests.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Linq; using Avalonia.Controls.Primitives; using Avalonia.Input; @@ -149,6 +150,90 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(0, grids[0].ColumnDefinitions[0].ActualWidth); } + [Fact] + public void Collection_Changes_Are_Tracked() + { + var grid = CreateGrid( + ("A", new GridLength(20)), + ("A", new GridLength(30)), + ("A", new GridLength(40)), + (null, new GridLength())); + + var scope = new Panel(); + scope.Children.Add(grid); + + var root = new TestRoot(); + root.SetValue(Grid.IsSharedSizeScopeProperty, true); + root.Child = scope; + + grid.Measure(new Size(200, 200)); + grid.Arrange(new Rect(new Point(), new Point(200, 200))); + Assert.All(grid.ColumnDefinitions.Where(cd => cd.SharedSizeGroup == "A"), cd => Assert.Equal(40, cd.ActualWidth)); + + grid.ColumnDefinitions.RemoveAt(2); + + grid.Measure(new Size(200, 200)); + grid.Arrange(new Rect(new Point(), new Point(200, 200))); + Assert.All(grid.ColumnDefinitions.Where(cd => cd.SharedSizeGroup == "A"), cd => Assert.Equal(30, cd.ActualWidth)); + + grid.ColumnDefinitions.Insert(1, new ColumnDefinition { Width = new GridLength(35), SharedSizeGroup = "A" }); + + grid.Measure(new Size(200, 200)); + grid.Arrange(new Rect(new Point(), new Point(200, 200))); + Assert.All(grid.ColumnDefinitions.Where(cd => cd.SharedSizeGroup == "A"), cd => Assert.Equal(35, cd.ActualWidth)); + + grid.ColumnDefinitions[1] = new ColumnDefinition { Width = new GridLength(10), SharedSizeGroup = "A" }; + + grid.Measure(new Size(200, 200)); + grid.Arrange(new Rect(new Point(), new Point(200, 200))); + Assert.All(grid.ColumnDefinitions.Where(cd => cd.SharedSizeGroup == "A"), cd => Assert.Equal(30, cd.ActualWidth)); + + grid.ColumnDefinitions[1] = new ColumnDefinition { Width = new GridLength(50), SharedSizeGroup = "A" }; + + grid.Measure(new Size(200, 200)); + grid.Arrange(new Rect(new Point(), new Point(200, 200))); + Assert.All(grid.ColumnDefinitions.Where(cd => cd.SharedSizeGroup == "A"), cd => Assert.Equal(50, cd.ActualWidth)); + } + + [Fact] + public void Size_Priorities_Are_Maintained() + { + var sizers = new List(); + var grid = CreateGrid( + ("A", new GridLength(20)), + ("A", new GridLength(20, GridUnitType.Auto)), + ("A", new GridLength(1, GridUnitType.Star)), + ("A", new GridLength(1, GridUnitType.Star)), + (null, new GridLength())); + for (int i = 0; i < 3; i++) + sizers.Add(AddSizer(grid, i, 6 + i * 6)); + var scope = new Panel(); + scope.Children.Add(grid); + + var root = new TestRoot(); + root.SetValue(Grid.IsSharedSizeScopeProperty, true); + root.Child = scope; + + grid.Measure(new Size(100, 100)); + grid.Arrange(new Rect(new Point(), new Point(100, 100))); + // all in group are equal to the first fixed column + Assert.All(grid.ColumnDefinitions.Where(cd => cd.SharedSizeGroup == "A"), cd => Assert.Equal(20, cd.ActualWidth)); + + grid.ColumnDefinitions[0].SharedSizeGroup = null; + + grid.Measure(new Size(100, 100)); + grid.Arrange(new Rect(new Point(), new Point(100, 100))); + // 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)); + + grid.ColumnDefinitions[1].SharedSizeGroup = null; + + grid.Measure(new Size(double.PositiveInfinity, 100)); + grid.Arrange(new Rect(new Point(), new Point(100, 100))); + // with no constraint star columns default to the MinWidth of the sizer in the column + Assert.All(grid.ColumnDefinitions.Where(cd => cd.SharedSizeGroup == "A"), cd => Assert.Equal(6 + 2 * 6, cd.ActualWidth)); + } + // grid creators private Grid CreateGrid(params string[] columnGroups) { @@ -187,5 +272,13 @@ namespace Avalonia.Controls.UnitTests return grid; } + + private Control AddSizer(Grid grid, int column, double size = 30) + { + var ctrl = new Control { MinWidth = size, MinHeight = size }; + ctrl.SetValue(Grid.ColumnProperty,column); + grid.Children.Add(ctrl); + return ctrl; + } } }