This is the second in a small series I am writing on programmatic drawing inside Silverlight.  The first post I did on this was an analogue clock and it can be found here:

http://www.andrewrea.co.uk/2009/08/12/ProgrammaticDrawingWithSilverlight2030AnAnalogueClock.aspx

The code for this can be downloaded from the following link: http://lab.andrewrea.co.uk/DigitalClock.rar

In this post I will create a digital clock, with the disjointed digits you see in those stereotypical red digit alarm clocks.  The way I tackled this was look at the individual component parts and their grouping to ultimately give the representation of time or any other numerical value.  So each digit of the display is made up of individual bars with angle ends.  One of the key things with the angle ends, is that they are at a 45 degree angle, so that when brought together they line up with one another.

Image

image

Working In Silverlight

The parts of this inline with the code files are as follows:

  • IDigitalDigitPiece
  • IDigitalDigit
  • IDigitalState
  • IDigitalClockView

The second one in the list is the Digit State.  From the above examples you can see that each digit is made up of 7 component pieces, and each one represents their assigned digit by using different combinations of these digits.  In this case I have assigned two properties to each Digit being the OnColor and the OffColor.

The IDigitState has the following signature:

    public interface IDigitState
    {
        void Handle(IDigitalDigit digit);
    }

An example of one of the states is the following.  They simply assign the the OffColor or the OnColor to the Color property of the DigitalDigitPiece.

    public class FourState : IDigitState
    {

        #region IDigitState Members

        public void Handle(IDigitalDigit digit)
        {
            digit.DigitalDigitPieces[0].DigitColor = digit.OffColor;
            digit.DigitalDigitPieces[1].DigitColor = digit.OnColor;
            digit.DigitalDigitPieces[2].DigitColor = digit.OnColor;
            digit.DigitalDigitPieces[3].DigitColor = digit.OnColor;
            digit.DigitalDigitPieces[4].DigitColor = digit.OffColor;
            digit.DigitalDigitPieces[5].DigitColor = digit.OnColor;
            digit.DigitalDigitPieces[6].DigitColor = digit.OffColor;
        }

        #endregion
    }

I have pasted both the code for the digit and the digit piece below.  In hind sight I think it would have been better to use examples which display the binding.  The digit code displays how I am positioning the pieces after any resize or load events and the digit piece displays the code which shows how I am creating the shape of the pieces which make up the digit.

