Create Chrome-Like Loading Animation In WPF⭕

Introduction

 
In this article, we are going to create a Chrome-like circular loading animation control that can be used to beautify our User Interface. Whenever we have to do some long task or processing, it needs some time to do it, in the meanwhile and for better user experience and user engagement with our application, we have to show something while the task is going on.
 
Let's see how it looks.
 
Create Chrome Like Loading Animation In WPF
 
Requirements
  1. Visual Studio (or Blend for Visual Studio)
  2. Basic knowledge of WPF 
Let's proceed with the following steps by creating a WPF Control Library in Visual Studio 2019
  • Open Visual Studio and click on Create New Project.
  • In the Create New Project dialog box, select WPF User Control Library (.NET Core), you can also choose .NET Framework also, then click on the Next button.
create project
  •  In the Configure your new project give it a meaningful project name like WpfLoadingAnimation and then click on Create button.
Create Chrome Like Loading Animation In WPF
  • Open Solution Explorer, navigate to UserControl1.xaml and rename it to CircularAnimation.xaml and then open that file.
  • Then click on the project file and then click Add Reference of Microsoft.Expression.Drawing.dll (it can be found at C:\Program Files (x86)\Microsoft SDKs\Expression\Blend\.NETFramework\v4.5\Libraries)

Designing User Control

 
Include the namespace in the xaml designer window and set the DesignWidth/DesignHeight to 80 (Default size of control).
  1. <UserControl x:Class="WpfLoadingAnimation.CircularAnimation"  
  2.              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  3.              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  4.              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"   
  5.              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"  
  6.              xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing"  
  7.              mc:Ignorable="d"   
  8.              d:DesignHeight="80" d:DesignWidth="80">  
We will use Arc control to make Circular Animation control, let's design it.
  1. <Viewbox Stretch="Fill">  
  2.        <Grid x:Name="grid" Height="300" Width="300">  
  3.            <ed:Arc x:Name="arc" ArcThickness="30" EndAngle="-350" Fill="#FF27D1D1" HorizontalAlignment="Left" Height="280" Margin="10,10,0,0" Stretch="None" Stroke="Black" StartAngle="-360" VerticalAlignment="Top" Width="280" RenderTransformOrigin="0.5,0.5" StrokeThickness="0">  
  4.                <ed:Arc.RenderTransform>  
  5.                    <TransformGroup>  
  6.                        <ScaleTransform/>  
  7.                        <SkewTransform/>  
  8.                        <RotateTransform/>  
  9.                        <TranslateTransform/>  
  10.                    </TransformGroup>  
  11.                </ed:Arc.RenderTransform>  
  12.            </ed:Arc>  
  13.        </Grid>  
  14.    </Viewbox>  
We are using Viewbox control of WPF, it is used to zoom/unzoom any control. 
 
Note
We are using StartAngle as -360 so it will give us smooth rotation experience
 
Now we will see the below design from the above code
 
 Create Chrome Like Loading Animation In WPF
 
Create some properties (or better called Dependency Properties), so it will allow developer/user to customize its properties
 
In classic Windows Form we had created properties like below.
  1. public double ArcThickness { getset; }  
But in WPF, we  will use Dependency Properties. This enables animation, styling, binding, etc... (Learn more about Dependency Property)
 
Go to Solution Explorer and navigate to CircularAnimation.cs (Under CircularAnimation.xaml) or press F7 (it will navigate you to code behind file).
  1.        public double ArcThickness  
  2.        {  
  3.            get { return (double)GetValue(ArcThicknessProperty); }  
  4.            set { SetValue(ArcThicknessProperty, value); }  
  5.        }  
  6.   
  7.        // Using a DependencyProperty as the backing store for ArcThickness.  This enables animation, styling, binding, etc...  
  8.        public static readonly DependencyProperty ArcThicknessProperty =  
  9.            DependencyProperty.Register("ArcThickness"typeof(double),  
  10.                                                        typeof(CircularAnimation),  
  11.                                                        new PropertyMetadata(30D,  
  12.                                                        new PropertyChangedCallback(ThicknessChange)));  
  13.   
  14.        private static void ThicknessChange(DependencyObject d, DependencyPropertyChangedEventArgs e)  
  15.        {  
  16.            (d as CircularAnimation).arc.ArcThickness = (double)e.NewValue;  
  17.        }  
Let's understand what the above code does (Not to worry, you don't have to memorize the code for preparing Dependency Property, we will do it in an expert way)
  •  ArcThickness property will able us to modify the thickness of Arc control that we have used for the design.
  •  ArcThickness Property (Line no 8) will create DependencyProperty to register the values
> The first argument in the DependencyProperty.Registerspecifies the property name to register
> Second argument specifies the datatype of the property
> Third argument specifies the owner of the property, it will be the class name
> Fourth argument determines the metadata of the property, (at Line 11) we will provide the default value of the Arc and we will specify the PropertyChangeCallback (when the value of that property changed, it will invoke a function)
  •  ThicknessChange (Line no 14) will be invoked when the value is changed, we will change the thickness of the Arc control with the new value.
We will create one more property that will allow us to change the color of the control according to the Look and Feel (UI)
  1.         public Brush Fill  
  2.         {  
  3.             get { return (Brush)GetValue(FillProperty); }  
  4.             set { SetValue(FillProperty, value); }  
  5.         }  
  6.   
  7.         // Using a DependencyProperty as the backing store for Fill.  This enables animation, styling, binding, etc...  
  8.         public static readonly DependencyProperty FillProperty =  
  9.             DependencyProperty.Register("Fill"typeof(Brush),  
  10.                                                 typeof(CircularAnimation),  
  11.                                                 new PropertyMetadata(new SolidColorBrush(DefaultColor),  
  12.                                                 new PropertyChangedCallback(ArcFill)));  
  13.   
  14.         private static void ArcFill(DependencyObject d, DependencyPropertyChangedEventArgs e)  
  15.         {  
  16.             (d as CircularAnimation).arc.Fill = (Brush)e.NewValue;  
  17.         }  
  18.   
  19.         private static Color DefaultColor => (Color)ColorConverter.ConvertFromString("#FF27B2D1"); //nearby cyan color  
We will use the expert way to create the Dependency Property
 
Type propdp and press tab twice, it will generate the Dependency Property for us, let's see this in action
 
Create Chrome Like Loading Animation In WPF
 
The basic design of the control has been completed, now we will create animation and for this WPF has Storyboard (a timeline based animation).
  • Move back to the design view (Press SHIFT + F7)
  • We will add Storyboard animation to our control
If we look carefully there were two scenarios (one is the completion of arc and second is rotation).
 
Creating Storyboard for completion of an Arc
  1. <Storyboard x:Key="Progress" RepeatBehavior="Forever" SpeedRatio="1.5">  
  2.             <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(ed:Arc.EndAngle)" Storyboard.TargetName="arc">  
  3.                 <EasingDoubleKeyFrame KeyTime="0:0:1" Value="-10">  
  4.                     <EasingDoubleKeyFrame.EasingFunction>  
  5.                         <CircleEase EasingMode="EaseInOut"/>  
  6.                     </EasingDoubleKeyFrame.EasingFunction>  
  7.                 </EasingDoubleKeyFrame>  
  8.                 <EasingDoubleKeyFrame KeyTime="0:0:2" Value="-1">  
  9.                     <EasingDoubleKeyFrame.EasingFunction>  
  10.                         <CircleEase EasingMode="EaseInOut"/>  
  11.                     </EasingDoubleKeyFrame.EasingFunction>  
  12.                 </EasingDoubleKeyFrame>  
  13.             </DoubleAnimationUsingKeyFrames>  
  14.   
  15.   
  16.             <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(ed:Arc.StartAngle)" Storyboard.TargetName="arc">  
  17.                 <EasingDoubleKeyFrame KeyTime="0:0:1" Value="-360"/>  
  18.                 <EasingDoubleKeyFrame KeyTime="0:0:2" Value="-11">  
  19.                     <EasingDoubleKeyFrame.EasingFunction>  
  20.                         <CircleEase EasingMode="EaseInOut"/>  
  21.                     </EasingDoubleKeyFrame.EasingFunction>  
  22.                 </EasingDoubleKeyFrame>  
  23.             </DoubleAnimationUsingKeyFrames>  
  24.         </Storyboard>  
