Ask a questionAsk a question
 

AnswerAnimations: stop fails and rendering is messed up

  • Wednesday, February 13, 2008 3:07 PMBladeWise Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    I'm playing with WPF since a couple of weeks, and I've found a strange behaviour when using animations.
    To put it simply, if I use an animation in a window (Window1), then stop it, close the window and open a new one (Window2), the latter WPF context is not rendered until I force the refresh, like collapsing and reopening the window.
    Such problem is exploited starting the animation either using XAML triggers, or from code-behind, and occurs every time I close the window (Window2) while animation is playing.
    Below a code that reproduces this behaviour.

    App.xaml

    <Application x:Class="AnimationTest.App"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Startup="Application_Startup">
        <Application.Resources>
            
        </Application.Resources>
    </Application>


    App.xaml.cs

    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Data;
    using System.Linq;
    using System.Windows;

    namespace AnimationTest
    {
        /// <summary>
        /// Interaction logic for App.xaml
        /// </summary>
        public partial class App : Application
        {
            private void Application_Startup(object sender, StartupEventArgs e)
            {
                Application.Current.MainWindow = new Window2();
                (new Window1()).Show();
            }
        }
    }


    Window1.xaml

    <Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        x:Class="AnimationTest.Window1"
        Title="Window1" Height="300" Width="300" WindowStartupLocation="CenterScreen">
        <Window.Resources>
            <Storyboard x:Key="animationRotate">
                <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" RepeatBehavior="Forever" Storyboard.TargetName="rectangle" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)">
                    <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
                    <SplineDoubleKeyFrame KeyTime="00:00:02" Value="360"/>
                </DoubleAnimationUsingKeyFrames>
            </Storyboard>
        </Window.Resources>
        <Grid>
            
            <Rectangle RenderTransformOrigin="0.5,0.5" Fill="#FFE10F0F" Stroke="#FF000000" HorizontalAlignment="Center" x:Name="rectangle" VerticalAlignment="Center" Width="100" Height="100">
                <Rectangle.RenderTransform>
                    <TransformGroup>
                        <ScaleTransform ScaleX="1" ScaleY="1"/>
                        <SkewTransform AngleX="0" AngleY="0"/>
                        <RotateTransform Angle="0"/>
                        <TranslateTransform X="0" Y="0"/>
                    </TransformGroup>
                </Rectangle.RenderTransform>
            </Rectangle>
            <Button Height="32" HorizontalAlignment="Left" Margin="24,0,0,8" Name="button1" VerticalAlignment="Bottom" Width="62" Click="button1_Click">Start</Button>
            <Button Height="32" HorizontalAlignment="Right" Margin="0,0,16,8" Name="button2" VerticalAlignment="Bottom" Width="62" Click="button2_Click">Stop</Button>
            <Button Height="32" Margin="112,0,0,8" Name="button3" VerticalAlignment="Bottom" HorizontalAlignment="Left" Width="62" Click="button3_Click">Pause</Button>
        </Grid>
    </Window>


    Window1.xaml.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Media.Animation;
    using System.Windows.Navigation;
    using System.Windows.Shapes;

    namespace AnimationTest
    {
        public partial class Window1 : Window
        {
            private Storyboard m_Animation;
            public Window1()
            {
                InitializeComponent();
                m_Animation = (Storyboard)FindResource("animationRotate");
                Loaded += new RoutedEventHandler(Window1_Loaded);
            }

            void Window1_Loaded(object sender, RoutedEventArgs e)
            {
                m_Animation.Begin(rectangle, true);
            }

            protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
            {
                base.OnClosing(e);
                //m_Animation.RepeatBehavior = new RepeatBehavior(1);
                m_Animation.Stop(rectangle);
                Application.Current.MainWindow.Show();
            }

            private void button1_Click(object sender, RoutedEventArgs e)
            {
                m_Animation.Begin(rectangle, true);
            }

            private void button2_Click(object sender, RoutedEventArgs e)
            {
                m_Animation.Stop(rectangle);
            }

            private void button3_Click(object sender, RoutedEventArgs e)
            {
                m_Animation.Pause(rectangle);
            }
        }
    }


    Window2.xaml

    <Window x:Class="AnimationTest.Window2"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window2" Height="300" Width="300" WindowStartupLocation="CenterScreen" WindowState="Maximized">
        <Grid>
           
        </Grid>
    </Window>


    Window2.xaml.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Shapes;

    namespace AnimationTest
    {
        public partial class Window2 : Window
        {
            public Window2()
            {
                InitializeComponent();
            }
        }
    }


    I use such kind of code to display a splash screen with a 'loading' animation and, on closing, to show the main form of the application.

    As you can see in the examples, I tried using a button to stop the animation before closing the window; this way the following window (Window2) is rendered without problems, so I think the problem is with the Stop(.) call in the OnClosing function not working, but I cannot understand what is wrong (the method is called and does not reporta no exception or warning).


    Is it a sort of bug or I'm missing something?

Answers

  • Friday, February 15, 2008 7:39 AMMarco Zhou Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    The following code can do the trick:
    protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
    {
    base.OnClosing(e);
    this.Dispatcher.BeginInvoke(DispatcherPriority.Send, (ThreadStart)(() =>
    {
    Application.Current.MainWindow.Show();
    }));
    }

    Hope this helps

All Replies

  • Thursday, February 14, 2008 9:35 AMBladeWise Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    I'm still trying to figure this out. In my real application I use a BackgroundWorker to load some data and I start and stop the animation through the ProgressChanged event. If I put a Thread.Sleep(1000) at the end of the DoWork delegate (after the Storyboard.Stop should have been executed through a ReportProgress call), the next window renders fine, without needing a 'refersh' of the WPF rendering context.
  • Friday, February 15, 2008 7:39 AMMarco Zhou Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    The following code can do the trick:
    protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
    {
    base.OnClosing(e);
    this.Dispatcher.BeginInvoke(DispatcherPriority.Send, (ThreadStart)(() =>
    {
    Application.Current.MainWindow.Show();
    }));
    }

    Hope this helps

  • Friday, February 15, 2008 1:45 PMBladeWise Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Your method works like a charm and solves the problem completely.
    Just one question about it: could you explain me why this method is needed? If I understand corretly, this way the dispacther asks to display the window (posting a message, instantiating a new thread or whatever), while the current thread closes the 'splash' one. How this is related with the animation? (Since this problem occurs only with animation still playing while closing the window, I'm interested in understanding what happens under the hood... just to avoid similar problems).
  • Tuesday, February 19, 2008 6:47 AMMarco Zhou Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Well, I am not exactly sure about this, but it seems that after the first window is closed, and after showing the second window, the message pump temporarily cannot pump messages, that's why a BeginInvoke called is needed to help pumping messages.

    Hope this helps
  • Tuesday, February 19, 2008 6:19 PMBladeWise Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Thanks again for the solution and the kind explanation.