public partial class DigitalDigit : UserControl, IDigitalDigit
    {
        public List<DigitalDigitPiece> DigitalDigitPieces { get; protected set; }

        private double _sepDiameter;

        public double SepDiameter
        {
            get { return _sepDiameter; }
            set
            {
                if (value != _sepDiameter)
                {
                    _sepDiameter = value;
                    Draw();
                }
            }
        }

        private Color _sepColor;

        public Color SepColor
        {
            get { return _sepColor; }
            set
            {
                if (value != _sepColor)
                {
                    _sepColor = value;
                    Draw();
                }
            }
        }

        private Color _onColor = Colors.Red;

        public Color OnColor
        {
            get { return _onColor; }
            set { _onColor = value; }
        }

        private Color _offColor = Colors.Black;

        public Color OffColor
        {
            get { return _offColor; }
            set { _offColor = value; }
        }

        private double _pointSize = 10;

        public double PointSize
        {
            get { return _pointSize; }
            set
            {
                if (value != _pointSize)
                {
                    _pointSize = value;
                    Draw();
                }
            }
        }

        private Color _digitColor = Colors.Red;

        public Color DigitColor
        {
            get { return _digitColor; }
            set
            {
                if (value != _digitColor)
                {
                    _digitColor = value;
                    Draw();
                }
            }
        }

        public DigitalDigit()
        {
            InitializeComponent();
            CreateDigits();
            SizeChanged += new SizeChangedEventHandler(DigitalDigit_SizeChanged);
            Loaded += new RoutedEventHandler(DigitalDigit_Loaded);
        }

        void DigitalDigit_Loaded(object sender, RoutedEventArgs e)
        {
            Draw();
        }

        void DigitalDigit_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            Draw();
        }

        protected void Draw()
        {
            foreach (var digit in DigitalDigitPieces)
            {
                digit.PointSize = PointSize;
            }

            AlignDigits();
        }

        protected double LongHeight
        {
            get
            {
                return (ActualHeight / 2) - _pointSize;
            }
        }

        protected void AlignDigits()
        {
            if (ActualWidth > 0 && ActualHeight > 0)
            {
                DigitalDigitPieces[0].Orientation = DigitalDigitPieceOrientation.Horizontal;
                DigitalDigitPieces[0].Height = ActualWidth - (DigitalDigitPieces[0].PointSize * 2);
                DigitalDigitPieces[0].SetValue(Canvas.LeftProperty, DigitalDigitPieces[0].PointSize);
                DigitalDigitPieces[0].PointSize = PointSize;

                DigitalDigitPieces[1].SetValue(Canvas.LeftProperty, 0D);
                DigitalDigitPieces[1].Height = LongHeight;
                DigitalDigitPieces[1].SetValue(Canvas.TopProperty, (double)DigitalDigitPieces[0].Width / 2);
                DigitalDigitPieces[1].PointSize = PointSize;

                DigitalDigitPieces[2].SetValue(Canvas.LeftProperty, (double)ActualWidth - DigitalDigitPieces[2].Width);
                DigitalDigitPieces[2].Height = LongHeight;
                DigitalDigitPieces[2].SetValue(Canvas.TopProperty, (double)DigitalDigitPieces[0].Width / 2);
                DigitalDigitPieces[2].PointSize = PointSize;

                DigitalDigitPieces[3].Orientation = DigitalDigitPieceOrientation.Horizontal;
                DigitalDigitPieces[3].Height = ActualWidth - (DigitalDigitPieces[3].PointSize * 2);
                DigitalDigitPieces[3].SetValue(Canvas.LeftProperty, DigitalDigitPieces[3].PointSize);
                DigitalDigitPieces[3].SetValue(Canvas.TopProperty, (((double)DigitalDigitPieces[2].GetValue(Canvas.TopProperty)) + (double)(DigitalDigitPieces[2].Height - DigitalDigitPieces[2].PointSize)));
                DigitalDigitPieces[3].PointSize = PointSize;

                DigitalDigitPieces[4].SetValue(Canvas.LeftProperty, 0D);
                DigitalDigitPieces[4].Height = LongHeight;
                DigitalDigitPieces[4].SetValue(Canvas.TopProperty, (((double)DigitalDigitPieces[3].GetValue(Canvas.TopProperty)) + (double)(DigitalDigitPieces[3].Width / 2)));
                DigitalDigitPieces[4].PointSize = PointSize;

                DigitalDigitPieces[5].SetValue(Canvas.LeftProperty, (double)ActualWidth - DigitalDigitPieces[4].Width);
                DigitalDigitPieces[5].Height = LongHeight;
                DigitalDigitPieces[5].SetValue(Canvas.TopProperty, (((double)DigitalDigitPieces[3].GetValue(Canvas.TopProperty)) + (double)(DigitalDigitPieces[3].Width / 2)));
                DigitalDigitPieces[5].PointSize = PointSize;

                DigitalDigitPieces[6].Orientation = DigitalDigitPieceOrientation.Horizontal;
                DigitalDigitPieces[6].Height = ActualWidth - (DigitalDigitPieces[6].PointSize * 2);
                DigitalDigitPieces[6].SetValue(Canvas.LeftProperty, DigitalDigitPieces[3].PointSize);
                DigitalDigitPieces[6].SetValue(Canvas.TopProperty, (((double)DigitalDigitPieces[5].GetValue(Canvas.TopProperty)) + (double)(DigitalDigitPieces[5].Height - DigitalDigitPieces[5].PointSize)));
                DigitalDigitPieces[6].PointSize = PointSize;
            }
        }

        protected void CreateDigits()
        {
            DigitalDigitPieces = new List<DigitalDigitPiece>();
            for (var i = 0; i < 7; i++)
            {
                var piece = new DigitalDigitPiece();
                DigitalDigitPieces.Add(piece);
                LayoutRoot.Children.Add(DigitalDigitPieces[i]);
            }
        }

        #region IDigitalDigit Members

        public void SetState(IDigitState state)
        {
            state.Handle(this);
        }

        #endregion

        public void Update()
        {
            Draw();
        }
    }

 public partial class DigitalDigitPiece : UserControl
    {
        private double _angle = 0D;

        private double _pointSize = 10;

        public double PointSize
        {
            get { return _pointSize; }
            set
            {
                if (value != _pointSize)
                {
                    _pointSize = value;
                    Draw();
                }
            }
        }

        private Color _digitColor;

        public Color DigitColor
        {
            get { return _digitColor; }
            set
            {
                if (value != _digitColor)
                {
                    _digitColor = value;
                    Draw();
                }
            }
        }

        private DigitalDigitPieceOrientation _orientation;

        public DigitalDigitPieceOrientation Orientation
        {
            get { return _orientation; }
            set
            {
                if (value != _orientation)
                {
                    _orientation = value;
                    Draw();
                }
            }
        }

        private Polygon _polygon;

        public Polygon Polygon
        {
            get { return _polygon; }
            set { _polygon = value; }
        }

        public DigitalDigitPiece()
        {
            _digitColor = Colors.Red;
            _orientation = DigitalDigitPieceOrientation.Vertical;
            _polygon = new Polygon();

            InitializeComponent();
            SizeChanged += new SizeChangedEventHandler(DigitalDigitPiece_SizeChanged);
            Loaded += new RoutedEventHandler(DigitalDigitPiece_Loaded);

            LayoutRoot.Children.Add(_polygon);
        }

        void DigitalDigitPiece_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            Draw();
        }

        void DigitalDigitPiece_Loaded(object sender, RoutedEventArgs e)
        {
            Draw();
        }

        protected void Draw()
        {
            Width = _pointSize * 2;

            _polygon.Points = new PointCollection
            {
                 new Point(Width / 2,0),
                 new Point(Width, _pointSize),
                 new Point(Width, Height - _pointSize),
                 new Point(Width/2,Height),
                 new Point(0,Height-_pointSize),
                 new Point(0, _pointSize),
                 new Point(Width/2,0)
            };
            _polygon.Fill = new SolidColorBrush(DigitColor);
            _polygon.Stroke = new SolidColorBrush(Colors.Black);
            _polygon.StrokeThickness = 1D;

            var rotate = new RotateTransform();
            rotate.CenterX = _pointSize;
            rotate.CenterY = _pointSize;
            if (_orientation == DigitalDigitPieceOrientation.Horizontal)
            {
                if (_angle == 0 || _angle == 90)
                    _angle = -90;
            }
            else
            {
                if (_angle == -90)
                    _angle = 90;
                else
                    _angle = 0;
            }

            rotate.Angle = _angle;
            _polygon.RenderTransform = rotate;

        }
    }

The actual view is simply a user control which contains instances of the digits inside and arranges them accordingly.  The class which I have not mentioned is the separator class and that is simply two ellipses which I have added control for, both for size and the colour inside the containers.

<UserControl x:Class="DigitalClock.DigitalClockView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:custom="clr-namespace:DigitalClock">
    <Grid x:Name="LayoutRoot" Background="Black">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
        </Grid.RowDefinitions>
        <custom:DigitalDigit x:Name="Hour1" Grid.Column="0" PointSize="20" OnColor="Beige" />
        <custom:DigitalDigit x:Name="Hour2" Grid.Column="1" PointSize="20" OnColor="Beige"/>
        <custom:DigitalClockSeparator x:Name="sep1" Grid.Column="2" HorizontalAlignment="Center" />
        <custom:DigitalDigit x:Name="Minute1" Grid.Column="3" PointSize="20" />
        <custom:DigitalDigit x:Name="Minute2" Grid.Column="4" PointSize="20" />
        <custom:DigitalClockSeparator x:Name="sep2" Grid.Column="5" HorizontalAlignment="Center"/>
        <custom:DigitalDigit x:Name="Second1" Grid.Column="6" PointSize="20" />
        <custom:DigitalDigit x:Name="Second2" Grid.Column="7" PointSize="20" />
    </Grid>
</UserControl>

I found a couple of gotchas and a big one includes that upon an exception, SilverLight will continue to try and render what it has even after an exception and it looks likes values default to zero, which admittedly seems logical.  Take a peek at this screen print from the above app during development.

image 

Tis all for now,

Cheers,


Andrew



.NET | C# | Silverlight
Sunday, November 22, 2009 11:22:03 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer

Download the source project

Image

image

Working in Silverlight

 

 

I have seen a lot of comparisons made between ActionScript and Silverlight with specific focus on the difference in lines of code, and more specifically when referring to the drawing api.  What I thought I would do is do a little drawing and in this example I have developed a teeny tiny interface for a clock view, which has a method of simply SetTime(DateTime time)  and I have made one implementation of this Clock view which is an analogue clock.  I have used some simple ellipse equations to allow for dynamic resize and redraw and in hind sight I would refactor my code so as not to keep adding and removing the shapes which are UIElements.  I will make some more clocks with this refactoring present.  It does feel like a gauge control is on the way also, there are so many gauge controls out there including the, cool, free ones from Microsoft, well they are charting controls but never the less and absolutely amazing freebie.

So the whole purpose of this post is about programmatic drawing as opposed to using the XAML equivalent, which I might say would be worth looking at to replicate this example! 

The Code

So to the code, first I have defined a short interface as follows:

namespace SilverlightClock
{
    public interface IClockView
    {
        void setTime(DateTime time);
    }
}

Next is the xaml mark-up, and the only thing I have amended is the Root UIElement which I have used a Canvas as opposed to the default Grid.  This lets me set things out using the Canvas.LeftProperty and Canvas.TopProperty.

<UserControl x:Class="SilverlightClock.AnalogueClock"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="300">
    <Canvas x:Name="LayoutRoot" Background="White">

    </Canvas>
</UserControl>

Next is the mark-up and class diagram for the actual UserControl - AnalogueClock.

image  

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace SilverlightClock
{
    public partial class AnalogueClock : UserControl, IClockView
    {
        private DateTime _time = new DateTime(2000, 1, 1, 18, 15, 50);
        private double hour;
        private double minute;
        private double second;

        public AnalogueClock()
        {
            InitializeComponent();
            SizeChanged += new SizeChangedEventHandler(AnalogueClock_SizeChanged);
            Loaded += new RoutedEventHandler(AnalogueClock_Loaded);
        }

        void AnalogueClock_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            Draw();
        }

        void AnalogueClock_Loaded(object sender, RoutedEventArgs e)
        {
            Draw();
        }

        void Draw()
        {
            LayoutRoot.Children.Clear();
            var step = 360 / 60;
            var innerRadiusX = (Width * 0.7) / 2;
            var innerRadiusY = (Height * 0.7) / 2;
            var outerRadiusX = (Width * 0.8) / 2;
            var outerRadiusY = (Height * 0.8) / 2;
            var textRadiusX = (Width * 0.9) / 2;
            var textRadiusY = (Height * 0.9) / 2;
            var outerCasing = new Ellipse();
            outerCasing.Stroke = new SolidColorBrush(Colors.Black);
            outerCasing.Width = (Width * 0.8);
            outerCasing.Height = (Height * 0.8);
            outerCasing.SetValue(Canvas.LeftProperty, Width * 0.1);
            outerCasing.SetValue(Canvas.TopProperty, Height * 0.1);
            LayoutRoot.Children.Add(outerCasing);

            for (var i = 0; i < 60; i++)
            {
                var line = new Line
                {
                    Stroke = new SolidColorBrush(Colors.Black),
                    X1 = (Width / 2) + Math.Sin((step * i) * (Math.PI / 180)) * innerRadiusX,
                    Y1 = (Height / 2) + Math.Cos((step * i) * (Math.PI / 180)) * innerRadiusY,
                    X2 = (Width / 2) + Math.Sin((step * i) * (Math.PI / 180)) * outerRadiusX,
                    Y2 = (Height / 2) + Math.Cos((step * i) * (Math.PI / 180)) * outerRadiusY
                };


                if (i % 5 == 0)
                {
                    line.X1 = (Width / 2) + Math.Sin((step * i) * (Math.PI / 180)) * ((Width * 0.6) / 2);
                    line.Y1 = (Height / 2) + Math.Cos((step * i) * (Math.PI / 180)) * ((Height * 0.6) / 2);

                    var textblock = new TextBlock();
                    textblock.Text = i == 0 ? "12" : ((double)(i / 60D) * 12D).ToString();

                    var textX = (Width / 2) + Math.Sin(-((step * i + 180) % 360) * (Math.PI / 180)) * textRadiusX;
                    var textY = (Height / 2) + Math.Cos(-((step * i + 180) % 360) * (Math.PI / 180)) * textRadiusY;

                    textblock.SetValue(Canvas.LeftProperty, textX - textblock.ActualWidth / 2);
                    textblock.SetValue(Canvas.TopProperty, textY - textblock.ActualHeight / 2);

                    LayoutRoot.Children.Add(textblock);
                }

                LayoutRoot.Children.Add(line);
            }


            DrawHourHand();
            DrawMinuteHand();
            DrawSecondHand();
            DrawMilliSeconds();
            DrawLogo();
        }

        private void DrawLogo()
        {
            var textBlockLogo = new TextBlock();
            textBlockLogo.Text = "andrewrea.co.uk";
            textBlockLogo.FontFamily = new FontFamily("Arial");
            textBlockLogo.FontSize = 9D;
            textBlockLogo.SetValue(Canvas.LeftProperty, (Width - textBlockLogo.ActualWidth) / 2);
            textBlockLogo.SetValue(Canvas.TopProperty, (Height - textBlockLogo.ActualHeight) / 3);
            LayoutRoot.Children.Add(textBlockLogo);
        }

        private void DrawHourHand()
        {
            //Change hour value to percentage for use with 360
            double hourPercentage = (hour + (minute / 60D)) / 12D;

            //Get the Hour degree value
            double hourDegree = 360 * hourPercentage;

            DrawHand(Width / 5.5D, Height / 5.5D, -hourDegree, Colors.Black, 3);
        }

        private void DrawMinuteHand()
        {
            //Change minute value to percentage for use with 360
            double minutePercentage = (minute + (second / 60D)) / 60;
            //Get the minute percentage
            double minuteDegree = 360 * minutePercentage;

            DrawHand(Width / 4.5D, Height / 4.5D, -minuteDegree, Colors.Blue, 2);
        }

        private void DrawSecondHand()
        {
            double secondPercentage = second / 60D;
            //Get the minute percentage
            double secondDegree = 360 * secondPercentage;

            DrawHand(Width / 3.5D, Height / 3.5D, -secondDegree, Colors.Red, 1);
        }

        private void DrawMilliSeconds()
        {
            //Figure out the second hand
            double millisecond = _time.Millisecond;
            //Change minute value to percentage for use with 360
            double millisecondPercentage = millisecond / 1000;
            //Get the minute percentage
            double millisecondDegree = 360 * millisecondPercentage;

            var milliContainer = new Ellipse();
            milliContainer.Width = (Width * 0.1D) + 1;
            milliContainer.Height = (Height * 0.1D) + 1;
            milliContainer.Stroke = new SolidColorBrush(Colors.Black);
            milliContainer.SetValue(Canvas.LeftProperty, (Width * 0.65) - (Width * 0.05D));
            milliContainer.SetValue(Canvas.TopProperty, (Height * 0.65) - (Height * 0.05D));

            var hand = new Line
            {
                Stroke = new SolidColorBrush(Colors.Green),
                X1 = Width * 0.65,
                Y1 = Height * 0.65,
                X2 = (Width * 0.65) + -Math.Sin(-millisecondDegree * (Math.PI / 180)) * (Width * 0.05D),
                Y2 = (Height * 0.65) + -Math.Cos(-millisecondDegree * (Math.PI / 180)) * (Height * 0.05D)
            };

            LayoutRoot.Children.Add(milliContainer);
            LayoutRoot.Children.Add(hand);
        }

        private void DrawHand(double radiusX, double radiusY, double angle, Color color, double thickness)
        {
            var hand = new Line
            {
                Stroke = new SolidColorBrush(color),
                X1 = Width / 2,
                Y1 = Height / 2,
                X2 = (Width / 2) + -Math.Sin(angle * (Math.PI / 180)) * radiusX,
                Y2 = (Height / 2) + -Math.Cos(angle * (Math.PI / 180)) * radiusY
            };
            hand.StrokeThickness = thickness;

            LayoutRoot.Children.Add(hand);
        }

        #region IClockView Members

        public void SetTime(DateTime time)
        {
            _time = time;
            hour = _time.Hour;
            minute = _time.Minute;
            second = _time.Second;
            
            Draw();
        }

        #endregion
    }
}

Next is the xaml mark-up for the actual page.xaml .  Really I could have created a Presenter for this, but I haven’t.  The setup is primed for one since I am declaring an interface for an actual clock view, and like many examples in many other programming languages, a good second view for this interface would be a DigitalClock user control. I think that it would be a nice second example to use to have a deeper look into how we can skin it up to such an extent for it to resemble to classic 80’s style red digit alarm clock.

So the xaml mark-up.  This is simply the grid layout, labels and instances of the user control.

<UserControl x:Class="SilverlightClock.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
             xmlns:x2="clr-namespace:SilverlightClock"
    Width="600" Height="460">
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition />
            <RowDefinition Height="30" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <TextBlock Text="London" Grid.Column="0" Grid.Row="0" 
                   HorizontalAlignment="Center" 
                   VerticalAlignment="Center" FontWeight="ExtraBlack"></TextBlock>
        <x2:AnalogueClock Height="200" Width="200" x:Name="ClockLondon" 
                          HorizontalAlignment="Stretch" 
                          VerticalAlignment="Stretch"
                          Grid.Column="0" Grid.Row="1"></x2:AnalogueClock>
        <TextBlock Text="New York" Grid.Column="1" Grid.Row="0" 
                   HorizontalAlignment="Center" 
                   VerticalAlignment="Center" FontWeight="ExtraBlack"></TextBlock>
        <x2:AnalogueClock Height="200" Width="200" x:Name="ClockNewyork" 
                          HorizontalAlignment="Stretch" 
                          VerticalAlignment="Stretch"
                          Grid.Column="1" Grid.Row="1"></x2:AnalogueClock>
        <TextBlock Text="Sydney" Grid.Column="2" Grid.Row="0" 
                   HorizontalAlignment="Center" 
                   VerticalAlignment="Center" FontWeight="ExtraBlack"></TextBlock>
        <x2:AnalogueClock Height="200" Width="200" x:Name="ClockSydney" 
                          HorizontalAlignment="Stretch" 
                          VerticalAlignment="Stretch"
                          Grid.Column="2" Grid.Row="1"></x2:AnalogueClock>
        <TextBlock Text="Paris" Grid.ColumnSpan="3" Grid.Row="2" 
                   HorizontalAlignment="Center" 
                   VerticalAlignment="Center" FontWeight="ExtraBlack"></TextBlock>
        <x2:AnalogueClock Height="200" Width="600" x:Name="ClockParis" 
                          HorizontalAlignment="Stretch" 
                          VerticalAlignment="Stretch"
                          Grid.ColumnSpan="3" Grid.Row="3"></x2:AnalogueClock>
    </Grid>
</UserControl>

The last part is the code behind for this page.xaml, and it is something which I think I should have probably used a storyboard for, but I am not too sure. Either way I have used a timer and it is a bit too processor intensive, I think I would want to use the Silverlight equivalent of the flash Event.ENTER_FRAME and I say equivalent because Silverlight does not use frames ;-)

namespace SilverlightClock
{
    public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();

            Loaded += new RoutedEventHandler(Page_Loaded);
        }

        void dt_Tick(object sender, EventArgs e)
        {
            ClockLondon.SetTime(DateTime.Now);
            ClockNewyork.SetTime(DateTime.Now.AddHours(-5));
            ClockSydney.SetTime(DateTime.Now.AddHours(10));
            ClockParis.SetTime(DateTime.Now.AddHours(2));
        }

        void Page_Loaded(object sender, RoutedEventArgs e)
        {
            System.Windows.Threading.DispatcherTimer dt = new System.Windows.Threading.DispatcherTimer();
            dt.Interval = new TimeSpan(0, 0, 0, 0, 100); // 500 Milliseconds
            dt.Tick += new EventHandler(dt_Tick);
            dt.Start();
        }
    }
}

I am setting the time zones using the DateTime methods and the rest is simply using the interface to set the time of the view. 

I hope this is of some use and hopefully of interest to you.

Cheers for now,

Andrew



.NET | C# | Silverlight
Wednesday, August 12, 2009 9:54:27 PM (GMT Daylight Time, UTC+01:00)  #    Disclaimer