Поскольку это кажется ошибкой в реализации класса ChangePropertyAction, я решил, что лучший способ докопаться до сути этого - бросить сборку в ваше любимое приложение в стиле отражателя и посмотреть на внутренности реализации..
Вот выдержка (из нее многое еще не вышло, но есть соответствующий бит):
public class ChangePropertyAction : TargetedTriggerAction<object>
{
/* some dependency properties here, like DurationProperty, ValueProperty, etc... */
protected override void Invoke(object parameter)
{
/* a lot of validation here, but skimming over that mostly. Valid input results in a call to AnimatePropertyChange() */
}
private void AnimatePropertyChange(PropertyInfo propertyInfo, object fromValue, object newValue)
{
Storyboard storyboard = new Storyboard();
Timeline timeline = !typeof (double).IsAssignableFrom(propertyInfo.PropertyType)
? (!typeof (Color).IsAssignableFrom(propertyInfo.PropertyType)
? (!typeof (Point).IsAssignableFrom(propertyInfo.PropertyType)
? this.CreateKeyFrameAnimation(fromValue, newValue)
: this.CreatePointAnimation((Point) fromValue, (Point) newValue))
: this.CreateColorAnimation((Color) fromValue, (Color) newValue))
: this.CreateDoubleAnimation((double) fromValue, (double) newValue);
timeline.Duration = this.Duration;
storyboard.Children.Add(timeline);
Storyboard.SetTarget((Timeline) storyboard, (DependencyObject) this.Target);
Storyboard.SetTargetProperty((Timeline) storyboard, new PropertyPath(propertyInfo.Name, new object[0]));
storyboard.Completed += (EventHandler) ((o, e) => propertyInfo.SetValue(this.Target, newValue, new object[0]));
storyboard.FillBehavior = FillBehavior.Stop;
storyboard.Begin();
}
private static object GetCurrentPropertyValue(object target, PropertyInfo propertyInfo)
{
FrameworkElement frameworkElement = target as FrameworkElement;
target.GetType();
object obj = propertyInfo.GetValue(target, (object[]) null);
if (frameworkElement != null && (propertyInfo.Name == "Width" || propertyInfo.Name == "Height") && double.IsNaN((double) obj))
obj = !(propertyInfo.Name == "Width") ? (object) frameworkElement.ActualHeight : (object) frameworkElement.ActualWidth;
return obj;
}
private Timeline CreateDoubleAnimation(double fromValue, double newValue)
{
return (Timeline) new DoubleAnimation()
{
From = new double?(fromValue),
To = new double?(newValue),
EasingFunction = this.Ease
};
}
}
Если вы хотите посмотреть полный код, запустите его через DotPeekили ILSpy самостоятельно, оба бесплатны: -)
Итак, в конце концов, все, что он делает, это проверяет входные данные, смотрит на тип значения и создает раскадровку с анимацией перехода, соответствующей типу свойства.Эффект 'мерцания' на самом деле представляет собой значение, кратковременно возвращающееся к своему первоначальному значению (то, которое фактически связано), когда анимация завершена, после чего привязка обновляется, чтобы отразить новое значение.Причина такого поведения кроется в одной единственной настройке свойства на раскадровке:
storyboard.FillBehavior = FillBehavior.Stop;
Этот FillBehavior определяет, что происходит, когда Временная шкала (раскадровка в данном случае) достигает своего конца.У MSDN есть это, чтобы сказать:
HoldEnd : После того, как он достигает конца своего активного периода, временная шкала задерживает свое продвижение до конца периодов активности и удержания своего родителя.
Стоп : временная шкала останавливается, если она выходит за пределы активного периода, в то время как его родитель находится внутри активного периода.
Если мы просто изменим это свойство для установкиFillBehavior.HoldEnd, мерцание исчезло.Недостатком является то, что вам придется повторно реализовать это действие TriggerAction, но вы, вероятно, можете многое пропустить, если просто хотите, чтобы он работал для двойной анимации.
Надеюсь, это кому-нибудь поможет!