Please note that as of October 24, 2014, the Nokia Developer Wiki will no longer be accepting user contributions, including new entries, edits and comments, as we begin transitioning to our new home, in the Windows Phone Development Wiki. We plan to move over the majority of the existing entries. Thanks for all your past and future contributions.

Retemplate Windows Phone progress bar with spinning image

From Wiki
Jump to: navigation, search

This article explains how to quickly modify the default Windows Phone progress bar to show a spinning image instead of running dots.

WP Metro Icon UI.png
SignpostIcon XAML 40.png
WP Metro Icon WP8.png
Article Metadata
Code ExampleTested with
SDK: Windows Phone 8.0 SDK
Devices(s): Nokia Lumia 920
Windows Phone 8
Created: andrejt (01 Jan 2014)
Last edited: influencer (14 Feb 2014)



In a Windows Phone app, there are lot of situations to show indeterminate progress bar. The most common case would probably be when an application is busy loading data from web server. "Indeterminate" here means that we can't determine when the process will end so we have no way of showing the progress status. The default design of Windows Phone progress bar is showing a line of dots, endlessly running from left to right.

But sometimes we need to change that design, either because of the overall brand design, or simply because a more obvious busy indicator is required. This article will present a simple tweak to the default ProgressBar control template that will allow placing a rotating image in place of those running dots.

ProgressBar control template

When you edit the control's default style, you will be presented with its template shown below:

  1. <ControlTemplate TargetType="ProgressBar">
  2.     <Grid>
  3.         <VisualStateManager.VisualStateGroups>
  4.             <VisualStateGroup x:Name="CommonStates">
  5.                 <VisualState x:Name="Determinate"/>
  6.                 <VisualState x:Name="Indeterminate">
  7.                     <Storyboard Duration="00:00:00" RepeatBehavior="Forever">
  8.                         <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="IndeterminateRoot">
  9.                             <DiscreteObjectKeyFrame KeyTime="0">
  10.                                 <DiscreteObjectKeyFrame.Value>
  11.                                     <Visibility>Visible</Visibility>
  12.                                 </DiscreteObjectKeyFrame.Value>
  13.                             </DiscreteObjectKeyFrame>
  14.                         </ObjectAnimationUsingKeyFrames>
  15.                         <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="DeterminateRoot">
  16.                             <DiscreteObjectKeyFrame KeyTime="0">
  17.                                 <DiscreteObjectKeyFrame.Value>
  18.                                     <Visibility>Collapsed</Visibility>
  19.                                 </DiscreteObjectKeyFrame.Value>
  20.                             </DiscreteObjectKeyFrame>
  21.                         </ObjectAnimationUsingKeyFrames>
  22.                     </Storyboard>
  23.                 </VisualState>
  24.             </VisualStateGroup>
  25.         </VisualStateManager.VisualStateGroups>
  26.         <Grid x:Name="DeterminateRoot" Margin="{TemplateBinding Padding}" Visibility="Visible">
  27.             <Rectangle x:Name="ProgressBarTrack" Fill="{TemplateBinding Background}" Height="4"/>
  28.             <Rectangle x:Name="ProgressBarIndicator" Fill="{TemplateBinding Foreground}" HorizontalAlignment="Left" Height="4"/>
  29.         </Grid>
  30.         <Border x:Name="IndeterminateRoot" Margin="{TemplateBinding Padding}" Visibility="Collapsed">
  31.             <Grid x:Name="SliderContainer" IsHitTestVisible="False">
  32.                 <Rectangle x:Name="Slider0" CacheMode="BitmapCache" Fill="{TemplateBinding Foreground}" HorizontalAlignment="Left" Height="4" Width="4"/>
  33.                 <Rectangle x:Name="Slider1" CacheMode="BitmapCache" Fill="{TemplateBinding Foreground}" HorizontalAlignment="Left" Height="4" Width="4"/>
  34.                 <Rectangle x:Name="Slider2" CacheMode="BitmapCache" Fill="{TemplateBinding Foreground}" HorizontalAlignment="Left" Height="4" Width="4"/>
  35.                 <Rectangle x:Name="Slider3" CacheMode="BitmapCache" Fill="{TemplateBinding Foreground}" HorizontalAlignment="Left" Height="4" Width="4"/>
  36.                 <Rectangle x:Name="Slider4" CacheMode="BitmapCache" Fill="{TemplateBinding Foreground}" HorizontalAlignment="Left" Height="4" Width="4"/>
  37.             </Grid>
  38.         </Border>
  39.     </Grid>
  40. </ControlTemplate>

