A pesar del tiempo que hace que colgué mi último post (hace casi dos meses), no he parado quieto y he seguido programando:

 

 

y asistiendo a eventos como el de “Coding Camp de Windows Phone 7” que organizó Microsoft hace un par de semanas entre otros.

Últimamente estoy tocando tecnologías como WPF, Silverlight (sobre todo en WP7) y con ellas algo de MVVM (Model View ViewModel). Este último tiene algo de historia hasta que te acostumbras pero poco a poco me voy haciendo a su uso.

Así que voy a colgar artículos (algo básicos al principio) sobre estas tecnologías por si pudieran ayudar a alguien a comenzar a trabajar con ellas.

El de hoy se podría decir que es un “hola mundo” en Silverlight, se trata de un reloj de agujas para el cual no nos va a hacer falta tocar demasiado código.

Lo primero de todo es abrir Expression Blend y crear un nuevo proyecto de tipo “Silverlight Application”.

 

A continuación vamos a añadir dos elipses (tenéis la herramienta en la parte izquierda) la primera con un ancho y alto de 260 y un borde o StrokeThickness de 3, y la segunda algo menor en sus dimensiones, asignar 240 tanto al alto y al ancho y al borde le asignáis valor 1.

Para facilitar el proceso una vez tenéis añadida la primera elipse podéis duplicarla en la vista de “Objects and Timeline” (copiando y pegando) y ya simplemente tendréis que cambiar los valores que mencione anteriormente en la vista de propiedades. Así conseguiréis que una quede dentro de la otra.

Ahora añadiremos los números al reloj, agregamos cuatro TextBlocks con el texto 3, 6, 9 y 12. Y los colocamos de la misma forma que veis en la imagen:

Para terminar vamos a añadir tres rectángulos que harán de agujas del reloj. Añadimos el primero que va a ser la aguja que cuente las horas con los siguientes parámetros, RadiusX y RadiusY a 4 y el borde a 1, el ancho de rectángulo va a ser 9 y el alto 69, y para terminar el valor de RenderTransformOrigin lo ponemos a 0,5 y 0.

El RenderTransformOrigin define el origen de las transformaciones, o lo que es lo mismo, el punto sobre el que vamos a realizar las rotaciones de las agujas del reloj.

La aguja del minutero tendrá los siguientes parámetros: RadiusX y RadiusY a 0,5 y el borde a 1, el ancho de rectángulo va a ser 1 y el alto 91, y para terminar el valor de RenderTransformOrigin lo ponemos a 0,5 y 0.

Los parámetros del segundero son exactamente iguales a los del minutero simplemente ponedla de color rojo para distinguirla. 

Algo muy importante en esta última parte es dar nombre a las agujas para distinguirlas y poderles asignar valores.

El código completo de la interfaz es este:

1 <Grid x:Name="LayoutRoot" Background="White">
2 <Ellipse x:Name="BordeExterior" Fill="White" Margin="194,110,186,110" Stroke="Black" StrokeThickness="3" Width="260" Height="260"/>
3 <Ellipse x:Name="BordeInterior" Margin="204,121,196,119" Stroke="Black" Width="240" Height="240"/>
4 <TextBlock Height="23" Margin="308,121,302,0" TextWrapping="Wrap" Text="12" VerticalAlignment="Top" FontSize="21.333" FontFamily="Arial Rounded MT Bold" TextAlignment="Center"/>
5 <TextBlock Height="23" Margin="308,0,302,119" TextWrapping="Wrap" Text="6" VerticalAlignment="Bottom" FontSize="21.333" FontFamily="Arial Rounded MT Bold" TextAlignment="Center"/>
6 <TextBlock Margin="0,224,196,233" TextWrapping="Wrap" Text="3" FontSize="21.333" FontFamily="Arial Rounded MT Bold" TextAlignment="Center" HorizontalAlignment="Right" Width="30"/>
7 <TextBlock Margin="207,224,0,233" TextWrapping="Wrap" Text="9" FontSize="21.333" FontFamily="Arial Rounded MT Bold" TextAlignment="Center" HorizontalAlignment="Left" Width="30"/>
8 <Rectangle x:Name="Segundos" Fill="Red" HorizontalAlignment="Right" Margin="0,238,316,151" Stroke="Red" Width="1" RenderTransformOrigin="0.5,0" RadiusY="0.5" RadiusX="0.5"/>
9 <Rectangle x:Name="Minutos" Fill="Black" HorizontalAlignment="Right" Margin="0,238,316,151" Stroke="Black" Width="1" RenderTransformOrigin="0.5,0" RadiusY="0.5" RadiusX="0.5"/>
10 <Rectangle x:Name="Horas" Fill="Black" HorizontalAlignment="Right" Margin="0,237,312,174" Width="9" RenderTransformOrigin="0.5,0" RadiusY="4" RadiusX="4"/>
11 </Grid>

Ahora vamos a añadir el código para que se muevan las agujas según pasa el tiempo, para ello podemos pulsar con el botón derecho sobre la solución y abrir desde Blend el VisualStudio.

 

Si abrimos MainPage.xaml.cs evidentemente va a estar vacío, aquí lo que vamos a hacer es crearnos un Timer que cuando “salte” actualice la rotación de las agujas.

Para ello tenemos que añadir un namespace System.Windows.Threading y declararnos un objeto global de tipo DispacherTimer. Este objeto que creamos dentro del constructor de la clase se tiene que actualizar cada segundo y cada vez que se actualice llamaremos al método correspondiente para que renueve la posición de las agujas.

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Net;
5 using System.Windows;
6 using System.Windows.Controls;
7 using System.Windows.Documents;
8 using System.Windows.Input;
9 using System.Windows.Media;
10 using System.Windows.Media.Animation;
11 using System.Windows.Shapes;
12 using System.Windows.Threading;
13
14 namespace relojDeAgujas
15 {
16 public partial class MainPage : UserControl
17 {
18 DispatcherTimer miReloj;
19
20 public MainPage()
21 {
22 InitializeComponent();
23
24 miReloj = new DispatcherTimer();
25 miReloj.Interval = TimeSpan.FromSeconds(1);
26 miReloj.Tick += new EventHandler(miReloj_Tick);
27 miReloj.Start();
28 }
29
30 void miReloj_Tick(object sender, EventArgs e)
31 {
32 RotateTransform rotateTransform = new RotateTransform();
33 rotateTransform.Angle = DateTime.Now.Second * 6 + 180;
34 Segundos.RenderTransform = rotateTransform;
35
36 rotateTransform = new RotateTransform();
37 rotateTransform.Angle = DateTime.Now.Minute * 6 + 180;
38 Minutos.RenderTransform = rotateTransform;
39
40 rotateTransform = new RotateTransform();
41 int lahora = Convert.ToInt32(DateTime.Now.ToString("hh"));
42 rotateTransform.Angle = (lahora*30) + (DateTime.Now.Minute* 0.5) + 180;
43 Horas.RenderTransform = rotateTransform;
44 }
45 }
46 }

 

Para calcular los grados que tengo que avanzar cada aguja del reloj hay que tener en cuenta lo siguiente: el reloj analógico se divide en 12 partes y cada una de ellas en 5, por lo que durante una hora la aguja que la marca las horas de mueve 30 grados.

  • El recorrido de 30 grados no lo hace de golpe sino que cada minuto avanza un poco, exactamente 0,5 grados (30 / 60 = 0,5).

  • El minutero da la vuelta entera al reloj por lo que recorre 360 grados en una hora o lo que es lo mismo 6 grados cada minuto.

  • Por último tenemos al segundero que al igual que el minutero se mueve de 6 en 6 grados pero al segundo.

Si repasamos el código que mueve el minutero y segundero lo que vemos es que hago uso de un objeto RotateTransform que me ayuda a rotar cada objeto de la pantalla. El ángulo lo obtengo multiplicando el minuto o segundo de ese instante por 6 y le sumo 180, esto último es para corregir el ángulo resultante y que aparezca en el lugar adecuado. Por ultimo aplico la transformación al objeto.

 

1 RotateTransform rotateTransform = new RotateTransform();
2 rotateTransform.Angle = DateTime.Now.Second * 6 + 180;
3 Segundos.RenderTransform = rotateTransform;
4
5 rotateTransform = new RotateTransform();
6 rotateTransform.Angle = DateTime.Now.Minute * 6 + 180;
7 Minutos.RenderTransform = rotateTransform;

De la misma manera realizo el cálculo para la aguja de las Horas solo que debo tener en cuenta que el sistema me da las horas en formato 24H y yo necesito que me devuelva un valor que no pase del 12, para ello formateo la salida de DateTime.Now y la convierto a entero.

1 rotateTransform = new RotateTransform();
2 int lahora = Convert.ToInt32(DateTime.Now.ToString("hh"));
3 rotateTransform.Angle = (lahora*30) + (DateTime.Now.Minute* 0.5) + 180;
4 Horas.RenderTransform = rotateTransform;

Y con estas pocas líneas tenemos nuestro primer programa en Silverlight 😉 a continuación tenéis el código disponible para descargar.