diff --git a/src/Avalonia.Base/ValueStore.cs b/src/Avalonia.Base/ValueStore.cs index 1bdbd4ca7c..e06c5996c9 100644 --- a/src/Avalonia.Base/ValueStore.cs +++ b/src/Avalonia.Base/ValueStore.cs @@ -57,7 +57,8 @@ namespace Avalonia { if (priority == (int)BindingPriority.LocalValue) { - _propertyValues.SetValue(property, Validate(property, value)); + Validate(property, ref value); + _propertyValues.SetValue(property, value); Changed(property, priority, v, value); return; } @@ -78,7 +79,8 @@ namespace Avalonia if (priority == (int)BindingPriority.LocalValue) { - _propertyValues.AddValue(property, Validate(property, value)); + Validate(property, ref value); + _propertyValues.AddValue(property, value); Changed(property, priority, AvaloniaProperty.UnsetValue, value); return; } @@ -166,16 +168,14 @@ namespace Avalonia validate2); } - private object Validate(AvaloniaProperty property, object value) + private void Validate(AvaloniaProperty property, ref object value) { var validate = ((IStyledPropertyAccessor)property).GetValidationFunc(_owner.GetType()); if (validate != null && value != AvaloniaProperty.UnsetValue) { - return validate(_owner, value); + value = validate(_owner, value); } - - return value; } private DeferredSetter GetDeferredSetter(AvaloniaProperty property) diff --git a/src/Avalonia.Controls/AppBuilderBase.cs b/src/Avalonia.Controls/AppBuilderBase.cs index d9be9171ed..3b644191c2 100644 --- a/src/Avalonia.Controls/AppBuilderBase.cs +++ b/src/Avalonia.Controls/AppBuilderBase.cs @@ -125,9 +125,8 @@ namespace Avalonia.Controls }); // Copy-pasted because we can't call extension methods due to generic constraints - var lifetime = new ClassicDesktopStyleApplicationLifetime(Instance) {ShutdownMode = ShutdownMode.OnMainWindowClose}; - Instance.ApplicationLifetime = lifetime; - SetupWithoutStarting(); + var lifetime = new ClassicDesktopStyleApplicationLifetime() {ShutdownMode = ShutdownMode.OnMainWindowClose}; + SetupWithLifetime(lifetime); lifetime.Start(Array.Empty()); } diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs index 2533191ae4..6dd5b8cc81 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs @@ -5,12 +5,12 @@ using System.Threading; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Interactivity; +using Avalonia.Threading; namespace Avalonia.Controls.ApplicationLifetimes { public class ClassicDesktopStyleApplicationLifetime : IClassicDesktopStyleApplicationLifetime, IDisposable { - private readonly Application _app; private int _exitCode; private CancellationTokenSource _cts; private bool _isShuttingDown; @@ -34,12 +34,11 @@ namespace Avalonia.Controls.ApplicationLifetimes _activeLifetime?._windows.Add((Window)sender); } - public ClassicDesktopStyleApplicationLifetime(Application app) + public ClassicDesktopStyleApplicationLifetime() { if (_activeLifetime != null) throw new InvalidOperationException( "Can not have multiple active ClassicDesktopStyleApplicationLifetime instances and the previously created one was not disposed"); - _app = app; _activeLifetime = this; } @@ -103,7 +102,7 @@ namespace Avalonia.Controls.ApplicationLifetimes Startup?.Invoke(this, new ControlledApplicationLifetimeStartupEventArgs(args)); _cts = new CancellationTokenSource(); MainWindow?.Show(); - _app.Run(_cts.Token); + Dispatcher.UIThread.MainLoop(_cts.Token); Environment.ExitCode = _exitCode; return _exitCode; } @@ -124,7 +123,7 @@ namespace Avalonia this T builder, string[] args, ShutdownMode shutdownMode = ShutdownMode.OnLastWindowClose) where T : AppBuilderBase, new() { - var lifetime = new ClassicDesktopStyleApplicationLifetime(builder.Instance) {ShutdownMode = shutdownMode}; + var lifetime = new ClassicDesktopStyleApplicationLifetime() {ShutdownMode = shutdownMode}; builder.SetupWithLifetime(lifetime); return lifetime.Start(args); } diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs index ce4358648b..64db832a81 100644 --- a/src/Avalonia.Controls/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox.cs @@ -704,7 +704,7 @@ namespace Avalonia.Controls added.Add(e.NewValue); } - OnSelectionChanged(new SelectionChangedEventArgs(SelectionChangedEvent, removed, added)); + OnSelectionChanged(new SelectionChangedEventArgs(SelectionChangedEvent, added, removed)); } /// diff --git a/src/Avalonia.Controls/ColumnDefinition.cs b/src/Avalonia.Controls/ColumnDefinition.cs index 9c520c434e..293b6326d6 100644 --- a/src/Avalonia.Controls/ColumnDefinition.cs +++ b/src/Avalonia.Controls/ColumnDefinition.cs @@ -26,6 +26,16 @@ namespace Avalonia.Controls public static readonly StyledProperty WidthProperty = AvaloniaProperty.Register(nameof(Width), new GridLength(1, GridUnitType.Star)); + /// + /// Initializes static members of the class. + /// + static ColumnDefinition() + { + AffectsParentMeasure(MinWidthProperty, MaxWidthProperty); + + WidthProperty.Changed.AddClassHandler(OnUserSizePropertyChanged); + } + /// /// Initializes a new instance of the class. /// @@ -68,7 +78,6 @@ namespace Avalonia.Controls } set { - Parent?.InvalidateMeasure(); SetValue(MaxWidthProperty, value); } } @@ -84,7 +93,6 @@ namespace Avalonia.Controls } set { - Parent?.InvalidateMeasure(); SetValue(MinWidthProperty, value); } } @@ -100,7 +108,6 @@ namespace Avalonia.Controls } set { - Parent?.InvalidateMeasure(); SetValue(WidthProperty, value); } } diff --git a/src/Avalonia.Controls/DefinitionBase.cs b/src/Avalonia.Controls/DefinitionBase.cs index a68fe1265f..e4ae777453 100644 --- a/src/Avalonia.Controls/DefinitionBase.cs +++ b/src/Avalonia.Controls/DefinitionBase.cs @@ -7,9 +7,6 @@ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; - -using Avalonia; -using Avalonia.Collections; using Avalonia.Utilities; namespace Avalonia.Controls @@ -50,6 +47,8 @@ namespace Avalonia.Controls } } } + + Parent?.InvalidateMeasure(); } /// @@ -63,6 +62,8 @@ namespace Avalonia.Controls _sharedState.RemoveMember(this); _sharedState = null; } + + Parent?.InvalidateMeasure(); } /// @@ -114,6 +115,36 @@ namespace Avalonia.Controls } } + /// + /// Notifies parent or size scope that definition size has been changed. + /// + internal static void OnUserSizePropertyChanged(DefinitionBase definition, AvaloniaPropertyChangedEventArgs e) + { + if (definition.Parent == null) + { + return; + } + + if (definition._sharedState != null) + { + definition._sharedState.Invalidate(); + } + else + { + GridUnitType oldUnitType = ((GridLength)e.OldValue).GridUnitType; + GridUnitType newUnitType = ((GridLength)e.NewValue).GridUnitType; + + if (oldUnitType != newUnitType) + { + definition.Parent.Invalidate(); + } + else + { + definition.Parent.InvalidateMeasure(); + } + } + } + /// /// Returns true if this definition is a part of shared group. /// @@ -730,5 +761,22 @@ namespace Avalonia.Controls SharedSizeGroupProperty.Changed.AddClassHandler(OnSharedSizeGroupPropertyChanged); PrivateSharedSizeScopeProperty.Changed.AddClassHandler(OnPrivateSharedSizeScopePropertyChanged); } + + /// + /// Marks a property on a definition as affecting the parent grid's measurement. + /// + /// The properties. + 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); + } + } } } diff --git a/src/Avalonia.Controls/Grid.cs b/src/Avalonia.Controls/Grid.cs index 8ecfe349f8..23c1cd4794 100644 --- a/src/Avalonia.Controls/Grid.cs +++ b/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); } + /// + /// + /// + protected override void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e) + { + CellsStructureDirty = true; + base.ChildrenChanged(sender, e); + } + /// /// Invalidates grid caches and makes the grid dirty for measure. /// diff --git a/src/Avalonia.Controls/RowDefinition.cs b/src/Avalonia.Controls/RowDefinition.cs index 1f2f738670..85e7ed6519 100644 --- a/src/Avalonia.Controls/RowDefinition.cs +++ b/src/Avalonia.Controls/RowDefinition.cs @@ -26,6 +26,16 @@ namespace Avalonia.Controls public static readonly StyledProperty HeightProperty = AvaloniaProperty.Register(nameof(Height), new GridLength(1, GridUnitType.Star)); + /// + /// Initializes static members of the class. + /// + static RowDefinition() + { + AffectsParentMeasure(MaxHeightProperty, MinHeightProperty); + + HeightProperty.Changed.AddClassHandler(OnUserSizePropertyChanged); + } + /// /// Initializes a new instance of the class. /// @@ -68,7 +78,6 @@ namespace Avalonia.Controls } set { - Parent?.InvalidateMeasure(); SetValue(MaxHeightProperty, value); } } @@ -84,7 +93,6 @@ namespace Avalonia.Controls } set { - Parent?.InvalidateMeasure(); SetValue(MinHeightProperty, value); } } @@ -100,7 +108,6 @@ namespace Avalonia.Controls } set { - Parent?.InvalidateMeasure(); SetValue(HeightProperty, value); } } diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Validation.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Validation.cs index 2cf1eb1a97..f0e93dbb3a 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Validation.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Validation.cs @@ -78,6 +78,18 @@ namespace Avalonia.Base.UnitTests Assert.Equal(10, target.GetValue(Class1.AttachedProperty)); } + [Fact] + public void PropertyChanged_Event_Uses_Coerced_Value() + { + var inst = new Class1(); + inst.PropertyChanged += (sender, e) => + { + Assert.Equal(10, e.NewValue); + }; + + inst.SetValue(Class1.QuxProperty, 15); + } + private class Class1 : AvaloniaObject { public static readonly StyledProperty QuxProperty = diff --git a/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs b/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs index 74523d4193..dee7a84812 100644 --- a/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs +++ b/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs @@ -16,7 +16,7 @@ namespace Avalonia.Controls.UnitTests public void Should_Set_ExitCode_After_Shutdown() { using (UnitTestApplication.Start(TestServices.MockThreadingInterface)) - using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current)) + using(var lifetime = new ClassicDesktopStyleApplicationLifetime()) { lifetime.Shutdown(1337); @@ -31,7 +31,7 @@ namespace Avalonia.Controls.UnitTests public void Should_Close_All_Remaining_Open_Windows_After_Explicit_Exit_Call() { using (UnitTestApplication.Start(TestServices.StyledWindow)) - using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current)) + using(var lifetime = new ClassicDesktopStyleApplicationLifetime()) { var windows = new List { new Window(), new Window(), new Window(), new Window() }; @@ -50,7 +50,7 @@ namespace Avalonia.Controls.UnitTests public void Should_Only_Exit_On_Explicit_Exit() { using (UnitTestApplication.Start(TestServices.StyledWindow)) - using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current)) + using(var lifetime = new ClassicDesktopStyleApplicationLifetime()) { lifetime.ShutdownMode = ShutdownMode.OnExplicitShutdown; @@ -84,7 +84,7 @@ namespace Avalonia.Controls.UnitTests public void Should_Exit_After_MainWindow_Closed() { using (UnitTestApplication.Start(TestServices.StyledWindow)) - using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current)) + using(var lifetime = new ClassicDesktopStyleApplicationLifetime()) { lifetime.ShutdownMode = ShutdownMode.OnMainWindowClose; @@ -112,7 +112,7 @@ namespace Avalonia.Controls.UnitTests public void Should_Exit_After_Last_Window_Closed() { using (UnitTestApplication.Start(TestServices.StyledWindow)) - using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current)) + using(var lifetime = new ClassicDesktopStyleApplicationLifetime()) { lifetime.ShutdownMode = ShutdownMode.OnLastWindowClose; @@ -142,7 +142,7 @@ namespace Avalonia.Controls.UnitTests public void Show_Should_Add_Window_To_OpenWindows() { using (UnitTestApplication.Start(TestServices.StyledWindow)) - using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current)) + using(var lifetime = new ClassicDesktopStyleApplicationLifetime()) { var window = new Window(); @@ -156,7 +156,7 @@ namespace Avalonia.Controls.UnitTests public void Window_Should_Be_Added_To_OpenWindows_Only_Once() { using (UnitTestApplication.Start(TestServices.StyledWindow)) - using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current)) + using(var lifetime = new ClassicDesktopStyleApplicationLifetime()) { var window = new Window(); @@ -174,7 +174,7 @@ namespace Avalonia.Controls.UnitTests public void Close_Should_Remove_Window_From_OpenWindows() { using (UnitTestApplication.Start(TestServices.StyledWindow)) - using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current)) + using(var lifetime = new ClassicDesktopStyleApplicationLifetime()) { var window = new Window(); @@ -197,7 +197,7 @@ namespace Avalonia.Controls.UnitTests windowingPlatform: new MockWindowingPlatform(() => windowImpl.Object)); using (UnitTestApplication.Start(services)) - using(var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current)) + using(var lifetime = new ClassicDesktopStyleApplicationLifetime()) { var window = new Window(); diff --git a/tests/Avalonia.Controls.UnitTests/GridTests.cs b/tests/Avalonia.Controls.UnitTests/GridTests.cs index 2b9197e20b..353bb9c98d 100644 --- a/tests/Avalonia.Controls.UnitTests/GridTests.cs +++ b/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 { @@ -1178,6 +1172,41 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(0, grids[0].ColumnDefinitions[0].ActualWidth); } + [Fact] + public void Size_Group_Definition_Resizes_Are_Tracked() + { + var grids = new[] { + CreateGrid(("A", new GridLength(5, GridUnitType.Pixel)), (null, new GridLength())), + CreateGrid(("A", new GridLength(5, GridUnitType.Pixel)), (null, new GridLength())) }; + var scope = new Grid(); + foreach (var xgrids in grids) + scope.Children.Add(xgrids); + + var root = new Grid(); + root.UseLayoutRounding = false; + root.SetValue(Grid.IsSharedSizeScopeProperty, true); + root.Children.Add(scope); + + root.Measure(new Size(50, 50)); + root.Arrange(new Rect(new Point(), new Point(50, 50))); + + PrintColumnDefinitions(grids[0]); + Assert.Equal(5, grids[0].ColumnDefinitions[0].ActualWidth); + Assert.Equal(5, grids[1].ColumnDefinitions[0].ActualWidth); + + grids[0].ColumnDefinitions[0].Width = new GridLength(10, GridUnitType.Pixel); + + foreach (Grid grid in grids) + { + grid.Measure(new Size(50, 50)); + grid.Arrange(new Rect(new Point(), new Point(50, 50))); + } + + PrintColumnDefinitions(grids[0]); + Assert.Equal(10, grids[0].ColumnDefinitions[0].ActualWidth); + Assert.Equal(10, grids[1].ColumnDefinitions[0].ActualWidth); + } + [Fact] public void Collection_Changes_Are_Tracked() { @@ -1270,11 +1299,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 +1411,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; } diff --git a/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs index 56b14c3936..c263a80ef3 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs +++ b/tests/Avalonia.ReactiveUI.UnitTests/AutoSuspendHelperTest.cs @@ -45,7 +45,7 @@ namespace Avalonia.ReactiveUI.UnitTests public void AutoSuspendHelper_Should_Immediately_Fire_IsLaunchingNew() { using (UnitTestApplication.Start(TestServices.MockWindowingPlatform)) - using (var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current)) + using (var lifetime = new ClassicDesktopStyleApplicationLifetime()) { var isLaunchingReceived = false; var application = AvaloniaLocator.Current.GetService(); @@ -86,7 +86,7 @@ namespace Avalonia.ReactiveUI.UnitTests public void ShouldPersistState_Should_Fire_On_App_Exit_When_SuspensionDriver_Is_Initialized() { using (UnitTestApplication.Start(TestServices.MockWindowingPlatform)) - using (var lifetime = new ClassicDesktopStyleApplicationLifetime(Application.Current)) + using (var lifetime = new ClassicDesktopStyleApplicationLifetime()) { var shouldPersistReceived = false; var application = AvaloniaLocator.Current.GetService(); @@ -105,4 +105,4 @@ namespace Avalonia.ReactiveUI.UnitTests } } } -} \ No newline at end of file +}