Функция обратного вызова, когда значение равно указанному номеру - PullRequest
1 голос
/ 14 января 2011

Я довольно новичок в WPF-анимации, поэтому прошу прощения, если это слишком просто, но я не могу найти ни ответа (ни моего вопроса).Итак: у меня очень простая анимация - некоторые холсты вращаются от угла -45 градусов до 45 градусов.Вся анимация сделана в XAML (есть некоторые проблемы с анимацией кода).Я хотел бы связать функцию, когда значение равно 0 (например, пошуметь).Как я могу подойти к этому?Спасибо за все подсказки.

1 Ответ

2 голосов
/ 14 января 2011

У меня есть два варианта решения этой проблемы. Один навязчив, но дает вам больший контроль над фактическим значением, другой не навязчив, но дает вам только косвенный контроль над значением. Я приведу пример кода с обоими вариантами в конце ответа.

Не навязчивое решение

Подпишитесь на событие CurrentTimeInvalidated вашего объекта DoubleAnimation. Если вы знаете функцию анимации и ее продолжительность, вы можете приблизительно сказать, когда значение анимации близко к вашему событию. Например, продолжительность анимации составляет 500 мс, а функция анимации линейная. Тогда вы можете сказать, что в 250 мс вы на полпути.

Интрузивное решение

Помните: 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 свойство зависимости от реального объекта, вам также придется обновить замороженный объект. Это не совсем желанная практика, и поэтому я называю это навязчивым. Будьте очень осторожны с этим кодом и тщательно тестируйте его. Он просто показывает возможное направление и не является конечным пунктом назначения.

Надеюсь, это поможет

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...