Wednesday, October 20, 2010

Win7/Vista like busy Indicator control using Spinner animation in WPF

I have been working on WPF XBAP projects which establish the connection between mobile and system similar to Remote Desktop connectivity. I am in need to show the current status and waiting status between mobile and system connection. Hence I've created this control which is showing similar to Win7/Vista busy indicator. I am happy to share the control with you.

Initially I have a tried to show the circle shaped rectangular using rotate animation.  But I couldn't get the expected feel similar to Win7/Vista like busy indicator. Then I have spent few minutes in net and I come to know about the spinner animation and found the ImageStrip control from this link for implementing Image animation.  Then I have created Win7/Vista like busy indicator using the following code and control template.
ImageStrip Class:-
 
1 public class ImageStrip : Control 2 { 3 #region Dependency Properties 4 5 public int Frame 6 { 7 get { return (int)GetValue(FrameProperty); } 8 set { SetValue(FrameProperty, value); } 9 } 10 11 public static readonly DependencyProperty FrameProperty = 12 DependencyProperty.Register("Frame", typeof(int), typeof(ImageStrip), new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.AffectsRender)); 13 14 public double FrameSize 15 { 16 get { return (double)GetValue(FrameSizeProperty); } 17 set { SetValue(FrameSizeProperty, value); } 18 } 19 20 public static readonly DependencyProperty FrameSizeProperty = 21 DependencyProperty.Register("FrameSize", typeof(double), typeof(ImageStrip), new FrameworkPropertyMetadata(0D, FrameworkPropertyMetadataOptions.AffectsRender)); 22 23 public ImageSource Image 24 { 25 get { return (ImageSource)GetValue(ImageProperty); } 26 set { SetValue(ImageProperty, value); } 27 } 28 29 public static readonly DependencyProperty ImageProperty = 30 DependencyProperty.Register("Image", typeof(ImageSource), typeof(ImageStrip), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender)); 31 32 public Orientation Orientation 33 { 34 get { return (Orientation)GetValue(OrientationProperty); } 35 set { SetValue(OrientationProperty, value); } 36 } 37 38 public static readonly DependencyProperty OrientationProperty = 39 DependencyProperty.Register("Orientation", typeof(Orientation), typeof(ImageStrip), new FrameworkPropertyMetadata(Orientation.Horizontal, FrameworkPropertyMetadataOptions.AffectsRender)); 40 41 #endregion 42 43 #region Rendering 44 45 protected override void OnRender(DrawingContext drawingContext) 46 { 47 if (Image != null) 48 { 49 Rect rect = new Rect(0, 0, RenderSize.Width, RenderSize.Height); 50 51 ImageBrush brush = new ImageBrush(Image); 52 brush.Stretch = Stretch.None; 53 brush.Viewbox = (Orientation == Orientation.Vertical) ? 54 new Rect(0, (((Frame + 0.5) * FrameSize) / Image.Height) - 0.5, 1, 1) : 55 new Rect((((Frame + 0.5) * FrameSize) / Image.Width) - 0.5, 0, 1, 1); 56 57 drawingContext.DrawRectangle(brush, null, rect); 58 } 59 } 60 61 #endregion 62 } 63

BusyIndicator Class:-
 
1 public class BusyIndicator : Control 2 { 3 static BusyIndicator() 4 { 5 DefaultStyleKeyProperty.OverrideMetadata(typeof(BusyIndicator), new FrameworkPropertyMetadata(typeof(BusyIndicator))); 6 } 7 8 public string Text 9 { 10 get { return (string)GetValue(TextProperty); } 11 set { SetValue(TextProperty, value); } 12 } 13 public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(BusyIndicator), new UIPropertyMetadata(null)); 14 } 15
XAML Style:-
 
