是否有一种干净的方式来动画只读DependencyProperty的值?

时间:2015-02-26 11:02:26

标签: wpf animation wpf-controls dependency-properties

我有一个控件,我想要为只读DependencyProperty

的值设置动画

使用像这样的香草动画不起作用(按预期):

var animation = new DoubleAnimation(value, TimeSpan.FromMilliseconds(100));
this.BeginAnimation(SomeValueProperty, animation);

猜猜我正在寻找类似的东西:

var animation = new DoubleAnimation(value, TimeSpan.FromMilliseconds(100));
this.BeginAnimation(SomeValuePropertyKey, animation);

是否有一种使用框架动画我的属性的简洁方法?

3 个答案:

答案 0 :(得分:2)

找到一个〜聪明的解决方案:

public class DummyControl : Control
{
    internal static readonly DependencyPropertyKey SomePropPropertyKey = DependencyProperty.RegisterReadOnly(
        "SomeProp",
        typeof(double),
        typeof(DummyControl),
        new PropertyMetadata(0.0));

    // A proxy that is used for animating. Sets the value of the readonly property on change.
    private static readonly DependencyProperty SomePropProxyProperty = DependencyProperty.Register(
        "SomePropProxy",
        typeof(double),
        typeof(DummyControl),
        new PropertyMetadata(0.0, OnSomePropProxyChanged));

    public static readonly DependencyProperty SomePropProperty = SomePropPropertyKey.DependencyProperty;

    public double SomeProp
    {
        get { return (double)this.GetValue(SomePropProperty); }
        protected set { this.SetValue(SomePropPropertyKey, value); }
    }

    public void AnimateTo(double value)
    {
        var animation = new DoubleAnimation(value, TimeSpan.FromMilliseconds(100));
        this.BeginAnimation(SomePropProxyProperty, animation);
        // We are animating the proxy here.
    }

    private static void OnSomePropProxyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        d.SetValue(SomePropPropertyKey, e.NewValue);
        // Updating the value of the readonly property here.
    }
}

这不是一个非常干净的解决方案,但它至少使用标准动画。请提出更好的建议!

答案 1 :(得分:1)

公开代理属性是一种解决方案,但令人毛骨悚然的是,这制动了无用的只读限制。更好的方法是创建一个代理对象。这将使动画属性只能在您正在编写的控件内访问。

public class AnimationProxy<T> : Animatable
{

    private Action<T> _valueUpdateCallback;

    #region AnimatableProperty

    public static readonly DependencyProperty AnimatablePropertyProperty =
        DependencyProperty.Register("AnimatableProperty", typeof(T), typeof(AnimationProxy<T>),
            new FrameworkPropertyMetadata(default(T),
                new PropertyChangedCallback(OnAnimatablePropertyChanged)));

    public T AnimatableProperty
    {
        get { return (T)GetValue(AnimatablePropertyProperty); }
        set { SetValue(AnimatablePropertyProperty, value); }
    }

    private static void OnAnimatablePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        AnimationProxy<T> target = (AnimationProxy<T>)d;
        T oldAnimatableProperty = (T)e.OldValue;
        T newAnimatableProperty = target.AnimatableProperty;
        target.OnAnimatablePropertyChanged(oldAnimatableProperty, newAnimatableProperty);
    }

    protected virtual void OnAnimatablePropertyChanged(T oldAnimatableProperty, T newAnimatableProperty)
    {
        _valueUpdateCallback?.Invoke(newAnimatableProperty);
    }

    #endregion


    public AnimationProxy(Action<T> valueUpdateCallback)
    {
        _valueUpdateCallback = valueUpdateCallback;
    }

    //Override to keep compiler happy
    protected override Freezable CreateInstanceCore()
    {
        throw new NotImplementedException();
    }
}

所以现在要设置readonly属性的动画,您只需提供回调并为代理对象设置动画:

var animationProxy = new AnimationProxy<double>(SetReadOnlyProperty);
animationProxy.AnimatableProperty = ReadOnlyProperty;
var animation = new DoubleAnimation(0, new Duration(CurrentTimeLeft));
animationProxy.BeginAnimation(AnimationProxy<double>.AnimatablePropertyProperty, animation);

答案 2 :(得分:-1)

尝试这样的事情:

DoubleAnimation anim = new DoubleAnimation()
                        {
                            From = 0,
                            To = 200,
                            Duration = TimeSpan.FromMilliseconds(600),
                            DecelerationRatio = 1
                        };
                        anim.Completed += (objAnim, eAnim) =>
                        {
                            //Do something when animation completed
                        };
yourControl.BeginAnimation(Canvas.LeftProperty, anim);