Creating a Simple Silverlight Countdown Blog badge

image I’m going to be speaking at RIAPalooza in about two weeks, well more specifically at the time of this writing it’s 10 days, 12 hours, 48 minutes and 45 seconds. :)

I’m going to be tag teaming with Mike Labriola again talking about “10 questions about RIA you haven’t had the courage to ask”. We’ve got a short list of questions but are hoping to get some questions from the crowd as well. If you happen to have any questions, feel free to shoot them to me in the comments section on this post.

Anyways, we were discussing different ways to get the word out about RIAPalooza and someone mentioned that we didn’t have have a blog badge so I decided to create one.

I started by grabbing some art from the RIAPalooza web site. I started out in Expression Blend with a simple Silverlight project. Here are the steps that I took.

  1. Resize the Page.xaml user control to 100×200 to fit the blog badge size.
  2. Add the wood background that I grabbed from the RIAPalooza web site trimmed down to the blog badge size.
  3. Paste in the two other images, one for the logo (referred to as i and one for a a free t-shirt offer.
  4. Animate the two images to wiggle, spin, wiggle, wait, wiggle, spin, wiggle, wait, reverse.
    •  image
      To do this, I opened a timeline and started by rotating the first image -6 degrees and then a frame later rotating it to 6 degrees and then a frame later back to 0. This gives the images a nice little wiggle and catches the viewer’s eye without being too annoying. Well, I don’t think it’s too annoying but you can tell me…
    • image
      Then I kicked forward 2 seconds and did the wiggle again in preparation of swapping to two images. The wiggle makes a nice little transition before the action starts.
    • image
      At the end of the wiggle, I spun the image on it’s X axis over the span of half a second. That’s accomplished by setting the X Skew to zero.
    • On the same frame that the first logo hits an X Skew of zero, I make the other image visible with it’s X Skew to zero an proceed to transform it’s skew to 1 (normal) over the course of a half second.
    • Then I wiggled the t-shirt offer image in the same manner that I did the first logo.
    • Now, back in the code, I set a few properties and started the animation.
      AnimateLogos.AutoReverse = true;
      AnimateLogos.RepeatBehavior = new RepeatBehavior(1000);
      AnimateLogos.Begin();
  5. Now that the logos are moving, I needed a link to the site. Simple enough using a HyperLinkButton.
  6. Last thing I needed was to provide some type of count down. At first I just used a couple of labels and set the text.
    • First, I used a timer set to go every second and set the time
      _timer = newTimer(newTimerCallback(Timer_Tick), null, 0, 1000);
    • Next I used the dispatch object to work on the UI thread as follows:
      public void Timer_Tick(object state)
      {
          Dispatcher.BeginInvoke(() =>
            {
                try
                {
                    DateTime launchDate = new DateTime(2009, 5, 8, 8, 0, 0).ToUniversalTime();
                    TimeSpan span = launchDate.Subtract(DateTime.Now.ToUniversalTime());
      
                    txtDays.Text = string.Format("{0} Days", span.Days);
                    ...
                }
                catch
                {
                    //Eat all errors. We'll get another chance in a second...
                }
            }
          );
      }

There are a couple of things to notice about the code above. First, I just used everything as UniversalTime. I thought about trying to do the whole timezone thing but realized that it didn’t matter for a countdown because as long as everything was in the same timezone the math would be right.

Second, notice that I’m eating all of the errors in the timer. The basic error that I can get from this code is a threading issue and since this is low priority code, I didn’t care about the threading issue. It’s not like there’s a real recovery path other than trying again in a second.

Now that I had the simple countdown and badge done, I decided that I didn’t like the flat labels and wanted a real clock style count down. To accomplish this I started a Silverlight FlipClock project to build a reusable clock face.

Since I was creating a flipping clock with a separate top and bottom, it made sense to create a control to encapsulate those bits in user controls that I could reuse. There were two interesting parts here. First, how to get the text to cut off at the top and second, how to get the text to scale correctly with the number if we resized it.

image The first issue was solved by using a brush that cut off to an opacity of zero just under halfway. I did that on both the background and the textbox. The whole gradient is actually made up of three stop points. Two set to be the same color and opacity of 100% and a third that is set to the same color but opacity of zero. This third stop is just a hair further down than the second. The purpose of the second stop is to keep it from fading from top to bottom rather than having the hard like that I desired.

<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
    <GradientStop Color="#FFFFFFFF" Offset="0"/>
    <GradientStop Color="#FFFFFFFF" Offset="0.48"/>
    <GradientStop Color="#00FFFFFF" Offset="0.49"/>
</LinearGradientBrush>

The second issue was solved thanks to Laurent Bugnion. He pointed out that although Silverlight doesn’t have a scalable font, there is a VIewbox in the Silverlight Control Toolkit on Codeplex that will scale anything inside itself correctly.

<controls:Viewbox Height="Auto" HorizontalAlignment="Stretch" Margin="0,0,0,0"
VerticalAlignment="Stretch" Width="Auto" Content="Viewbox" Stretch="Fill">
<Grid x:Name="LayoutRoot" Background="{x:Null}">
. . .
</Grid>
</controls:Viewbox>

I added a simple property to set the value in the textbox.

public int Value
{
    get
    {
        return int.Parse(txtNumber.Text);
    }
    set
    {
        txtNumber.Text = value.ToString();
    }
}

Then I just played with the styling a little with a slight highlight on the top and the like.

Next, I created a FlippingNumber control that would be the base for the various numbers on the clock. To make it look right,  I actually needed 4 sections to the control. Two to show the current number and two to show the next number and we flip between them. I simply put two of the top and two of the bottom number controls one the page. The flip was just an animation similar to the flipping of the two images in the top of the badge. The only difference is that I skewed the controls a little to give it a slight 3d effect.

Once that was done, the rest of the number was built in code.

public FlippingNumber()
{
    InitializeComponent();

    FlipDown.Completed += new EventHandler(FlipDown_Completed);
}

void FlipDown_Completed(object sender, EventArgs e)
{
    numBottomBack.Value = _val;
    numTopFlip.Value = _val;
}

int _val = -9999;
public int Value
{
    get
    {
        return _val;
    }
    set
    {
        if (value != _val)
        {
            _val = value;
            numBottomFlip.Value = _val;
            numTopBack.Value = _val;

            FlipDown.Begin();
        }
    }
}

The other property that I added was so that the clock could adjust the speed at which the number flipped.

public double SpeedRatio
{
    get
    {
        return FlipDown.SpeedRatio;
    }
    set
    {
        FlipDown.SpeedRatio = value;
    }
}

Finally, I was ready to piece together the clock. That was a simple matter of placing 6 of the flipping number controls on the clock face. All that was left was set the time on the clock based on a value in the passed in TimeSpan.

TimeSpan _timeSpan;
public TimeSpan TimeSpan
{
    get
    {
        return _timeSpan;
    }
    set
    {
        _timeSpan = value;

        numSecondOne.Value = FirstCharacter(_timeSpan.Seconds);
        numSecondTwo.Value = SecondCharacter(_timeSpan.Seconds);

        numMinuteOne.Value = FirstCharacter(_timeSpan.Minutes);
        numMinuteTwo.Value = SecondCharacter(_timeSpan.Minutes);

        numHourOne.Value = FirstCharacter(_timeSpan.Hours);
        numHourTwo.Value = SecondCharacter(_timeSpan.Hours);
    }
}

private int FirstCharacter(int num)
{
    string s = string.Format("{0:00}", num);
    return int.Parse(s.Substring(1, 1));
}

private int SecondCharacter(int num)
{
    string s = string.Format("{0:00}", num);
    return int.Parse(s.Substring(0, 1));
}

The last thing on the badge that I did was change the timer control code on the overall blog badge to pass the time span into the clock face rather than parsing it into textboxes.

Now I wanted people to be able to use it from their own blog. To allow that to happen, I needed a put together a cross domain policy via the clientaccesspolicy.xml file.

<?xml version="1.0" encoding="utf-8"?>
<access-policy>
    <cross-domain-access>
        <policy>
            <allow-from http-request-headers="*">
                <domain uri="*"/>
            </allow-from>
            <grant-to>
                <resource path="/resources/SilverCountDown.xap" include-subpaths="true"/>
            </grant-to>
        </policy>
    </cross-domain-access>
</access-policy>

Notice that I’m allowing access from any domain but restricting access to only load the specific xap file.

Anyone who wants to put it on their blog can do so simply by leveraging the following object tag.

<div id="silverlightControlHost">
    <object data="data:application/x-silverlight," 
type="application/x-silverlight-2" width="100px" height="200px"> <param name="source" value="http://www.joshholmes.com/resources/SilverCountDown.xap"/> <param name="background" value="white" /> <param name="minRuntimeVersion" value="2.0.31005.0" /> <param name="autoUpgrade" value="true" /> <a href="http://go.microsoft.com/fwlink/?LinkID=124807" style="text-decoration: none;"> <img src=http://go.microsoft.com/fwlink/?LinkId=108181
alt="Get Microsoft Silverlight" style="border-style: none"/> </a> </object> <iframe style='visibility:hidden;height:0;width:0;border:0px'></iframe> </div>

Feel free to place the badge on your own blog to help promote RIAPalooza – When this year finishes, I’ll update the counter with next years dates as soon as we know it… :)

Also – I put the code up at http://www.joshholmes.com/resources/silvercountdown.zip. Feel free to take it and play with it.