Silverlight Stopwatch class in C#

I needed a simple method for doing some timings in Silverlight. Silverlight lacks the high performance query counter that is available natively in Windows (and available in .NET 3.5 for example), but it does have a TickCount. My code (mostly) mirrors the existing .NET Stopwatch class using the TickCount property of the Environment class.

Just create an instance of the class and call Start to start the timer, and then get the value of one of the  ElapsedNNN properties to get a live reading without stopping the timer or the Stop function to stop the timer completely.

Update April 4, 2010:

Even though there were some doubters in the comments suggesting I didn’t test the original version that was here – I had … so I’m confused as to why it had worked for me, yet not in current versions of Silverlight. In any case, I’ve updated the code and have included a sample of how to use it. Thanks to those who provided suggested fixes.

using System;
using System.Diagnostics;

namespace WiredPrairie.Silverlight
{
    /// <summary>
    /// Stopwatch is used to measure the general performance of Silverlight functionality. Silverlight
    /// does not currently provide a high resolution timer as is available in many operating systems,
    /// so the resolution of this timer is limited to milliseconds. This class is best used to measure
    /// the relative performance of functions over many iterations.
    /// </summary>
    public sealed class Stopwatch
    {
        private long _startTick;
        private long _elapsed;
        private bool _isRunning;

        /// <summary>
        /// Creates a new instance of the class and starts the watch immediately.
        /// </summary>
        /// <returns>An instance of Stopwatch, running.</returns>
        public static Stopwatch StartNew()
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            return sw;
        }

        /// <summary>
        /// Creates an instance of the Stopwatch class.
        /// </summary>
        public Stopwatch() { }

        /// <summary>
        /// Completely resets and deactivates the timer.
        /// </summary>
        public void Reset()
        {
            _elapsed = 0;
            _isRunning = false;
            _startTick = 0;
        }

        /// <summary>
        /// Begins the timer.
        /// </summary>
        public void Start()
        {
            if (!_isRunning)
            {
                _startTick = GetCurrentTicks();
                _isRunning = true;
            }
        }

        /// <summary>
        /// Stops the current timer.
        /// </summary>
        public void Stop()
        {
            if (_isRunning)
            {
                _elapsed += GetCurrentTicks() - _startTick;
                _isRunning = false;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the instance is currently recording.
        /// </summary>
        public bool IsRunning
        {
            get { return _isRunning; }
        }

        /// <summary>
        /// Gets the Elapsed time as a Timespan.
        /// </summary>
        public TimeSpan Elapsed
        {
            get { return TimeSpan.FromMilliseconds(ElapsedMilliseconds); }
        }

        /// <summary>
        /// Gets the Elapsed time as the total number of milliseconds.
        /// </summary>
        public long ElapsedMilliseconds
        {
            get { return GetCurrentElapsedTicks() / TimeSpan.TicksPerMillisecond; }
        }

        /// <summary>
        /// Gets the Elapsed time as the total number of ticks (which is faked
        /// as Silverlight doesn't have a way to get at the actual "Ticks")
        /// </summary>
        public long ElapsedTicks
        {
            get { return GetCurrentElapsedTicks(); }
        }

        private long GetCurrentElapsedTicks()
        {
            return (long) (this._elapsed + (IsRunning ? (GetCurrentTicks() - _startTick) : 0));
        }

        private long GetCurrentTicks()
        {
            // TickCount: Gets the number of milliseconds elapsed since the system started.
            return Environment.TickCount * TimeSpan.TicksPerMillisecond;
        }
    }
}


Sample usage:

Stopwatch sw1 = Stopwatch.StartNew();
Thread.Sleep(20);
sw1.Stop();
Debug.WriteLine("Slept for 5 milliseconds ... actually: {0}", sw1.ElapsedMilliseconds);

Silverlight: Dynamically creating XAML elements at runtime

Given the following XML file:

<assets>
  <asset name="tile">
    <Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
      <Path x:Name="top" Width="24" Height="12" Stretch="Fill" Fill="#FF065F02" 
            Stroke="#10000000" Data="F1 M 0,6L 12,12L 24,6L 12,0L 0,6 Z "/>
    </Canvas>
  </asset>
</assets>

It’s easy enough to create reusable assets in Silverlight and load them on demand from XML, and refer to the various pieces that make up the asset by name.

First, add an XML file to your Silverlight project. Set the Build Action to “Content” and the Copy to Output Directory to “Do not copy”. In the example below, it’s “r.xml”.

image

Next, add the following methods to a class (probably your UserControl):

private Canvas CreateXamlAsset(string assetName)
{
    return XamlReader.Load(LoadAsset(assetName)) as Canvas;
}

private string LoadAsset(string assetName)
{
    var e = from a in XDocument.Load("r.xml").Descendants("assets")
            where (string) a.Attribute("name") == assetName
            select a.FirstNode;
    return e.First().ToString();
}

The code uses LINQ to extract the asset and create it using the XamlReader.Load method. It assumes that the type being returned is a Canvas (it needs to be a container type and for these types of situations I normally use Canvas).

Once the canvas is created, you can call methods such as this:

private Shape FindShape(Canvas c, string name)
{
    return c.FindName(name) as Shape;
}

to locate the named elements within the dynamically created Xaml. For example, given the XAML at the top of this post, I could call the FindShape method like this:

Shape top = FindShape(canvas, "top");

That would return the Path named “top" in the XAML.

Make sure that you actually add the asset being returned from CreateXamlAsset to a parent control at some point! :) 

