Silverlight Clock
Here we focus how to draw a clock using Silverlight. I have seen this in a Walkthrough: Creating a Silverlight Clock by Using Expression Blend or Code example from the Silverlight Documentation chm file. I will try my level best to explain the bits and pieces of this code, since I took a while to understand the entire code.
The UI is purely developed by Expression Blend. I just followed what is written in the document step by step. Some how I could make a clock like shape at the end :). I will not explaning how to draw this using Blend, but will be focusing what I have done other than the Blend tool.
This is the output of this application
To use C#, click Visual C# and Silverlight on the left-hand pane and Silverlight Project under Templates.Name the project SilverlightClock. After drawing different circles for the frame and the panel, this is how we draw all the required needles in the Xaml file.
<Path x:Name="secondHand" Fill="{x:Null}"
Stretch="Fill" Stroke="#FFD30946"
StrokeThickness="5" Height="53.5"
HorizontalAlignment="Left"
Margin="152,76,0,0"
VerticalAlignment="Top"
Width="5" UseLayoutRounding="False"
Data="M154,78 L154,126.5"
RenderTransformOrigin="0.4,1.234">
<Path.RenderTransform>
<RotateTransform x:Name="secondHandTransform"/>
</Path.RenderTransform>
</Path>
<Path x:Name="minuteHand" Fill="{x:Null}"
Stretch="Fill" Stroke="#FF094CD3"
StrokeThickness="5" Height="40"
HorizontalAlignment="Left"
Margin="151,88,0,0"
VerticalAlignment="Top" Width="5"
RenderTransformOrigin="0.6,1.35"
UseLayoutRounding="False"
Data="M154,78 L154,126.5">
<Path.RenderTransform>
<RotateTransform x:Name="minuteHandTransform"/>
</Path.RenderTransform>
</Path>
<Path x:Name="hourHand" Fill="{x:Null}"
Stretch="Fill" Stroke="#FFC2D309"
StrokeThickness="5" Height="30"
HorizontalAlignment="Left"
Margin="151,100.5,0,0"
VerticalAlignment="Top"
Width="5"
RenderTransformOrigin="0.6,1.4"
UseLayoutRounding="False"
Data="M154,78 L154,126.5">
<Path.RenderTransform>
<RotateTransform x:Name="hourHandTransform"/>
</Path.RenderTransform>
</Path>
We will apply animations to the rotation transforms of the clock hands. These rotation animations will make the clock hands rotate around the center of the clock. By specifying the timing of the animations, we can make the hands rotate around the clock in sync with the current time on the computer. To make this work, add the following code after the root UserControl element to add the clock hand animations.
<UserControl.Resources>
<Storyboard x:Name="clockStoryboard">
<!-- This animation targets the hour hand transform -->
<DoubleAnimation x:Name="hourAnimation"
Storyboard.TargetName="hourHandTransform"
Storyboard.TargetProperty="Angle"
Duration="12:0:0" RepeatBehavior="Forever" To="360" />
<!-- This animation targets the minute hand transform -->
<DoubleAnimation x:Name="minuteAnimation"
Storyboard.TargetName="minuteHandTransform"
Storyboard.TargetProperty="Angle"
Duration="1:0:0" RepeatBehavior="Forever" To="360" />
<!-- This animation targets the second hand transform -->
<DoubleAnimation x:Name="secondAnimation"
Storyboard.TargetName="secondHandTransform"
Storyboard.TargetProperty="Angle"
Duration="0:1:0" RepeatBehavior="Forever" To="360" />
</Storyboard>
</UserControl.Resources>
This code defines a Storyboard that contains the animations that animate the hands of the clock. To start the Storyboard, use the Loaded event to call the Begin method on the Storyboard. To do this, first attach the Loaded event to the Grid that contains the clock graphics.
<Grid x:Name="LayoutRoot" Background="White" Loaded="SetAndStartClock">
Storyboard as a container for other animation objects (for example, a DoubleAnimation) as well as other Storyboard objects. We can nest Storyboard objects within each other and specify BeginTime values for each Storyboard separately. Using nested storyboards can help us orchestrate elaborate animation sequences. Each child Storyboard waits until its parent Storyboard begins and then starts the countdown before it in turn begins.
DoubleAnimation animates the value of a Double property between two target values using linear interpolation over a specified Duration. Specifying a Duration by only an integer without any time span literal characters such as : or . will result in a Duration of that number of days! This is seldom the intended result. Usually you specify animation durations in seconds. As such, the Duration string must include preceding 0 values for hours and minutes, along with the appropriate literal : characters as separators between hours, minutes and seconds. For instance, to specify a Duration of five seconds, the Duration string would be "0:0:5".
User control static resources are used to declare instances of objects that should be alive and reusable throughout the whole life of a user control. When we declare a static resource in the Xaml of a user control Visual Studio actually declares this instance as an internal class field of that user control, thus making it directly accessible only in our code behind. Usually such things like styles and templates are declared as static resources and as result they can be used multiple times from within our control. This is how we declared the Resource above. Note that for each resource we declare, either x:Name or x:Key should be specified!
The Code behind SetAndStartClock function looks like this.
private void SetAndStartClock(object sender, EventArgs e)
{
System.DateTime currentDate = DateTime.Now;
double hourangle = (((float)currentDate.Hour) / 12) * 360 + currentDate.Minute / 2;
double minangle = (((float)currentDate.Minute) / 60) * 360;
double secangle = (((float)currentDate.Second) / 60) * 360;
hourAnimation.From = hourangle;
hourAnimation.To = hourangle + 360;
minuteAnimation.From = minangle;
minuteAnimation.To = minangle + 360;
secondAnimation.From = secangle;
secondAnimation.To = secangle + 360;
// Start the storyboard.
clockStoryboard.Begin();
}
Full Xaml file
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="SilverlightClock.MainPage"
Width="400" Height="300" mc:Ignorable="d">
<UserControl.Resources>
<Storyboard x:Name="clockStoryboard">
<!-- This animation targets the hour hand transform -->
<DoubleAnimation x:Name="hourAnimation"
Storyboard.TargetName="hourHandTransform"
Storyboard.TargetProperty="Angle"
Duration="12:0:0" RepeatBehavior="Forever" To="360" />
<!-- This animation targets the minute hand transform -->
<DoubleAnimation x:Name="minuteAnimation"
Storyboard.TargetName="minuteHandTransform"
Storyboard.TargetProperty="Angle"
Duration="1:0:0" RepeatBehavior="Forever" To="360" />
<!-- This animation targets the second hand transform -->
<DoubleAnimation x:Name="secondAnimation"
Storyboard.TargetName="secondHandTransform"
Storyboard.TargetProperty="Angle"
Duration="0:1:0" RepeatBehavior="Forever" To="360" />
</Storyboard>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White" Loaded="SetAndStartClock">
<Ellipse x:Name="shadowEllipse" Fill="#FF0B0B0B"
Stroke="#FF000000"
HorizontalAlignment="Stretch"
Margin="38,29,112,21"
VerticalAlignment="Stretch" Opacity="0.3"/>
<Ellipse x:Name="outerRimEllipse" Stroke="#FF000000"
HorizontalAlignment="Stretch"
Margin="32,20,118,30"
VerticalAlignment="Stretch" Opacity="1">
<Ellipse.Fill>
<LinearGradientBrush EndPoint="0.825,0.753" StartPoint="0.155,0.203">
<GradientStop Color="#FFE4E5F4"/>
<GradientStop Color="#FFC0C0C0" Offset="0.541"/>
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Ellipse x:Name="bevelEllipse" Stroke="#FF000000"
Margin="66,0,154,66"
VerticalAlignment="Bottom"
Opacity="1" Height="180">
<Ellipse.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF000000"/>
<GradientStop Color="#FFFFFFFF" Offset="1"/>
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Ellipse x:Name="faceEllipse" Stroke="#FF000000"
Margin="85,0,174,83"
VerticalAlignment="Bottom"
Opacity="1" Height="144"
Fill="#FF000000"
RenderTransformOrigin="0.496,0.479"/>
<Ellipse x:Name="centerCircle" Fill="{x:Null}"
Stroke="#FF1EE716" HorizontalAlignment="Left"
Margin="142,130,0,145" Width="25"
StrokeThickness="8" Height="25"/>
<Path x:Name="secondHand" Fill="{x:Null}"
Stretch="Fill" Stroke="#FFD30946"
StrokeThickness="5" Height="53.5"
HorizontalAlignment="Left"
Margin="152,76,0,0"
VerticalAlignment="Top"
Width="5" UseLayoutRounding="False"
Data="M154,78 L154,126.5"
RenderTransformOrigin="0.4,1.234">
<Path.RenderTransform>
<RotateTransform x:Name="secondHandTransform"/>
</Path.RenderTransform>
</Path>
<Path x:Name="minuteHand" Fill="{x:Null}"
Stretch="Fill" Stroke="#FF094CD3"
StrokeThickness="5" Height="40"
HorizontalAlignment="Left"
Margin="151,88,0,0"
VerticalAlignment="Top" Width="5"
RenderTransformOrigin="0.6,1.35"
UseLayoutRounding="False"
Data="M154,78 L154,126.5">
<Path.RenderTransform>
<RotateTransform x:Name="minuteHandTransform"/>
</Path.RenderTransform>
</Path>
<Path x:Name="hourHand" Fill="{x:Null}"
Stretch="Fill" Stroke="#FFC2D309"
StrokeThickness="5" Height="30"
HorizontalAlignment="Left"
Margin="151,100.5,0,0"
VerticalAlignment="Top"
Width="5"
RenderTransformOrigin="0.6,1.4"
UseLayoutRounding="False"
Data="M154,78 L154,126.5">
<Path.RenderTransform>
<RotateTransform x:Name="hourHandTransform"/>
</Path.RenderTransform>
</Path>
</Grid>
</UserControl>
Full Code behind file
using System;
using System.Windows.Controls;
namespace SilverlightClock
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}
private void SetAndStartClock(object sender, EventArgs e)
{
// The current date and time.
System.DateTime currentDate = DateTime.Now;
// Find the appropriate angle (in degrees) for the hour hand
// based on the current time.
double hourangle = (((float)currentDate.Hour) / 12) * 360 + currentDate.Minute / 2;
// The same as for the minute angle.
double minangle = (((float)currentDate.Minute) / 60) * 360;
// The same for the second angle.
double secangle = (((float)currentDate.Second) / 60) * 360;
// Set the beginning of the animation (From property) to the angle
// corresponging to the current time.
hourAnimation.From = hourangle;
// Set the end of the animation (To property)to the angle
// corresponding to the current time PLUS 360 degrees. Thus, the
// animation will end after the clock hand moves around the clock
// once. Note: The RepeatBehavior property of the animation is set
// to "Forever" so the animation will begin again as soon as it completes.
hourAnimation.To = hourangle + 360;
// Same as with the hour animation.
minuteAnimation.From = minangle;
minuteAnimation.To = minangle + 360;
// Same as with the hour animation.
secondAnimation.From = secangle;
secondAnimation.To = secangle + 360;
// Start the storyboard.
clockStoryboard.Begin();
}
}
}
Reference Articles
