diff --git a/samples/RenderDemo/MainWindow.xaml b/samples/RenderDemo/MainWindow.xaml
index aa165d13f7..f37df56b73 100644
--- a/samples/RenderDemo/MainWindow.xaml
+++ b/samples/RenderDemo/MainWindow.xaml
@@ -36,6 +36,9 @@
+
+
+
diff --git a/samples/RenderDemo/Pages/CustomAnimatorPage.xaml b/samples/RenderDemo/Pages/CustomAnimatorPage.xaml
new file mode 100644
index 0000000000..b386636cae
--- /dev/null
+++ b/samples/RenderDemo/Pages/CustomAnimatorPage.xaml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
diff --git a/samples/RenderDemo/Pages/CustomAnimatorPage.xaml.cs b/samples/RenderDemo/Pages/CustomAnimatorPage.xaml.cs
new file mode 100644
index 0000000000..eed8ee29ce
--- /dev/null
+++ b/samples/RenderDemo/Pages/CustomAnimatorPage.xaml.cs
@@ -0,0 +1,27 @@
+using System.Reactive.Linq;
+using Avalonia;
+using Avalonia.Animation;
+using Avalonia.Controls;
+using Avalonia.Controls.Shapes;
+using Avalonia.Data;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using Avalonia.Markup.Xaml;
+using Avalonia.Media;
+using RenderDemo.ViewModels;
+
+namespace RenderDemo.Pages
+{
+ public class CustomAnimatorPage : UserControl
+ {
+ public CustomAnimatorPage()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}
diff --git a/samples/RenderDemo/Pages/CustomStringAnimator.cs b/samples/RenderDemo/Pages/CustomStringAnimator.cs
new file mode 100644
index 0000000000..851a2d0187
--- /dev/null
+++ b/samples/RenderDemo/Pages/CustomStringAnimator.cs
@@ -0,0 +1,16 @@
+using Avalonia.Animation.Animators;
+
+namespace RenderDemo.Pages
+{
+ public class CustomStringAnimator : Animator
+ {
+ public override string Interpolate(double progress, string oldValue, string newValue)
+ {
+ if (newValue.Length == 0) return "";
+ var step = 1.0 / newValue.Length;
+ var length = (int)(progress / step);
+ var result = newValue.Substring(0, length + 1);
+ return result;
+ }
+ }
+}
diff --git a/src/Avalonia.Animation/Animation.cs b/src/Avalonia.Animation/Animation.cs
index c42153ec4f..daa4793ef0 100644
--- a/src/Avalonia.Animation/Animation.cs
+++ b/src/Avalonia.Animation/Animation.cs
@@ -194,6 +194,33 @@ namespace Avalonia.Animation
[Content]
public KeyFrames Children { get; } = new KeyFrames();
+ // Store values for the Animator attached properties for IAnimationSetter objects.
+ private static readonly Dictionary s_animators = new Dictionary();
+
+ ///
+ /// Gets the value of the Animator attached property for a setter.
+ ///
+ /// The animation setter.
+ /// The property animator type.
+ public static Type GetAnimator(IAnimationSetter setter)
+ {
+ if (s_animators.TryGetValue(setter, out var type))
+ {
+ return type;
+ }
+ return null;
+ }
+
+ ///
+ /// Sets the value of the Animator attached property for a setter.
+ ///
+ /// The animation setter.
+ /// The property animator value.
+ public static void SetAnimator(IAnimationSetter setter, Type value)
+ {
+ s_animators[setter] = value;
+ }
+
private readonly static List<(Func Condition, Type Animator)> Animators = new List<(Func, Type)>
{
( prop => typeof(bool).IsAssignableFrom(prop.PropertyType), typeof(BoolAnimator) ),
@@ -248,7 +275,7 @@ namespace Avalonia.Animation
{
foreach (var setter in keyframe.Setters)
{
- var handler = GetAnimatorType(setter.Property);
+ var handler = Animation.GetAnimator(setter) ?? GetAnimatorType(setter.Property);
if (handler == null)
{