1 <ResourceDictionary 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:local="clr-namespace:SpinnerAnimation"> 5 6 <Style x:Key="{x:Type local:BusyIndicator}" TargetType="{x:Type local:BusyIndicator}"> 7 <Setter Property="HorizontalAlignment" Value="Center" /> 8 <Setter Property="VerticalAlignment" Value="Center" /> 9 <Setter Property="Template" > 10 <Setter.Value> 11 <ControlTemplate TargetType="{x:Type local:BusyIndicator}"> 12 <Border x:Name="mainBorder" Background="#FFFFFFFF" CornerRadius="3"> 13 <Grid> 14 <Border x:Name="shaddowBorder" Background="#FFFFFFFF" CornerRadius="3" BorderBrush="#0D566B" BorderThickness="1"> 15 <Border.Effect> 16 <DropShadowEffect BlurRadius="2" ShadowDepth="3" Opacity="0.6" Color="#FF000000" Direction="310"/> 17 </Border.Effect> 18 </Border> 19 <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> 20 <Viewbox Height="24" Width="24" Margin="2"> 21 <local:ImageStrip Image="Images\BusyIndicator.png" FrameSize="20" Width="20" Height="20" Margin="0 3 0 0"> 22 <local:ImageStrip.Effect> 23 <DropShadowEffect BlurRadius="3" ShadowDepth="3" Opacity="0.6" Color="#FF000000" Direction="300"/> 24 </local:ImageStrip.Effect> 25 <FrameworkElement.Triggers> 26 <EventTrigger RoutedEvent="FrameworkElement.Loaded"> 27 <BeginStoryboard> 28 <Storyboard> 29 <Int32Animation From="0" To="17" Duration="0:0:0.75" Storyboard.TargetProperty="Frame" RepeatBehavior="Forever" /> 30 </Storyboard> 31 </BeginStoryboard> 32 </EventTrigger> 33 </FrameworkElement.Triggers> 34 </local:ImageStrip> 35 </Viewbox> 36 <Label x:Name="label" FontFamily="Calibri" Content="{TemplateBinding Text}" FontSize="12" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center" /> 37 </StackPanel> 38 </Grid> 39 </Border> 40 <ControlTemplate.Triggers> 41 <Trigger Property="Text" Value="{x:Null}"> 42 <Setter Property="Visibility" Value="Collapsed" TargetName="label" /> 43 <Setter Property="Visibility" Value="Collapsed" TargetName="shaddowBorder" /> 44 </Trigger> 45 </ControlTemplate.Triggers> 46 </ControlTemplate> 47 </Setter.Value> 48 </Setter> 49 </Style> 50 51 </ResourceDictionary>

By using ImageStrip control, we can also show the downloading, ready to connect, etc., indicators. We just need to create an appropriate image and give the Frame, FrameSize, Image and Orientation (If it is required). The sample code is below,
 
1 <local:ImageStrip Image="Images/Download.png" FrameSize="22.27" Width="22" Height="22" Margin="10" Grid.Row="2" Grid.Column="3"> 2 <FrameworkElement.Triggers> 3 <EventTrigger RoutedEvent="FrameworkElement.Loaded"> 4 <BeginStoryboard> 5 <Storyboard> 6 <Int32Animation From="0" To="6" Duration="0:0:1.5" Storyboard.TargetProperty="Frame" RepeatBehavior="Forever" /> 7 </Storyboard> 8 </BeginStoryboard> 9 </EventTrigger> 10 </FrameworkElement.Triggers> 11 </local:ImageStrip> 12 13 <local:ImageStrip Image="Images/Ready.png" FrameSize="21" Width="21" Height="21" Grid.Row="3" Grid.Column="2"> 14 <FrameworkElement.Triggers> 15 <EventTrigger RoutedEvent="FrameworkElement.Loaded"> 16 <BeginStoryboard> 17 <Storyboard> 18 <Int32Animation From="0" To="5" Duration="0:0:1.3" Storyboard.TargetProperty="Frame" RepeatBehavior="Forever" /> 19 </Storyboard> 20 </BeginStoryboard> 21 </EventTrigger> 22 </FrameworkElement.Triggers> 23 </local:ImageStrip> 24

Sample Output:-
Happy Coding… J

No comments: