I noticed the other day that Storyboard has a SetTarget method, but it does not have a corresponding GetTarget method. So how can you find the target of a Timeline child? Fortunately, Storybard has a GetTargetName method, we can use this method to obtain the name of the target and then search parent container’s children for a matching child. Suppose we had the following code:
<Grid x:Name="LayoutRoot">
<Grid.Resources>
<Storyboard x:Key="FadeOut">
<DoubleAnimation
To="0"
Duration="0:0:3"
Storyboard.TargetName="TestText"
Storyboard.TargetProperty="(UIElement.Opacity)" />
</Storyboard>
</Grid.Resources>
<TextBlock x:Name="TestText" Text="The Rain In Spain"></TextBlock>
</Grid>
In the example above, the TextBlock TestText is the target of the DoubleAnimation timeline in the FadeOut Storyboard. We can find this using the following code:
var fadeOut = (Storyboard)LayoutRoot.Resources["FadeOut"];
var doubleAnimation = (DoubleAnimation)fadeOut.Children[0];
var targetName = Storyboard.GetTargetName(doubleAnimation);
var target = LayoutRoot.Children.Single(c => ((FrameworkElement)c).Name == targetName);
It is worth noting that you must specify a TargetName, or the GetTargetName method will return null. In other words, if you are creating a Storyboard programmatically, and you only use the SetTarget method instead of the SetTargetName method, the GetTargetName method will not return what you expect:
var sb = new Storyboard();
var da = new DoubleAnimation();
da.To = 0;
da.Duration = TimeSpan.FromSeconds(3);
Storyboard.SetTarget(da, TestText); // Instead try: Storyboard.SetTargetName(da, "TestText");
Storyboard.SetTargetProperty(da, new PropertyPath("(UIElement.Opacity)"));
sb.Children.Add(da);
var target = Storyboard.GetTargetName(da); // Returns null if we don’t call SetTargetName
Enjoy!
In many situations, it is important that your Silverlight appilcations load as quickly as possible. For example, when building a Silverlight Advertisement, the user should not experience a significant load time. The advertisement should load just as fast as the other page assets. But how do you achieve this when your application includes heavy-weight resources, such as video or images? Of course you could pull these individual assets from a server during runtime, but if your assets are packaged inside the XAP package, you need a better solution.
The most common solution I have seen is to build a small, light-weight Silverlight application that will behave as a preloader for the main application. The preloader application will load first to provide some immediate feedback to the user and then begin downloading the main application asynchronously in the background. As soon as the main application has finished downloading, the preloader application will instantiate and then inject the main application into its visual tree.
It is important that the preloader application is as small as possible. However, most of the preloaders I have seen take advantage of LINQ to parse the AppManifest.xaml file. LINQ is great, but this requires you to reference System.Xml.Linq.dll in your application. Uncompressed, this assembly is 118KB. If you want to minimize your XAP size, you need to eliminate this dependency. Below, I have written a small preloader that uses the good old-fashioned XmlReader to parse the AppManifest file. To take advantage of this preloader, simply include this class in your preloader application and call it using the example code below:
XapLoader
public class XapLoader
{
public event EventHandler<XapLoaderEventArgs> XapLoaded;
private string m_rootAssembly;
private string m_typeName;
public void LoadXap(Uri xapUri, string typeName)
{
m_rootAssembly = Path.GetFileNameWithoutExtension(xapUri.ToString());
m_typeName = typeName;
WebClient wc = new WebClient();
wc.OpenReadCompleted += wc_OpenReadCompleted;
wc.OpenReadAsync(xapUri);
}
private void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
var manifestStream = Application.GetResourceStream(
new StreamResourceInfo(e.Result, null),
new Uri("AppManifest.xaml", UriKind.Relative));
string appManifest = new StreamReader(manifestStream.Stream).ReadToEnd();
string assemblyName = m_rootAssembly + ".dll";
XmlReader reader = XmlReader.Create(new StringReader(appManifest));
Assembly asm = null;
while (reader.Read())
{
if (reader.IsStartElement("AssemblyPart"))
{
reader.MoveToAttribute("Source");
reader.ReadAttributeValue();
if (reader.Value == assemblyName)
{
var assemblyStream = new StreamResourceInfo(e.Result, "application/binary");
var si = Application.GetResourceStream(assemblyStream, new Uri(reader.Value, UriKind.Relative));
AssemblyPart p = new AssemblyPart();
asm = p.Load(si.Stream);
break;
}
}
}
if (asm == null)
throw new InvalidOperationException("Could not find specified assembly.");
var o = asm.CreateInstance(m_typeName);
if (o == null)
throw new InvalidOperationException("Could not create instance of requested type.");
RaiseXapLoadedEvent(o);
}
private void RaiseXapLoadedEvent(object instance)
{
if (XapLoaded != null)
{
XapLoaded(this, new XapLoaderEventArgs(instance));
}
}
}
public class XapLoaderEventArgs : EventArgs
{
public object Instance { get; set; }
public XapLoaderEventArgs(object instance)
{
this.Instance = instance;
}
}
// USAGE
...
XapLoader loader = new XapLoader();
loader.XapLoaded += loader_XapLoaded;
loader.LoadXap(new Uri("MyTestApp.xap", UriKind.Relative), "MyTestApp.Page");
...
...
private void loader_XapLoaded(object sender, XapLoaderEventArgs e)
{
var instance = e.Instance as UIElement;
if(instance != null)
{
LayoutRoot.Children.Add(instance);
}
}
There are probably more optimizations that would shrink the size down even further. If you can find any significant optimizations, please let me know and I’ll update the code. I’ve attached a sample project below. Enjoy!
In my spare time, I have been re-writing the Star Selector control that is part of Silverlight Contrib. I re-wrote this control because there were several things I wanted to accomplish. This re-write includes quite a few new features. I will highlight them in the next few sections:
Half-Star Selection
The control supports enabling half-star selection. To enable or disable this feature, simply set the AllowHalfStarSelection property accordingly. The default value is false.
DisplayValue and Value Property Data Types
The DisplayValue and Value properties have been changed from an Integer to a double data type. This allows you to select half-stars and the control will reflect the specified value by rounding up or down to the nearest half-star when AllowHalfStarSelection is true.
DisplayValue reflects which stars will be visible. Value reflects what the user has chosen. I have split this concept into 2 properties to allow for more flexibility when using the control.
For example, suppose you wanted to start off with an empty rating. Once the user selects a value, you want to pull down the newly calculated average for all ratings. The following code would allow you to achieve this:
void Page_Loaded(object sender, RoutedEventArgs e)
{
StarSelector1.DisplayValue = 0;
}
private void StarSelector1_ValueChanged(object sender, RoutedEventArgs e)
{
// Calculate Average including (StarSelector1.Value)
StarSelector1.DisplayValue = GetCalculatedAverage();
}
Half-Star Templatability