this.Children.Add(canvas);

Silverlight Stars/Sparkles

I was in a “star” mood this afternoon and created this Silverlight 2.0 demonstration.

image

For rendering it uses the CompositionTarget.Rendering method (the easiest way to control dynamic animations such as this). It also uses the VisualStateManager in a variety of places to control the user interface. I’ve become a big fan of the VSM – it’s easier to use than what WPF has as of 3.5 SP1 (I know it’s coming to WPF as well).

If you click into the UI to give it focus, you can press the [F] key to go full screen.

Switching to full screen is simple:

SilverlightHost host = Application.Current.Host;
if(host != null)
{
    Content content = host.Content;
    content.IsFullScreen = true;
}

I’ve also used my ToColorFromHex function liberally (I used Kuler to make some colors and grabbed their hex values and directly pasted into my source code).

To create the star trails (as I’ve called them), I needed to do a little bit of trigonometry:

double radians = Math.Atan2(-star.Vector2D.Y, -star.Vector2D.X);
(star.StarTrailUI.RenderTransform as RotateTransform).Angle = radians * 180 / Math.PI;

I used a RenderTransform in place of building a line with the proper X & Y coordinates as it made dealing with the LinearGradientBrush on the trail a TON easier (for me at least).

Demonstration

I’ve provided all of the source code – maybe you’ll find it useful (if so, I’d like to hear about it!).

Source code

Update: Fixed the links. Sorry!

ColorConverter for Silverlight

Silverlight 2.0 is missing a ColorConverter. There’s no easy and efficient way from code to convert from a HEX color format to a built in Color type. I like being able to paste standard web color formats into my source code without converting them to a Color.FromArgb byte format (as most web color selectors support that format).

I wrote this code to end this annoyance (but didn’t bother with a full-fledged TypeConverter):

public static class Extensions
{
    public static void SetFromHex ( this Color c, string hex )
    {
        Color c1 = ToColorFromHex(hex);

        c.A = c1.A;
        c.R = c1.R;
        c.G = c1.G;
        c.B = c1.B;                        
    }
public static Color ToColorFromHex ( string hex )
{
    if(string.IsNullOrEmpty(hex))
    {
        throw new ArgumentNullException("hex");
    }

    // remove any "#" characters
    while(hex.StartsWith("#"))
    {
        hex = hex.Substring(1);
    }

    int num = 0;
    // get the number out of the string 
    if(!Int32.TryParse(hex, System.Globalization.NumberStyles.HexNumber, null, out num))
    {
        throw new ArgumentException("Color not in a recognized Hex format.");
    }

    int[] pieces = new int[4];
    if(hex.Length > 7)
    {
        pieces[0] = ((num >> 24) & 0x000000ff);
        pieces[1] = ((num >> 16) & 0x000000ff);
        pieces[2] = ((num >> 8) & 0x000000ff);
        pieces[3] = (num & 0x000000ff);
    }
    else if(hex.Length > 5)
    {
        pieces[0] = 255;
        pieces[1] = ((num >> 16) & 0x000000ff);
        pieces[2] = ((num >> 8) & 0x000000ff);
        pieces[3] = (num & 0x000000ff);
    }
    else if(hex.Length == 3)
    {
        pieces[0] = 255;
        pieces[1] = ((num >> 8) & 0x0000000f);
        pieces[1] += pieces[1] * 16;
        pieces[2] = ((num >> 4) & 0x000000f);
        pieces[2] += pieces[2] * 16;
        pieces[3] = (num & 0x000000f);
        pieces[3] += pieces[3] * 16;
    }
    return Color.FromArgb((byte) pieces[0], (byte) pieces[1], (byte) pieces[2], (byte) pieces[3]);
}

}

 

It accepts 3 standard formats for colors:

  1. [ALPHA][RED][GREEN][BLUE] (alpha and all colors)
  2. [RED][GREEN][BLUE] (no alpha channel, automatically set to 255)
  3. Short hand [R][G][B] (takes the single digit hex number and adds it to the same number * 16, alpha set to 255);

To use the function:

Color c1 = Extensions.ToColorFromHex("#00B2D6E8");

Obviously the built in components, brushes, etc. support that format already, but there doesn’t seem to be any place that functionality is exposed.