WPF StoryBoard doesn't begin if Stop() is call

2019-08-22 16:51发布

问题:

I have a button containing an image, and I want that image rotates while some code is executed, then stop at the end of this processing.

My code almost works if I begin the storyboard, but when I add the line sb.Stop(), the animation never begins.

Here is my code:

private void refreshPostIt(int postItIndex)
    {
        Button btn = // Button to rotate

        Storyboard sb = new Storyboard();
        DoubleAnimation rotate = new DoubleAnimation();
        rotate.From = 0;
        rotate.To = 360;
        rotate.RepeatBehavior = RepeatBehavior.Forever;

        RotateTransform rt = new RotateTransform();
        btn.RenderTransformOrigin = new Point(0.5, 0.5);
        btn.RenderTransform = rt;
        Storyboard.SetTarget(rotate, btn);
        Storyboard.SetTargetName(rotate, btn.Name);
        Storyboard.SetTargetProperty(rotate, new PropertyPath("(UIElement.RenderTransform).(RotateTransform.Angle)"));
        sb.Children.Add(rotate);
        sb.Begin(this, true);

        // Some code which can take several seconds

        sb.Stop();
    }

I'm stuck.

回答1:

I have a solution that works for me.

private void refreshPostIt()
    {
        // Button btn; is defined somewhere else

        Storyboard sb = new Storyboard();
        DoubleAnimation rotate = new DoubleAnimation();
        rotate.From = 0;
        rotate.To = 360;
        rotate.RepeatBehavior = RepeatBehavior.Forever;

        RotateTransform rt = new RotateTransform();
        btn.RenderTransformOrigin = new Point(0.5, 0.5);
        btn.RenderTransform = rt;
        Storyboard.SetTarget(rotate, btn);
        Storyboard.SetTargetName(rotate, btn.Name);
        Storyboard.SetTargetProperty(rotate, new PropertyPath("(UIElement.RenderTransform).(RotateTransform.Angle)"));
        sb.Children.Add(rotate);
        sb.Begin(btn, true);

            // Do your stuff here (Not in the UI Thread)
            // Maybe use a semaphore to lock the sb.Stop(btn)

        sb.Stop(btn);
    }

whole example code:

public partial class Window1 : Window
{
    Button btn;
    Storyboard sb;
    public Window1()
    {
        btn = new Button();
        InitializeComponent();
        this.grid.Children.Add(btn);
        refreshPostIt();
    }

    private void refreshPostIt()
    {
        // Button btn; is defined somewhere else

        sb = new Storyboard();
        DoubleAnimation rotate = new DoubleAnimation();
        rotate.From = 0;
        rotate.To = 360;
        rotate.RepeatBehavior = RepeatBehavior.Forever;

        RotateTransform rt = new RotateTransform();
        btn.RenderTransformOrigin = new Point(0.5, 0.5);
        btn.RenderTransform = rt;
        Storyboard.SetTarget(rotate, btn);
        Storyboard.SetTargetName(rotate, btn.Name);
        Storyboard.SetTargetProperty(rotate, new PropertyPath("(UIElement.RenderTransform).(RotateTransform.Angle)"));
        sb.Children.Add(rotate);
        sb.Begin(btn, true);

        BackgroundWorker asd = new BackgroundWorker();
        asd.DoWork += new DoWorkEventHandler(asd_DoWork);
        asd.RunWorkerCompleted += new RunWorkerCompletedEventHandler(asd_RunWorkerCompleted);
        asd.RunWorkerAsync();

    }

    void asd_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (sb != null)
        {
            sb.Stop(btn);
        }
    }

    void asd_DoWork(object sender, DoWorkEventArgs e)
    {
        Thread.Sleep(2000);
    }
}

Ok, I want to explain what I think is your problem here. When you use the UI-Thread to do your "work" it can't update your UI and though won't animate the rotating, when its done with your "work", between starting and stopping the animation, and finally would have time to update the UI, you call to stop animating. That leads to not showing any animation. You need to put your work in a different thread to prevent this from happening.

Hope that helps.