From Line no 2 to 13 we had used Easing animation (smooth elastic animation) and from Line no 16 to 23 we are changing the value of the Arc from -360 to -11 (-11 is used to keep the tale of the Arc behind, so it will look smooth start and end)
 
Creating Storyboard for rotation of an Arc
  1. <Storyboard x:Key="rotate" RepeatBehavior="Forever">  
  2.            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)" Storyboard.TargetName="arc">  
  3.                <EasingDoubleKeyFrame KeyTime="0" Value="0"/>  
  4.                <EasingDoubleKeyFrame KeyTime="0:0:1.8" Value="360"/>  
  5.            </DoubleAnimationUsingKeyFrames>  
  6.        </Storyboard>  
We are rotating the Arc with a KeyTime of 1.8 sec of 360°
The above Storyboard resides under Resource tag (i.e ControlName.Resources), the whole storyboard looks like below
  1. <UserControl.Resources>  
  2.        <Storyboard x:Key="Progress" RepeatBehavior="Forever" SpeedRatio="1.5">  
  3.            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(ed:Arc.EndAngle)" Storyboard.TargetName="arc">  
  4.                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="-10">  
  5.                    <EasingDoubleKeyFrame.EasingFunction>  
  6.                        <CircleEase EasingMode="EaseInOut"/>  
  7.                    </EasingDoubleKeyFrame.EasingFunction>  
  8.                </EasingDoubleKeyFrame>  
  9.                <EasingDoubleKeyFrame KeyTime="0:0:2" Value="-1">  
  10.                    <EasingDoubleKeyFrame.EasingFunction>  
  11.                        <CircleEase EasingMode="EaseInOut"/>  
  12.                    </EasingDoubleKeyFrame.EasingFunction>  
  13.                </EasingDoubleKeyFrame>  
  14.            </DoubleAnimationUsingKeyFrames>  
  15.            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(ed:Arc.StartAngle)" Storyboard.TargetName="arc">  
  16.                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="-360"/>  
  17.                <EasingDoubleKeyFrame KeyTime="0:0:2" Value="-11">  
  18.                    <EasingDoubleKeyFrame.EasingFunction>  
  19.                        <CircleEase EasingMode="EaseInOut"/>  
  20.                    </EasingDoubleKeyFrame.EasingFunction>  
  21.                </EasingDoubleKeyFrame>  
  22.            </DoubleAnimationUsingKeyFrames>  
  23.        </Storyboard>  
  24.   
  25.        <Storyboard x:Key="rotate" RepeatBehavior="Forever">  
  26.            <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)" Storyboard.TargetName="arc">  
  27.                <EasingDoubleKeyFrame KeyTime="0" Value="0"/>  
  28.                <EasingDoubleKeyFrame KeyTime="0:0:1.8" Value="360"/>  
  29.            </DoubleAnimationUsingKeyFrames>  
  30.        </Storyboard>  
  31.    </UserControl.Resources>   

Create Trigger to start the animation at Element Load event

 
We will start both of our Storyboard when the owner of the element loaded 
  1. <UserControl.Triggers>  
  2.        <EventTrigger RoutedEvent="FrameworkElement.Loaded">  
  3.            <BeginStoryboard x:Name="rotate_BeginStoryboard" Storyboard="{StaticResource Progress}"/>  
  4.            <BeginStoryboard x:Name="rotate_BeginStoryboard1" Storyboard="{StaticResource rotate}"/>  
  5.        </EventTrigger>  
  6.    </UserControl.Triggers>  
Our Chrome-like loading animated User Control has been completed. Now let's build our project, and we will test our control. For this follow the below steps.
  • Create another WPF project by right clicking on the Solution in Solution Explorer
  • Choose WPF (.NET Framework/.NET Core)
  • Add Reference of the User Control Project/ or the output dll
  • You will find the control in the Toolbox, just drag it into the Window as usual. 
Here is the output in Light Theme and Dark Theme (with different Fill color and ArcThickness Property)
 
Create Chrome Like Loading Animation In WPF
 

Conclusion

 
By using simple techniques like storyboard and shapes we had created our own customized loading animation. It will not only beautify the UI but it will keep the  user enjoying the application and help their productivity too. 
 
I hope you enjoyed reading the article and learned to create beautiful controls. Any suggestion/query related to this article are most welcome. Stay tuned with C# Corner for more updates.