You Spin me Right Round Baby, Right Round
Having completed the series on MVVM, the next few post will feature some of the controls that I’ve developed to complement the standard WPF classes.
The first is relatively simple – a busy spinner (otherwise known as a progress ring). There are already many implementations of this type of control published, but I’ve come across quite a few that either consume way too much in the way of system resources, or fail to switch off when they are no longer active / displayed.
My version of the control consists of a Canvas object containing a group of ellipses, laid out in a circle. Each ellipse has the same fill value – bound to the foreground of the control, but with a different opacity value to provide the variation in display characteristics. There’s no reason why you have to use a SolidColorBrush for this value – any other Brush type can be used if you want to get really creative. Note that UseLayoutRounding is set to false for this canvas, otherwise the ellipses will appear to wobble as the spinner rotates, due to the WPF rendering adjusting their calculated position to the closest whole pixel. The canvas is held within Viewbox, which scales it to fit within the control boundary.
The spinning action is controlled by a DispatcherTimer. This is preferred to System.Timers.Timer in WPF applications due to potential threading issues. Each tick of the timer updates the Angle value of the RotateTransform on the canvas, so that each ellipse advances by one spot. Note how the timer is activated from the IsVisibleChanged event. The covers both the direct setting of the spinner’s own Visibility property, and also the indirect case where the spinner is placed inside another visual container which itself is collapsed or hidden. To activate the spinner, just make it visible.
The demo project for this post shows the spinner in action. The visibility property of the spinner is bound to the IsExecuting property of the async relay command – which saves the need to have an additional flag property. Note how the button state is automatically set to disabled (due to perRelayCommandAsync.CanExecute returning false) while the command is being executed.
The next post will introduce another new control, but something a little more complicated this time.
As usual the code samples for this blog are available on Github, along with my own personal C# / WPF library.
If you find this article useful, or you have any other feedback, please leave a comment below.