diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Implicit.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Implicit.cs index 93c799013e8..61cfb365389 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Implicit.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Implicit.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Windows.Foundation.Collections; +using System; using Windows.UI.Xaml; using Windows.UI.Xaml.Hosting; @@ -130,30 +130,30 @@ public static void SetAnimations(UIElement element, ImplicitAnimationSet value) /// The instance for the current event. private static void OnShowAnimationsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - static void OnAnimationsChanged(IObservableVector sender, IVectorChangedEventArgs args) + static void OnAnimationsChanged(object sender, EventArgs e) { var collection = (ImplicitAnimationSet)sender; if (collection.ParentReference!.TryGetTarget(out UIElement element)) { - ElementCompositionPreview.SetImplicitShowAnimation(element, collection.GetCompositionAnimationGroup()); + ElementCompositionPreview.SetImplicitShowAnimation(element, collection.GetCompositionAnimationGroup(element)); } } if (e.OldValue is ImplicitAnimationSet oldCollection) { - oldCollection.VectorChanged -= OnAnimationsChanged; + oldCollection.AnimationsChanged -= OnAnimationsChanged; } if (d is UIElement element && e.NewValue is ImplicitAnimationSet collection) { collection.ParentReference = new(element); - collection.VectorChanged -= OnAnimationsChanged; - collection.VectorChanged += OnAnimationsChanged; + collection.AnimationsChanged -= OnAnimationsChanged; + collection.AnimationsChanged += OnAnimationsChanged; ElementCompositionPreview.SetIsTranslationEnabled(element, true); - ElementCompositionPreview.SetImplicitShowAnimation(element, collection.GetCompositionAnimationGroup()); + ElementCompositionPreview.SetImplicitShowAnimation(element, collection.GetCompositionAnimationGroup(element)); } } @@ -164,30 +164,30 @@ static void OnAnimationsChanged(IObservableVector sender, IVec /// The instance for the current event. private static void OnHideAnimationsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - static void OnAnimationsChanged(IObservableVector sender, IVectorChangedEventArgs args) + static void OnAnimationsChanged(object sender, EventArgs e) { var collection = (ImplicitAnimationSet)sender; if (collection.ParentReference!.TryGetTarget(out UIElement element)) { - ElementCompositionPreview.SetImplicitHideAnimation(element, collection.GetCompositionAnimationGroup()); + ElementCompositionPreview.SetImplicitHideAnimation(element, collection.GetCompositionAnimationGroup(element)); } } if (e.OldValue is ImplicitAnimationSet oldCollection) { - oldCollection.VectorChanged -= OnAnimationsChanged; + oldCollection.AnimationsChanged -= OnAnimationsChanged; } if (d is UIElement element && e.NewValue is ImplicitAnimationSet collection) { collection.ParentReference = new(element); - collection.VectorChanged -= OnAnimationsChanged; - collection.VectorChanged += OnAnimationsChanged; + collection.AnimationsChanged -= OnAnimationsChanged; + collection.AnimationsChanged += OnAnimationsChanged; ElementCompositionPreview.SetIsTranslationEnabled(element, true); - ElementCompositionPreview.SetImplicitHideAnimation(element, collection.GetCompositionAnimationGroup()); + ElementCompositionPreview.SetImplicitHideAnimation(element, collection.GetCompositionAnimationGroup(element)); } } @@ -198,30 +198,30 @@ static void OnAnimationsChanged(IObservableVector sender, IVec /// The instance for the current event. private static void OnAnimationsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - static void OnAnimationsChanged(IObservableVector sender, IVectorChangedEventArgs args) + static void OnAnimationsChanged(object sender, EventArgs e) { var collection = (ImplicitAnimationSet)sender; if (collection.ParentReference!.TryGetTarget(out UIElement element)) { - ElementCompositionPreview.GetElementVisual(element).ImplicitAnimations = collection.GetImplicitAnimationCollection(); + ElementCompositionPreview.GetElementVisual(element).ImplicitAnimations = collection.GetImplicitAnimationCollection(element); } } if (e.OldValue is ImplicitAnimationSet oldCollection) { - oldCollection.VectorChanged -= OnAnimationsChanged; + oldCollection.AnimationsChanged -= OnAnimationsChanged; } if (d is UIElement element && e.NewValue is ImplicitAnimationSet collection) { collection.ParentReference = new(element); - collection.VectorChanged -= OnAnimationsChanged; - collection.VectorChanged += OnAnimationsChanged; + collection.AnimationsChanged -= OnAnimationsChanged; + collection.AnimationsChanged += OnAnimationsChanged; ElementCompositionPreview.SetIsTranslationEnabled(element, true); - ElementCompositionPreview.GetElementVisual(element).ImplicitAnimations = collection.GetImplicitAnimationCollection(); + ElementCompositionPreview.GetElementVisual(element).ImplicitAnimations = collection.GetImplicitAnimationCollection(element); } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Abstract/Animation.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Abstract/Animation.cs index 210e3828d5a..40df464bd8b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Abstract/Animation.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Abstract/Animation.cs @@ -91,14 +91,14 @@ public EasingMode? EasingMode /// public RepeatOption Repeat { - get => (RepeatOption)GetValue(RepeatOptionProperty); - set => SetValue(RepeatOptionProperty, value); + get => (RepeatOption)GetValue(RepeatProperty); + set => SetValue(RepeatProperty, value); } /// /// Identifies the dependency property. /// - public static readonly DependencyProperty RepeatOptionProperty = DependencyProperty.Register( + public static readonly DependencyProperty RepeatProperty = DependencyProperty.Register( nameof(Repeat), typeof(RepeatOption), typeof(Animation), diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Abstract/ImplicitAnimation{TValue,TKeyFrame}.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Abstract/ImplicitAnimation{TValue,TKeyFrame}.cs index 3f9ffc479ec..ac13e522558 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Abstract/ImplicitAnimation{TValue,TKeyFrame}.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Abstract/ImplicitAnimation{TValue,TKeyFrame}.cs @@ -4,6 +4,7 @@ #nullable enable +using System; using Windows.UI.Composition; using Windows.UI.Xaml; using static Microsoft.Toolkit.Uwp.UI.Animations.AnimationExtensions; @@ -17,6 +18,25 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations public abstract class ImplicitAnimation : Animation, IImplicitTimeline where TKeyFrame : unmanaged { + /// + public event EventHandler? AnimationPropertyChanged; + + /// + /// Initializes a new instance of the class. + /// + protected ImplicitAnimation() + { + RegisterPropertyChangedCallback(DelayProperty, RaiseAnimationPropertyChanged); + RegisterPropertyChangedCallback(DurationProperty, RaiseAnimationPropertyChanged); + RegisterPropertyChangedCallback(EasingTypeProperty, RaiseAnimationPropertyChanged); + RegisterPropertyChangedCallback(EasingModeProperty, RaiseAnimationPropertyChanged); + RegisterPropertyChangedCallback(RepeatProperty, RaiseAnimationPropertyChanged); + RegisterPropertyChangedCallback(DelayBehaviorProperty, RaiseAnimationPropertyChanged); + RegisterPropertyChangedCallback(ToProperty, RaiseAnimationPropertyChanged); + RegisterPropertyChangedCallback(FromProperty, RaiseAnimationPropertyChanged); + RegisterPropertyChangedCallback(KeyFramesProperty, RaiseAnimationPropertyChanged); + } + /// /// Gets or sets the optional implicit target for the animation. This can act as a trigger property for the animation. /// @@ -67,5 +87,15 @@ public CompositionAnimation GetAnimation(UIElement element, out string? target) return builder.GetAnimation(element.GetVisual(), out _); } + + /// + /// Raises the event. + /// + /// The instance raising the event. + /// The that was changed. + private void RaiseAnimationPropertyChanged(DependencyObject sender, DependencyProperty property) + { + AnimationPropertyChanged?.Invoke(this, EventArgs.Empty); + } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/ImplicitAnimationSet.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/ImplicitAnimationSet.cs index eda7836e7f4..b6eb2f259f9 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/ImplicitAnimationSet.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/ImplicitAnimationSet.cs @@ -6,6 +6,7 @@ using System; using System.Diagnostics.Contracts; +using Windows.Foundation.Collections; using Windows.UI.Composition; using Windows.UI.Xaml; using Windows.UI.Xaml.Hosting; @@ -13,11 +14,73 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations { /// - /// A collection of implicit animations that can be grouped together. This type represents a composite animation - /// (such as ) that is executed on a given element. + /// A collection of implicit animations that can be assigned to a and configured to be run automatically + /// when the element is either shown or hidden (through and ), + /// or whenever one of the targeted properties on the underlying element changes (through ). + /// + /// Animations within an should be objects implementing the interface, such as + /// types inheriting from (eg. , , + /// and , or custom ones such as and ). + /// Adding incompatible elements cannot be validated at build-time, but will result in a runtime crash. + /// + /// + /// Animations will monitor for changes in real-time to any of their public properties. For instance, if a binding is used to dynamically update the + /// or properties, the entire animation set will be + /// initialized again and assigned to the underlying object for the targeted . This does not currently apply + /// to changes to the property though (other than the entire property being reassigned). To achieve + /// dynamic updates to animation sets in that case, either leverage expression keyframes or just use code-behind to manually reinitialize the animations. + /// /// + /// + /// An instance can only be used on a single target, and it cannot be shared across multiple + /// elements. Attempting to do so will result in a runtime crash. Furthermore, it is recommended not to move instances from + /// one to another, and doing so will add unnecessary runtime overhead over time. If you want to apply the same animations + /// to multiple elements, simply create another instance and another set of animations with the same properties within it. + /// public sealed class ImplicitAnimationSet : DependencyObjectCollection { + /// + /// Raised whenever any configuration change occurrs within the current instance. + /// + internal event EventHandler? AnimationsChanged; + + /// + /// Initializes a new instance of the class. + /// + public ImplicitAnimationSet() + { + VectorChanged += ImplicitAnimationSetVectorChanged; + } + + /// + /// Registers for every added animation. + /// + /// The current vector of animations. + /// The instance for the current event. + private void ImplicitAnimationSetVectorChanged(IObservableVector sender, IVectorChangedEventArgs @event) + { + if (@event.CollectionChange == CollectionChange.ItemInserted || + @event.CollectionChange == CollectionChange.ItemChanged) + { + IImplicitTimeline item = (IImplicitTimeline)sender[(int)@event.Index]; + + item.AnimationPropertyChanged -= RaiseAnimationsChanged; + item.AnimationPropertyChanged += RaiseAnimationsChanged; + } + + AnimationsChanged?.Invoke(this, EventArgs.Empty); + } + + /// + /// Raises the event. + /// + /// The instance raising the event. + /// The empty for the event. + private void RaiseAnimationsChanged(object sender, EventArgs e) + { + AnimationsChanged?.Invoke(this, e); + } + /// /// Gets or sets the weak reference to the parent that owns the current implicit animation collection. /// @@ -27,11 +90,11 @@ public sealed class ImplicitAnimationSet : DependencyObjectCollection /// Creates a for the current collection. /// This can be used to be assigned to show/hide implicit composition animations. /// + /// The target to which the animations are being applied to. /// The instance to use. [Pure] - internal CompositionAnimationGroup GetCompositionAnimationGroup() + internal CompositionAnimationGroup GetCompositionAnimationGroup(UIElement parent) { - UIElement parent = GetParent(); Compositor compositor = ElementCompositionPreview.GetElementVisual(parent).Compositor; CompositionAnimationGroup animations = compositor.CreateAnimationGroup(); @@ -47,11 +110,11 @@ internal CompositionAnimationGroup GetCompositionAnimationGroup() /// Creates an for the current collection. /// This can be used to be assigned to implicit composition animations. /// + /// The target to which the animations are being applied to. /// The instance to use. [Pure] - internal ImplicitAnimationCollection GetImplicitAnimationCollection() + internal ImplicitAnimationCollection GetImplicitAnimationCollection(UIElement parent) { - UIElement parent = GetParent(); Compositor compositor = ElementCompositionPreview.GetElementVisual(parent).Compositor; ImplicitAnimationCollection animations = compositor.CreateImplicitAnimationCollection(); @@ -71,25 +134,5 @@ internal ImplicitAnimationCollection GetImplicitAnimationCollection() return animations; } - - /// - /// Gets the current parent instance. - /// - /// The reference from . - /// Thrown if there is no parent available. - [Pure] - private UIElement GetParent() - { - UIElement? parent = null; - - if (ParentReference?.TryGetTarget(out parent) != true) - { - ThrowInvalidOperationException(); - } - - return parent!; - - static void ThrowInvalidOperationException() => throw new InvalidOperationException("The current ImplicitAnimationSet object isn't bound to a parent UIElement instance."); - } } } diff --git a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Interfaces/IImplicitTimeline.cs b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Interfaces/IImplicitTimeline.cs index 963e07bd19e..c4aa6dd2dec 100644 --- a/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Interfaces/IImplicitTimeline.cs +++ b/Microsoft.Toolkit.Uwp.UI.Animations/Xaml/Interfaces/IImplicitTimeline.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using Windows.UI.Composition; using Windows.UI.Xaml; @@ -14,6 +15,13 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations /// public interface IImplicitTimeline { + /// + /// Raised whenever a property that influences the animation changes. + /// This event is used by to update the animations collection + /// assigned to a target when any of the individual animations is modified. + /// + event EventHandler? AnimationPropertyChanged; + /// /// Gets a from the current node. This animation might /// be used either as an implicit show/hide animation, or as a direct implicit animation.