Maximum Property
The Maximum property allows you to specify the number of stars that will be displayed on the control. The default value is 5.
ReadOnly Property
Sometimes you just need to show a rating without allowing the user to interact with the control. The ReadOnly property allows for this. The default value is false.
Disabled Property
The new star selector control also has a Disabled property. You can also provide a Disabled state in the control template if you wish. The default value is false.


You can try out a live demo of the control and download the source code below:
Live Demo
Enjoy!
As you probably already know, there are quite a few ways to pass data to a Silverlight application.
- Use InitParams in the Silverlight plugin declaration
- Read data off the querystring
- Use WebClient to request a value
- Interact with the HTML DOM to pull a value from the hosting page
But what do you do if you don’t have control over the hosting page? Really, your only option is to make a call to a web server using WebClient. But even that can have cross-domain policy implications. Besides, that’s a lot of work to be doing just to simply supply a small amount of data to your application.
There is one other option however.
Application.Current.Host.Source will provide you with the URI of the XAP package that the host will render. So what this means is that when you provide the URI for your XAP package, you can append a querystring to the end, like so:
<param name="source" value="ClientBin/MyApp.xap?MyKey=Testing"/>
Now, you simply need to read (and parse) the querystring in your application. Since Silverlight does not automatically parse the querystring for us, we have to do the dirty work. Yes, there are many ways you can do achieve this. Below, you will find one technique:
private string GetXapPackageQueryStringValue(string key)
{
if (string.IsNullOrEmpty(key))
return string.Empty;
key = key.ToLower();
string xapPackage = Application.Current.Host.Source.OriginalString;
int questionMarkIndex = xapPackage.IndexOf('?');
if(questionMarkIndex == -1)
return string.Empty;
string queryString = xapPackage.Substring(questionMarkIndex, xapPackage.Length - questionMarkIndex);
queryString = queryString.Replace("?", string.Empty).ToLower();
string[] keyValuePairs = queryString.Split(new[] { '&' });
for (int i = 0; i < keyValuePairs.Length; i++)
{
string[] pair = keyValuePairs[i].Split(new[] { '=' });
if (pair[0] == key)
{
return pair[1];
}
}
return string.Empty;
}
Then, you can retrieve the querystring value like so:
string val = GetXapPackageQueryStringValue("MyKey");
// TODO: Use val in your code.
This can be useful in situations when you have the ability to specify the path to a XAP package, but don’t have control over any other facets. One example of this would be CodePlex. On CodePlex, you have the ability to embed Silverlight applications in your Wiki. You do this with the following syntax:
{silverlight:url=http://mydomain.com/MyApp.xap?MyKey=testing,height=300,width=500}
Now, you can build one application and reference it multiple times while varying the querystring for each declaration. This is really helpful when you want to write one Silverlight application that behaves differently when you change the querystring. For example, on Silverlight Contrib, we have a tutorial application that will display one control for each different parameter that is passed in.
As you may already know, we are merging Silverlight Contrib and Silverlight Extensions into one super-ultra-mega project. Last month, we held a poll to let the community decide the name of the consolidated project. Which played out in the following way:
So it’s a no-brainer, obviously the community wants to keep the Silverlight Extensions name moving forward (and we will). In the next few months, both teams will be working to consolidate the code bases into a single code base. This will take some time, but moving forward, it will make for a much better product.
Please stay tuned!
In my opinion, one of the coolest new features in Silverlight 3 is Cached Composition. Cached Composition is a performance enhancement feature that will allow visual elements to be cached as bitmaps after the first render. After caching occurs, the application can effectively bypass the render phase for the cached visual elements and simply display the cached elements instead.
This is a huge plus for scenarios with scrolling objects! Before Cached Composition, Silverlight would re-render the object for each frame, even if the object itself never changed. With Cached Composition enabled, the object is cached and Silverlight can render the object even faster.

Composition Caching is an opt-in feature. In other words, you must first enable composition caching at the plug-in level by setting a parameter like so:
<param name="EnableGPUAcceleration" value="true" />
After enabling GPU acceleration, you can then specify at the elements you wish to cache. Specifying the CacheMode is a simple matter of setting a property:
<Grid CacheMode="BitmapCache" …
Here are a few notes about BitmapCaching:
- BitmapCache is the only cache-mode that is supported.
- The Caching is applied to the element and all of it’s child elements.
- BitmapCaching should be used in scenarios where you are blending, transforming (translating, stretching, rotating).
- Misuse of the CacheMode feature can hurt performance, so you need to really think through what you are doing. If your visual tree is interleaving cached and un-cached elements, you are effectively causing multiple rendering surfaces to get created behind the scenes. The un-cached surfaces are rendered in software and the cached surfaces are rendered in hardware. Your performance will be best if you can minimize the total number of rendering surfaces and get the hardware to do work where it can.
- You can determine which elements are being cached by adding the EnableCacheVisualization parameter to your Silverlight plugin declaration. <param name="EnableCacheVisualization" value="true" />
- As far as I can tell, GPU acceleration does not occur on Macs when the Silverlight application is not in full-screen mode. This is apparently a limitation of the Safari plug-in model.
Before Silverlight 3, you had to do perform some less-than-ideal tricks to achieve a drop shadow effect on your text. In fact, the most decent trick I could find had to rely on the TextBox control and not the TextBlock control since you cannot re-template a TextBlock control. But even that technique was not optimal since you couldn’t get a truly smooth shadow.
Well, that was then an this is now! In Silverlight 3, you can do the following:
<TextBlock
Foreground="White"
Text="Lorem ipsum dolor sit amet"
FontSize="50"
FontWeight="Bold">
<TextBlock.Effect>
<DropShadowEffect Color="Black" BlurRadius="5" />
</TextBlock.Effect>
</TextBlock>
And Presto!
Just look how much easier it is to read the first line over the second line! Also notice that there are a few other properties that you can specify if you desire:
- BlurRadius – Controls the radius of the blur. A larger number would indicate a softer shadow.
- Color – The color of the shadow (or glow if the color is appropriate).
- Direction – The angle of the drop shadow (315 degrees is the default). The angle increases in a counter-clockwise direction.
- Opacity – A double value indicating the percentage of opaqueness.
- ShadowDepth – Indicates how far the shadow should appear below the text. This property is used to control how far away the object appears from it’s backing surface.
Of course, you can apply effects to any UIElement, it just so happens that drop shadows on text is really, really, useful!
As promised, I am posting my slide deck and code from my Silverlight Advertising presentation at the 2009 Charlotte Code Camp. The turnout was great and I had some good questions!
The Charlotte guys hold an excellent code camp. I always enjoy making the trip!
Download Presentation


The Silverlight Contrib and Silverlight Extensions projects will be merging into a single project soon. By consolidating the two projects we hope to provide an even more valuable resource to the Silverlight Community. Before the consolidation, both projects were hosted on CodePlex and shared the same Ms-PL licensing. This will not change, however, there will be only one unified project moving forward.
Obviously, both teams are fond of the names chosen for their open source projects, so we decided it would be best if we let the Silverlight community decide which name to go with moving forward. Whichever project name receives more votes by March 29th, 2009 at 6:00 PM EDT will become the new project name after merging.
It looks like March is shaping up to be a busy month for me. There are a lot of community events going on:
Since I am a new MVP, this will be my first trip to the MVP Summit. I am really excited to finally meet (in person) the many Microsoft employees and fellow MVPs that I have had the opportunity to work with!
I hope to see you at some of these events!