From 6239e4f5e303f84e2b68976080258266bbc39faa Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 1 Jun 2020 12:08:52 +0200 Subject: [PATCH 1/2] Added failing test for #4059. --- .../AnimatableTests.cs | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/tests/Avalonia.Animation.UnitTests/AnimatableTests.cs b/tests/Avalonia.Animation.UnitTests/AnimatableTests.cs index b5c61883e7..784f40fe1f 100644 --- a/tests/Avalonia.Animation.UnitTests/AnimatableTests.cs +++ b/tests/Avalonia.Animation.UnitTests/AnimatableTests.cs @@ -2,6 +2,7 @@ using Avalonia.Controls; using Avalonia.Data; using Avalonia.Layout; +using Avalonia.Media; using Avalonia.Styling; using Avalonia.UnitTests; using Moq; @@ -265,6 +266,70 @@ namespace Avalonia.Animation.UnitTests } } + [Fact] + public void Replacing_Transitions_During_Animation_Does_Not_Throw_KeyNotFound() + { + // Issue #4059 + using (UnitTestApplication.Start(TestServices.RealStyler)) + { + Border target; + var clock = new TestClock(); + var root = new TestRoot + { + Clock = clock, + Styles = + { + new Style(x => x.OfType()) + { + Setters = + { + new Setter(Border.TransitionsProperty, + new Transitions + { + new DoubleTransition + { + Property = Border.OpacityProperty, + Duration = TimeSpan.FromSeconds(1), + }, + }), + }, + }, + new Style(x => x.OfType().Class("foo")) + { + Setters = + { + new Setter(Border.TransitionsProperty, + new Transitions + { + new DoubleTransition + { + Property = Border.OpacityProperty, + Duration = TimeSpan.FromSeconds(1), + }, + }), + new Setter(Border.OpacityProperty, 0.0), + }, + }, + }, + Child = target = new Border + { + Background = Brushes.Red, + } + }; + + root.Measure(Size.Infinity); + root.Arrange(new Rect(root.DesiredSize)); + + target.Classes.Add("foo"); + clock.Step(TimeSpan.FromSeconds(0)); + clock.Step(TimeSpan.FromSeconds(0.5)); + + Assert.Equal(0.5, target.Opacity); + + target.Classes.Remove("foo"); + } + } + private static Mock CreateTarget() { return CreateTransition(Visual.OpacityProperty); From 0694b22c51b96fb8d997ab2dc25a4d5503789a3f Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 1 Jun 2020 13:50:29 +0200 Subject: [PATCH 2/2] Fix KeyNotFound exception in Animatable. When transitions are replaced, add the new transitions before removing the old transitions, so that when the old transition being disposed causes the value to change, there is a corresponding entry in `_transitionStates`. Also search the `Transitions` collection from last to first to find a matching transition, so that later added transitions have priority. Fixes #4059 --- src/Avalonia.Animation/Animatable.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Animation/Animatable.cs b/src/Avalonia.Animation/Animatable.cs index 9e9b84537b..067d9f462f 100644 --- a/src/Avalonia.Animation/Animatable.cs +++ b/src/Avalonia.Animation/Animatable.cs @@ -93,17 +93,17 @@ namespace Avalonia.Animation var oldTransitions = change.OldValue.GetValueOrDefault(); var newTransitions = change.NewValue.GetValueOrDefault(); - if (oldTransitions is object) - { - oldTransitions.CollectionChanged -= TransitionsCollectionChanged; - RemoveTransitions(oldTransitions); - } - if (newTransitions is object) { newTransitions.CollectionChanged += TransitionsCollectionChanged; AddTransitions(newTransitions); } + + if (oldTransitions is object) + { + oldTransitions.CollectionChanged -= TransitionsCollectionChanged; + RemoveTransitions(oldTransitions); + } } else if (_transitionsEnabled && Transitions is object && @@ -111,8 +111,10 @@ namespace Avalonia.Animation !change.Property.IsDirect && change.Priority > BindingPriority.Animation) { - foreach (var transition in Transitions) + for (var i = Transitions.Count -1; i >= 0; --i) { + var transition = Transitions[i]; + if (transition.Property == change.Property) { var state = _transitionState[transition];