The template itself is quite simple: starting with visual states for determinate and indeterminate modes, following with actual containers for that same modes. This article focuses on indeterminate mode so the IndeterminateRoot is the place to change. Rectangles in highlighted lines represent running dots from the original progress bar which you won't need so it's safe to delete them and replace with the following lines (highlighted only).

  1. <Grid x:Name="SliderContainer" IsHitTestVisible="False">
  2.     <Image Source="/Assets/refresh.png" Stretch="None" RenderTransformOrigin=".5,.5" CacheMode="BitmapCache">
  3.         <Image.Triggers>
  4.             <EventTrigger RoutedEvent="Image.Loaded">
  5.                 <BeginStoryboard>
  6.                     <Storyboard>
  7.                         <DoubleAnimation Duration="0:0:2" 
  8.                                          From="0" 
  9.                                         To="360"
  10.                                         RepeatBehavior="Forever"
  11.                                         Storyboard.TargetName="RotatorTransform"
  12.                                         Storyboard.TargetProperty="Angle" />
  13.                     </Storyboard>
  14.                 </BeginStoryboard>
  15.             </EventTrigger>
  16.         </Image.Triggers>
  17.         <Image.RenderTransform>
  18.             <RotateTransform x:Name="RotatorTransform" />
  19.         </Image.RenderTransform>
  20.     </Image>
  21. </Grid>

Instead of the dots, the control is now showing a custom image with a RotateTransform applied as a render transform. The actual rotation will be handled by the animation, defined in Image's Loaded EventTrigger. You can change the Duration property to make rotation faster or slower.

A note before closing: to avoid possible performance issues, you should always set/bind ProgressBar's IsDeterminate property according to the process it is related to. E.g. set the property to false when you don't need it displayed. Changing just the Visibility property is often not enough to release the dedicated resources. See the following MSDN documentation for more information:

Note.pngNote: You can declare this template as a style in App.xaml's Application.Resources section with a x:Key attribute and set it on your ProgressBar instances.


The code in this article presents a simple way of replacing the default Windows Phone progress bar with a custom rotating image. Additional changes to the template may be applied to suit your specific design needs.

The sample project is available here.

Version Hint

Windows Phone: [[Category:Windows Phone]]
[[Category:Windows Phone 7.5]]
[[Category:Windows Phone 8]]

Nokia Asha: [[Category:Nokia Asha]]
[[Category:Nokia Asha Platform 1.0]]

Series 40: [[Category:Series 40]]
[[Category:Series 40 1st Edition]] [[Category:Series 40 2nd Edition]]
[[Category:Series 40 3rd Edition (initial release)]] [[Category:Series 40 3rd Edition FP1]] [[Category:Series 40 3rd Edition FP2]]
[[Category:Series 40 5th Edition (initial release)]] [[Category:Series 40 5th Edition FP1]]
[[Category:Series 40 6th Edition (initial release)]] [[Category:Series 40 6th Edition FP1]] [[Category:Series 40 Developer Platform 1.0]] [[Category:Series 40 Developer Platform 1.1]] [[Category:Series 40 Developer Platform 2.0]]

Symbian: [[Category:Symbian]]
[[Category:S60 1st Edition]] [[Category:S60 2nd Edition (initial release)]] [[Category:S60 2nd Edition FP1]] [[Category:S60 2nd Edition FP2]] [[Category:S60 2nd Edition FP3]]
[[Category:S60 3rd Edition (initial release)]] [[Category:S60 3rd Edition FP1]] [[Category:S60 3rd Edition FP2]]
[[Category:S60 5th Edition]]
[[Category:Symbian^3]] [[Category:Symbian Anna]] [[Category:Nokia Belle]]

This page was last modified on 14 February 2014, at 13:50.
105 page views in the last 30 days.