private void HandleCompleted(bool stopped = false)
        {
            var completed = _storyboardCompleted && _compositionCompleted;

            if (!completed && !stopped)
            {
                return;
            }

            if (_storyboardCompleted && _compositionCompleted)
            {
                State = AnimationSetState.Completed;
            }
            else
            {
                State = AnimationSetState.Stopped;
            }

            if (_animationTCS != null && !_animationTCS.Task.IsCompleted)
            {
                Completed?.Invoke(this, new AnimationSetCompletedEventArgs()
                {
                    Completed = _storyboardCompleted && _compositionCompleted
                });
                _animationTCS.SetResult(State == AnimationSetState.Completed);
            }
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="AnimationSet"/> class.
        /// </summary>
        /// <param name="element">The associated element</param>
        public AnimationSet(UIElement element)
        {
            if (element == null)
            {
                throw new NullReferenceException("Element must not be null");
            }

            var visual = ElementCompositionPreview.GetElementVisual(element);

            if (visual == null)
            {
                throw new NullReferenceException("Visual must not be null");
            }

            Visual = visual;
            if (Visual.Compositor == null)
            {
                throw new NullReferenceException("Visual must have a compositor");
            }

            Element     = element;
            State       = AnimationSetState.NotStarted;
            _compositor = Visual.Compositor;

            _compositionAnimations                  = new Dictionary <string, CompositionAnimation>();
            _compositionEffectAnimations            = new List <EffectAnimationDefinition>();
            _directCompositionPropertyChanges       = new Dictionary <string, object>();
            _directCompositionEffectPropertyChanges = new List <EffectDirectPropertyChangeDefinition>();
            _animationSets        = new List <AnimationSet>();
            _storyboard           = new Storyboard();
            _storyboardAnimations = new Dictionary <string, Timeline>();
            _animationTasks       = new List <AnimationTask>();
        }
        /// <summary>
        /// Starts all animations and returns an awaitable task.
        /// </summary>
        /// <returns>A <see cref="Task"/> that can be awaited until all animations have completed</returns>
        public async Task <bool> StartAsync()
        {
            if (_animationTCS == null || _animationTCS.Task.IsCompleted)
            {
                if (_animationTCS != null && _animationTCS.Task.IsCompleted)
                {
                    foreach (var set in _animationSets)
                    {
                        set.State         = AnimationSetState.NotStarted;
                        set._animationTCS = null;
                    }
                }

                State         = AnimationSetState.Running;
                _animationTCS = new TaskCompletionSource <bool>();
            }
            else
            {
                return(await _animationTCS.Task);
            }

            foreach (var set in _animationSets)
            {
                if (set.State != AnimationSetState.Completed)
                {
                    var completed = await set.StartAsync();

                    if (!completed)
                    {
                        // the animation was stopped
                        return(await _animationTCS.Task);
                    }
                }
            }

            foreach (var task in _animationTasks)
            {
                if (task.Task != null && !task.Task.IsCompleted)
                {
                    await task.Task;
                }

                // if _animationSet is stopped while task was running
                if (State == AnimationSetState.Stopped)
                {
                    return(await _animationTCS.Task);
                }
            }

            _animationTasks.Clear();

            foreach (var property in _directCompositionPropertyChanges)
            {
                typeof(Visual).GetProperty(property.Key).SetValue(Visual, property.Value);
            }

            foreach (var definition in _directCompositionEffectPropertyChanges)
            {
                definition.EffectBrush.Properties.InsertScalar(definition.PropertyName, definition.Value);
            }

            if (_compositionAnimations.Count > 0 || _compositionEffectAnimations.Count > 0)
            {
                if (_batch != null)
                {
                    if (!_batch.IsEnded)
                    {
                        _batch.End();
                    }

                    _batch.Completed -= Batch_Completed;
                }

                _batch            = _compositor.CreateScopedBatch(CompositionBatchTypes.Animation);
                _batch.Completed += Batch_Completed;

                foreach (var anim in _compositionAnimations)
                {
                    Visual.StartAnimation(anim.Key, anim.Value);
                }

                foreach (var effect in _compositionEffectAnimations)
                {
                    effect.EffectBrush.StartAnimation(effect.PropertyName, effect.Animation);
                }

                _compositionCompleted = false;
                _batch.End();
            }
            else
            {
                _compositionCompleted = true;
            }

            if (_storyboardAnimations.Count > 0)
            {
                _storyboardCompleted = false;

                _storyboard.Completed -= Storyboard_Completed;
                _storyboard.Completed += Storyboard_Completed;

                _storyboard.Begin();
            }
            else
            {
                _storyboardCompleted = true;
            }

            HandleCompleted();

            return(await _animationTCS.Task);
        }