WPF MVVM Property Change Animation

前端 未结 4 1012
盖世英雄少女心
盖世英雄少女心 2020-12-13 22:30

I am looking for a clean way to start an animation that will have dynamic values. Basically I want to do an animation where an element changes width based on the data of an

4条回答
  •  生来不讨喜
    2020-12-13 23:17

    Actually you want to bind the DoubleAnimation.ToProperty to the ViewModel property and animate actual control. The problem is animation should be continued when ToProperty changed. My solution encapsulate all this logic to a MarkupExtenstion which wraps a Binding.

    public class AnimateBindingExtension : MarkupExtension {
        static DependencyPropertyDescriptor dpd =
            DependencyPropertyDescriptor.FromProperty(DoubleAnimation.ToProperty, 
                typeof(DoubleAnimation));
    
        public AnimateBindingExtension(PropertyPath path) {
            Path = path;
        }
    
        public bool ValidatesOnExceptions { get; set; }
        public IValueConverter Converter { get; set; }
        public object ConverterParamter { get; set; }
        public string ElementName { get; set; }
        public RelativeSource RelativeSource { get; set; }
        public object Source { get; set; }
        public bool ValidatesOnDataErrors { get; set; }
        [ConstructorArgument("path")]
        public PropertyPath Path { get; set; }
        public object TargetNullValue { get; set; }
    
        public override object ProvideValue(IServiceProvider serviceProvider) {
            var valueProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
    
            if (valueProvider == null) {
                throw new Exception("could not get IProviderValueTarget service.");
            }
    
            var bindingTarget = valueProvider.TargetObject as FrameworkElement;
            var bindingProperty = valueProvider.TargetProperty as DependencyProperty;
    
            if (bindingProperty == null || bindingTarget == null) {
                throw new Exception();
            }
    
            var binding = new Binding {
                Path = Path,
                Converter = Converter,
                ConverterParameter = ConverterParamter,
                ValidatesOnDataErrors = ValidatesOnDataErrors,
                ValidatesOnExceptions = ValidatesOnExceptions,
                TargetNullValue = TargetNullValue
            };
    
            if (ElementName != null) binding.ElementName = ElementName;
            else if (RelativeSource != null) binding.RelativeSource = RelativeSource;
            else if (Source != null) binding.Source = Source;
    
            // you can add a Duration property to this class and use it here
            var anim = new DoubleAnimation {
                Duration = new Duration(TimeSpan.FromSeconds(0.1)),
                AccelerationRatio = 0.2,
                DecelerationRatio = 0.8
            };
            // this can be a new subclass of DoubleAnimation that 
            // overrides ToProperty metadata and add a property 
            // change callback
            dpd.AddValueChanged(anim, (s, e) => bindingTarget.BeginAnimation(bindingProperty, anim));
    
            BindingOperations.SetBinding(anim, DoubleAnimation.ToProperty, binding);
            // this is because we need to catch the DataContext so add animation object 
            // to the visual tree by adding it to target object's resources.
            bindingTarget.Resources[bindingProperty.Name] = anim;
            // animation will set the value
            return DependencyProperty.UnsetValue;
        }
    }
    

    You can do the same with other animation classes to animate other types.

提交回复
热议问题