Create analog clock in WPF
July 16 2009 / 13:30
I know, I have waited a long time to give examples in WPF. The problem is that I don’t want to repeat what lots of people have done already, subjects like “how to make a custom button” and “differences between grid and canvas” are very common on the web.
Therefore I chose a basic tutorial to illustrate the different steps. I start with the delivered design in Adobe Illustrator and end with a full working program. I’ll try to state the difficulties you have to be aware of, and show how easy it is to create a full working program from a design.
The example I chose is an analog clock, where we will move the arrows to show the right hour, and show the current date as text below.
You can download the source here: Aaltra.Clock.zip
1. Prerequisites
In order to be able to create this program the way I did it, you need several programs:
First is Adobe Illustrator, where the designer has made his vector drawings in.
Second is a plugin for Illustrator, AI to XAML, made by Michael Swanson and freely available on this site: http://www.mikeswanson.com/xamlexport/
This plug-in makes sure that you can easily import the vector drawings from illustrator into blend or visual studio.
This brings me to the last two programs, Blend in order to modify the design into objects that you can program with, and Visual Studio to write the code for the clock.
2. Illustrator
Before you start exporting the designs to XAML, you have to be aware of several things.
- The export function cannot export a percentage of a color. This means that if your designer has used a swatch and put it on 60%, you have to convert it to RGB color before exporting (also on gradients!).
- The plugin has difficulties with drop shadows and text. Text is possible, but would need modification in Blend in order to be the same.
- When you want to “animate” objects, it is best to have them in a “normal” state in the illustrator file. For instance, in this example, the arrows of the clock would have to stand upwards to make it easier. But for the sake of showing some features of blend, I left it as it is.
- When your design contains a lot of separate elements that need functionality, it might be better to export them one by one from a blank document. But that is all up to you.
When your document is ready, you have to export it to XAML by doing:
File à export à choose “XAML for WPF” à enter filename and click “save”
3. Create the project
- Open your Visual studio, and choose to create a new project.
- Choose “WPF application”
- Choose your location and press OK
4. Paste the XAML into the project
- open the XAML file and select the XAML code within the viewbox
- paste the XAML code within the <Grid> tags
- in order to get a nice result, we will change the width and height of the window to MinWidth and MinHeight, and resolution 600 x800 respectively.
5. Adjust the project in Blend
- Open the project in Blend
- We will create a resource brush in order to be able to resize the gradient over the background.
- select the rectangle that contains the background
- Select the “Fill” at the properties of the path
- You will see a gradient brush selected
- Click the “New Brush” button
- Give it a name you can remember (like: BackgroundBrush)
- Choose to define it in “Application” (this is to be able to use this brush in the whole application)
- Remove the path that defined the background
- Select the Window object
- Choose Background
- Go to the brush resource tab
- Choose “BackgroundBrush” (or any other name you chose before)
- Now we will make the arrows that will turn with the hours and minutes.
- Select the arrow and the path on top of the arrow that will serve for the minutes.
- Right click on the two paths and group them into a grid
- Once they are grouped, you would have to move the center point to the center of the clock. Do that by moving the small circle in the center of the bounding box, to the center of the clock.
- Now the hours arrow is ready to be animated.
A special case is when the objects are not in the “normal” state when you want to use them. An example is the minutes arrow, it is pointing to number “2” instead of pointing upwards. This means we would have to rotate the individual parts until they are in the normal state.
- Select the arrow path
- Move the center point to the center of the clock
- Enter -62 in the angle field of rotation
- Repeat this procedure for the path on top of the arrow
- Repeat the procedure to group the paths like the hours arrow
- You end up having 2 grids that are the two arrows, you can check if the center point is right by rotating them and see if they are behaving like expected.
- Enter a dummy value for the angle in each grid, which we will adjust in code later.
The last thing we will perform is making sure the text field is formatted right; the conversion from illustrator to XAML sometimes breaks up the text fields. We will remove the added text fields and make it one big field. It is a very “stupid” procedure, but works perfect for me :) If you have a lot of text fields in your design, it might be better to remove them before exporting to xaml, and recreating them in Blend.
- Select the text from right to left
- If the bounding box does not include the first letter, remove the text
- Repeat this until you end up with one text field, define the text(found in “common properties”)
- Give the text field a name so visual studio can work with it
The project is now ready to be coded for; our work in Blend is finished.
6. Do the coding in Visual Studio
The biggest advantage of WPF as I see it, is that you can name almost all tags in the XAML code and then refer to it from C# code and adjust or use them.
We will name the two RotateTransform objects from the two grids, so we can reference them in code:
- Search for the <RotateTransform> tag in the XAML code below the grid
- Add an “x:Name” attribute to the tag and give it some value
- Click on the tag again and see if Visual studio recognized the name
We want to show the time and keep it updated while the application is running. Therefore we create a Timer object with an interval of 500 ms (half a second). This means we could (worst case) be 499ms late with showing the right time.
Now… the magic !
since we named the rotatetransform object, we can use it in code, which means we can do something like this: rotate_hours.Angle = … and the arrow will rotate and show the right time ;)
You have to be aware that timers are running in separate threads, which means that you have to invoke a method that will update the GUI in the main thread. That’s the reason for the empty delegate and the Dispatcher.Invoke line.
This is the full code of the program (which is nothing if I may point out :D )
private Timer _t;
public Window1()
{
InitializeComponent();
_t = new Timer();
_t.Elapsed += new ElapsedEventHandler(_t_Elapsed);
_t.Enabled = true;
_t.Interval = 500;
_t.Start();
ShowTime();
}
void _t_Elapsed(object sender, ElapsedEventArgs e)
{
Dispatcher.Invoke(new MethodDelegate(ShowTime), new object[] { });
}
private delegate void MethodDelegate();
private void ShowTime()
{
DateTime now = DateTime.Now;
double minuteAngle = now.Minute / 60.0 * 360;
double hourAngle = now.Hour / 12.0 * 360 + now.Minute/60.0*30;
rotate_hours.Angle = hourAngle;
rotate_minutes.Angle = minuteAngle;
txtTodayValue.Text = now.ToLongDateString();
}
Posted by
Pieter-Paulus Vertongen
tutorial, clock, WPF
Sokhorn
Ken
Jim
teck
Zakos