简体   繁体   中英

How can I make a storyboard fade in, pause, and then fade out (through code)?

I wrote the following code. It tries to create a storyboard that does the following:

  1. fade in for 500ms
  2. pause for 1000ms
  3. fade out for 500ms

But at run time get an System.InvalidOperationException followed by the following output:

Additional information: Multiple animations in 
  the same containing Storyboard cannot target the
  same property on a single element.

This seems to suggest its trying to do all the animations at once rather than sequentially.

private Storyboard createStoryboard()
{
  Storyboard board = new Storyboard();

  addFadeToStoryboard(board, 0, 1, 500);
  addFadeToStoryboard(board, 1, 1, 1000);

  DoubleAnimation fadeOut = addFadeToStoryboard(board, 1, 0, 500);

  fadeOut.Completed += new EventHandler(onFadeCompleted);

  Storyboard.SetTarget(board, this);

  return board;
}

private DoubleAnimation addFadeToStoryboard(Storyboard board, 
  double fadeFrom, double fadeTo, double milliseconds)
{
  DoubleAnimation fade = new DoubleAnimation()
  {
    Duration = new Duration(TimeSpan.FromMilliseconds(milliseconds)),
    From = fadeFrom,
    To = fadeTo,
    RepeatBehavior = new RepeatBehavior(1)
  };

  Storyboard.SetTargetProperty(fade,
    new PropertyPath(UIElement.OpacityProperty));

  board.Children.Add(fade);

  return fade;
}

How can I make it sequential? Am I misinterpreting something fundamental about storyboards?

Thanks

If the storyboard contains multiple animations they all get started at the same time and run simultaneously. You can set the BeginTime property on the DoubleAnimations to a TimeSpan to indicate when they should begin...so by passing in an accumulative time of all the previous animations you should be able to get the sequential effect.

EDIT: Sorry - I just noticed the Silverlight tag. My answer works in WPF...I don't know about the differences between Silverlight and WPF animations.

This might help. It looks like it is a difference between WPF and Silverlight. Where WPF can handle animating the same property on different animations in the same storyboard, Silverlight doesn't. Instead of having separate DoubleAnimations they suggest using a single DoubleAnimationUsingKeyFrames...then each of your separate animations becomes a key frame within it and it should animate linearly between them.

I've solved this by creating my own sequencer class. It has a dependency on a LinkedList class, so you'll have to swap that out with the standard one if you want to use this code:

using System;
using System.Windows.Media.Animation;

namespace YourNamespace
{
  // An object that contains multiple storyboards, and fires off 
  // subsequent ones as they finish.

  public class StoryboardSequencer
  {
    public StoryboardSequencer()
    {
    }

    public void add(Storyboard board)
    {
      m_boards.add(board);
      board.Completed += new EventHandler(onBoardCompleted);
    }

    // Starts the storyboard from the first.
    // If already started, this call is ignored.
    public void begin()
    {
      if (m_boards.size() > 0)
      {
        m_currentBoardIndex = 0;
        beginCurrentBoard();
      }
      else
      {
        onLastBoardCompleted();
      }
    }

    // Stops and rewinds.
    // Does not call completed handler.
    public void stop()
    {
      if (m_currentBoardIndex != -1)
      {
        Storyboard board = m_boards.get(m_currentBoardIndex);
        if (board != null)
        {
          board.Stop();
        }

        m_currentBoardIndex = -1;
      }
    }

    private void beginCurrentBoard()
    {
      Storyboard board = m_boards.get(m_currentBoardIndex);
      if (board == null)
      {
        onLastBoardCompleted();
      }
      else
      {
        board.Begin();
      }
    }

    // Triggered when the sequence completes.
    public event EventHandler Completed;

    private void onBoardCompleted(object sender, EventArgs e)
    {
      m_currentBoardIndex++;

      if (m_currentBoardIndex >= m_boards.size())
      {
        onLastBoardCompleted();
      }
      else
      {
        beginCurrentBoard();
      }
    }

    private void onLastBoardCompleted()
    {
      m_currentBoardIndex = -1;
      Completed.Invoke(this, null);
    }

    private LinkedList<Storyboard> m_boards = new LinkedList<Storyboard>();

    // The current storyboard playing, or -1 if none.
    private int m_currentBoardIndex = -1;
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM