Foredecker

Jibe!

BizzySpinner 2 – A WPF Spinning Busy State Indicator (with source)

with 4 comments

I’ve made a key improvement to my BizzySpinner control form my last post.  It now starts and stop smoothly instead of abruptly.  This is quite visually attractive.  But it was somewhat non-trivial to do.

In my first example, I had a single simple animation that was either running or stopped.  The animation behavior is now like this:

BizzySpinnerAcceleration

I tried to do this all in XAML but it proved quite complex to handle in in that language.   This was complicated as there are two key dependency properties to deal with that leads to several states: The Spin property controls the actually spinning state.   And the IsEnabled property inherited from UIElement.  Both must work correctly together.

This gave me two significant problems:

  1. The XAML was really complicated and difficult to read.  I don’t know about you, but to me, XAML is very verbose and difficult to read.  Syntax highlighting helps, but only to a degree.   There is also no such thing as a XAML debugger so a lot of trial and error is involved with large amounts of XAML.   Kaxaml is really helpful, but this was hard even with that tool.
  2. I couldn’t get a fully correct state machine in XAML: No matter what I tried I could toggle the IsEnabled and Spin dependency properties and cause the spinner to “jump” unattractively.

BizzySpinnerAcceleration-Page2 So, I simply used C# code to do this.   This approach was much simpler.   It was easy to implement the two cooperating state machines needed to implement all cases.   The spinning state machine is shown to the right.   It has four states and six transitions – not at all difficult to implement.

This state machine is implemented in the ControlSpinning() function on line 432 in BizzySpinner.xaml.cs.

The transitions caused by the SpinUp and SpinDown values are straight forward and implemented using a couple of switch statements

The transitions labeled Acceleration Complete and Deceleration Complete are more interesting:  they are implemented using the Completed event on the DoubleAnimation using to spin the control.

For example when the spinner goes form the Not Spinning state to the Accelerating state, the code simply attaches a function to the animation’s Completed event like this:

Code Snippet
  1. spinAnimation.From = SpinAngle;
  2. spinAnimation.To = (SpinAngle + (OneRotation / 8));
  3. spinAnimation.Duration = new Duration(TimeSpan.FromSeconds(SpinRate/4));
  4. spinAnimation.DecelerationRatio = 0.0;
  5. spinAnimation.AccelerationRatio = 1.0;
  6. spinAnimation.Completed += SpinContinuously;
  7. theSpinState = SpinState.Accelerating;
  8. this.BeginAnimation(SpinAngleProperty, spinAnimation);

This is the code that causes the spinner to accelerate to its normal spinning rate.  The key line is #7.   When the spinner has finished accelerating the SpinContinuously member is called.  It looks like this:

Code Snippet
  1. void SpinContinuously(object sender, EventArgs e)
  2. {
  3. spinAnimation.Completed -= SpinContinuously;
  4. spinAnimation.From = SpinAngle;
  5. spinAnimation.To = SpinAngle + OneRotation;
  6. spinAnimation.Duration = new Duration(TimeSpan.FromSeconds(SpinRate));
  7. spinAnimation.DecelerationRatio = 0.0;
  8. spinAnimation.AccelerationRatio = 0.0;
  9. spinAnimation.RepeatBehavior = RepeatBehavior.Forever;
  10. theSpinState = SpinState.Running;
  11. this.BeginAnimation(SpinAngleProperty, spinAnimation);
  12. }

Note line 3 which disconnects the SpinContinuously from the animations Completed event.   This is super important as we don’t want this called again because the next state transition when the animation completes is a different one.

The other important state machine is the one that handles the IsEnabled and Spin dependency properties.   This is what sends the SpinUp and SpinDown commands to the spinning state machine.

BizzySpinnerAcceleration-Page3

This is simpler than it looks:  there are only four important transitions – the ones in green where the SpinUp and SpinDown commands are sent to the spinning state machine.

This is all handled easily in the IsEnabledChanged event handler like this:

Code Snippet
  1. private void IsEnabledChangedHandler(Object sender, DependencyPropertyChangedEventArgs e)
  2. {
  3. if ( !(bool)e.NewValue)
  4. {
  5. //
  6. // Going enabled
  7. //
  8. Background = BackgroundBrushSave;
  9. LeaderBrush = LeaderBrushSave;
  10. TailBrush = TailBrushSave;
  11. // The control is enabled, turn on spinning if the Spin property is ture
  12. ControlSpinning(Spin ? SpinCommand.SpinUp : SpinCommand.SpinDown);
  13. } else {
  14. //
  15. // Going disabled
  16. //
  17. if (theSpinState == SpinState.NotSpinning)
  18. {
  19. SetDisabledBrushes();
  20. }
  21. else
  22. {
  23. ControlSpinning(SpinCommand.SpinDown);
  24. }
  25. }
  26. }

The resulting animation is very pleasing – the control spins up and down smoothly.   Its rotational rate can also be controlled dynamically.

You will need NUnit 2.5.3 (download) as I’ve added assertions. The code builds for C# 3.0 and .NET 3.5 using the client profile.  As with the last post, the project is for the Visual Studio 2010 BETA which you can download for free..

Installing and building the project is easy:   Just download and run the installer.   It will put the project on your desktop.  Note, it doesn’t really ‘install’ anything – there are no shortcuts and it doesn’t write to the registry.  You can move the sample directory wherever you would like, or just delete it when you are done.  No un-installing is needed.   Also note that the contents are licensed with the Microsoft Public License.

Here is the source code download

Advertisements

Written by foredecker

January 11, 2010 at 3:49 am

Posted in Coding

Tagged with ,

4 Responses

Subscribe to comments with RSS.

  1. Hi Foredecker
    I have been trying for several days now to download your improved version of the “spinning busy state indicator”. Unfortunately the Windows Live site does not work and I cannot download your project.. Is this project still available? If so I would be very grateful if you can repost it somewhere so I can download it. Many thanks

    Martin Deitch

    April 11, 2012 at 1:57 pm

  2. There is a bug that occurs if you set the Spin property to True, then to False, multiple times while the control is Decelerating, The bug will ultimately cause an InvalidOperationException because ReBeginAnimation will be subscribed multiple times to the Completed event of the spinAnimation, as the result of not being unregistered from it when you set the Spin property to False and the control is Decelerating.

    In short, the bug fix comprises of adding the following two lines
    spinAnimation.Completed -= ReBeginAnimation;//CCL fix of bug
    spinAnimation.Completed += RemoveAnimation;//CCL fix of bug
    to the ControlSpinning method, when the spinCommand is SpinDown and theSpinState is Decelerating.

    Cosmin Lazar

    June 13, 2012 at 4:37 am

  3. certification problems

    g

    August 7, 2012 at 5:56 am

  4. If you put like 20 spinners on the screen this thing will use up like 30% cpu.

    Anonymous

    February 11, 2013 at 11:34 am


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: