Repository: AvaloniaUI/Avalonia
File: src/Avalonia.Animation/AnimatorStateMachine`1.cs (C#)

Code before:
                                {
                                    _currentState = KeyFramesStates.DoRun;
                                }
                                _currentIteration++;
                            }

                            if (_animationDirection == PlaybackDirection.Alternate
                             || _animationDirection == PlaybackDirection.AlternateReverse)
                                _isReversed = !_isReversed;

                            break;
                        }

                        _currentState = KeyFramesStates.Stop;
                        break;

                    case KeyFramesStates.Stop:

                        if (_fillMode == FillMode.Forward
                         || _fillMode == FillMode.Both)
                        {
                            _targetControl.SetValue(_parent.Property, _lastInterpValue, BindingPriority.LocalValue);
                        }

                        _targetObserver.OnCompleted();
                        _targetAnimation.SetDone(_targetControl);
                        Dispose();
                        handled = true;
                        break;
                    default:
                        handled = true;
                        break;
                }
            }
        }

        public IDisposable Subscribe(IObserver<object> observer)
        {
            _targetObserver = observer;
            return this;
        }

        public void Dispose()
        {
            _unsubscribe = true;
            _currentState = KeyFramesStates.Disposed;
        }
    }
}


Review comment (bug):
I've been doing some tests by re-implementing the page transitions, and I found a bug here. If the user animates a render transformation, then the value of `_targetControl` is the Transform object, not the control that is being transformed. This causes the animation to never be registered as completed.

Code after:
                                }
                                else
                                {
                                    _currentState = KeyFramesStates.DoRun;
                                }
                                _currentIteration++;
                            }

                            if (_animationDirection == PlaybackDirection.Alternate
                             || _animationDirection == PlaybackDirection.AlternateReverse)
                                _isReversed = !_isReversed;

                            break;
                        }

                        _currentState = KeyFramesStates.Stop;
                        break;

                    case KeyFramesStates.Stop:

                        if (_fillMode == FillMode.Forward
                         || _fillMode == FillMode.Both)
                        {
                            _targetControl.SetValue(_parent.Property, _lastInterpValue, BindingPriority.LocalValue);
                        }

                        _targetObserver.OnCompleted();
                        _onComplete?.Invoke();
                        Dispose();
                        handled = true;
                        break;
                    default:
                        handled = true;
                        break;
                }
            }
        }

        public IDisposable Subscribe(IObserver<object> observer)
        {
            _targetObserver = observer;
            return this;
        }

        public void Dispose()
        {
            _unsubscribe = true;
            _currentState = KeyFramesStates.Disposed;
        }
    }
}