我在WPF-Animations中很新,所以如果太容易就道歉,但我找不到任何答案(也不是我的问题)。所以: 我有非常简单的动画 - 一些画布从-45度到45度旋转。所有动画都是在XAML中制作的(在代码隐藏动画方面遇到了一些问题)。我想在值等于0时绑定函数(例如,然后制造一些噪声)。我怎么处理这个? 谢谢你的所有提示。
答案 0 :(得分:2)
我有两个选择来解决这个问题。一个是侵入性的,但是你可以更好地控制实际值,另一个不是侵入性的,但只能间接控制价值。我将在答案的最后给出示例代码和两个选项。
非侵入性解决方案
订阅DoubleAnimation
对象上的CurrentTimeInvalidated活动。如果你知道动画功能及其持续时间,你可以大致说出动画值何时接近你的事件。例如,动画持续时间为500毫秒,动画功能是线性的。然后你可以说,在250ms你就到了一半。
侵入式解决方案
请记住:DoubleAnimation
(与任何其他动画一样)只是一个类,欢迎您继承它并覆盖任何虚拟成员。如果DoubleAnimation
特别感兴趣的是GetCurrentValueCore()
方法。当然,您可以在此新类上定义任何事件或依赖项属性。现在你看到它的全部发展方向。继承DoubleAnimation
,覆盖GetCurrentValueCore()
,定义ValueChanged
事件,并在每次调用GetCurrentValueCore()
时触发它。
<强> MainWindow.xaml 强>
<Window x:Class="WpfPlayground.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:l="clr-namespace:WpfPlayground">
<Grid>
<Grid.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard Duration="00:00:00.500" Storyboard.TargetName="rectangle" RepeatBehavior="Forever">
<l:DoubleAnimationWithCallback From="0"
To="180" Duration="00:00:00.500"
Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).(RotateTransform.Angle)"
Callback="{Binding AnimationCallback, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type l:MainWindow}}}"
CurrentTimeInvalidated="OnCurrentTimeInvalidated" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
<!--We animate this rectangle-->
<Rectangle x:Name="rectangle" Width="50" Height="50" Fill="Green">
<Rectangle.LayoutTransform>
<RotateTransform />
</Rectangle.LayoutTransform>
</Rectangle>
<!--Debug information-->
<TextBlock x:Name="tbTime" HorizontalAlignment="Center" VerticalAlignment="Top"/>
<TextBlock x:Name="tbAngle" HorizontalAlignment="Center" VerticalAlignment="Bottom"/>
</Grid>
</Window>
<强> MainWindow.xaml.cs 强>
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Animation;
using System.Globalization;
namespace WpfPlayground
{
public partial class MainWindow : Window
{
public Func<double, double> AnimationCallback { get { return AnimationCallbackImpl; } }
public MainWindow()
{
InitializeComponent();
}
private double AnimationCallbackImpl(double value)
{
tbAngle.Text = value.ToString(CultureInfo.CurrentCulture);
return value;
}
private void OnCurrentTimeInvalidated(object sender, EventArgs e)
{
tbTime.Text = ((AnimationClock)sender).CurrentTime.ToString();
}
}
}
<强> DoubleAnimationWithCallback.cs 强>
using System;
using System.Windows;
using System.Windows.Media.Animation;
namespace WpfPlayground
{
public class DoubleAnimationWithCallback : DoubleAnimation
{
// Cache Callback DP, to avoid performance hit.
private Func<double, double> _callback;
// reference to frozen instance. See comments below for explanation.
private DoubleAnimationWithCallback _coreInstance;
public Func<double, double> Callback
{
get { return (Func<double, double>)GetValue(CallbackProperty); }
set { SetValue(CallbackProperty, value); }
}
public static readonly DependencyProperty CallbackProperty =
DependencyProperty.Register("Callback", typeof(Func<double, double>), typeof(DoubleAnimationWithCallback), new PropertyMetadata(null, OnCallbackChanged));
private static void OnCallbackChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var dawc = o as DoubleAnimationWithCallback;
if (dawc != null)
{
dawc.UpdateCallback(e.NewValue as Func<double, double>);
}
}
private void UpdateCallback(Func<double, double> callback)
{
_callback = callback;
if (_coreInstance != null)
{
_coreInstance._callback = _callback;
}
}
protected override Freezable CreateInstanceCore()
{
if (_coreInstance == null)
{
// When callback changes we update corresponding callback on
// the frozen object too.
_coreInstance = new DoubleAnimationWithCallback()
{
Callback = Callback
};
}
return _coreInstance;
}
protected override double GetCurrentValueCore(double defaultOriginValue, double defaultDestinationValue, AnimationClock animationClock)
{
var value = base.GetCurrentValueCore(defaultOriginValue, defaultDestinationValue, animationClock);
if (_callback != null)
{
return _callback(value);
}
return value;
}
}
}
但有一点需要注意:动画管道与Freezable
个对象一起使用,因此您必须覆盖CreateInstanceCore()
方法并返回正确的实例。此外,如果您在真实对象上更改Callback
依赖项属性,则还必须更新冻结的属性。这不是很受欢迎的做法,这就是我称之为侵入性的原因。小心这段代码并彻底测试。它只显示了一个可能的方向,而不是最终目的地。
希望这有帮助