Developing Silverlight 1.0 RIAs: XAML

(Read the intro here.)

First, XAML is an awkward language to work with. From a business perspective, this makes sense because Microsoft can push their Expression suite on developers and designers. But this is unacceptable for a client-side developer who wants to write markup by hand and is a Mac user.

XML is a terrible vehicle for describing anything more complicated than nested boxes. It is especially terrible with vectors. This is XAML generated by Blend to describe a tiny arrow pointing left:

  1. <Path Width="5" Height="11.061" Fill="#FFFFFFFF" Stretch="Fill"
  2. Data="M441.46101,604.74493 L445.48904,608.88653 441.52674,613.46898
  3. 442.43734,614.14997 446.91423,608.85206 442.3852,604.04503 z"
  4. Opacity="1" x:Name="back_arrow" Canvas.Left="6" Canvas.Top="8"
  5. RenderTransformOrigin="0.5,0.5">
  6.  <Path.RenderTransform>
  7.   <TransformGroup>
  8.    <RotateTransform Angle="-180"/>
  9.   </TransformGroup>
  10.  </Path.RenderTransform>
  11. </Path>

Opening up 2000 line XAML files full of markup like this is headache inducing.

Second, XAML in SL 1.0 is the Anti-DRY language. It lacks concepts like cascading styles and class names, so if you want all your TextBlock elements to be red, you have to specify Foreground="#FFFF0000" for each one.

It gets worse for repeating elements programmatically. Say you have XAML for a button with a TextBlock label, and you want to create four of them to make a menu:

  1. <Canvas Width="100" Height="20">
  2.  <Rectangle Fill="#FF000000" RadiusX="5" RadiusY="5"
  3.   Width="100" Height="20">
  4.   <TextBlock x:Name="buttonLabel" Canvas.Top="3"
  5.    Canvas.Left="5" Foreground="#FFFFFFFF">Label</TextBlock>
  6.  </Rectangle>
  7. </Canvas>

Of course, you’ll want programmatic access to that TextBlock so that you can change the label text. But because there is only one namespace for x:Names (x:Name="buttonLabel" can only be used once), adding more than one to the XAML hierarchy will throw an exception. Also, there are no utilities like .getElementsByTagName. Solutions like button.children.getItem[0].children.getItem[0] are brittle and don’t scale.

There is a solution to this, but it was nearly impossible to find in the Silverlight 1.0 documentation. The createFromXaml method takes a second boolean argument that can create a new, separate namespace for x:Names. This allows you to create multiple XAML objects from the same source and add them to the XAML hierarchy without x:Name collisions.

A caveat to this technique is that findName won’t find elements created in a new namespace, so you have to save a representation of each new element in JavaScript.

My methodology for using XAML tries to tackle these two big challenges. It breaks down to these steps:

  • Use images instead of XAML vectors when the file size overhead allows it.
  • If Blend is used, only create small, isolated pieces of XAML so each is easier to digest and manipulate.
  • Zip up every XAML file and download it all at the beginning. XAML files are usually light enough that it shouldn’t noticeably delay the start of your application.
  • Create XAML elements dynamically from the files in the zip file. Use createFromXaml(text, true) for repeated elements and always save a representation of the elements in JavaScript to manipulate later.

I built a number of utility functions to make this methodology very easy to use. Here is an example:

  1. var ComplicatedXamlCreator;
  2. new XamlDownloader('cmn/xaml/packaged/xaml.zip', {
  3.     onComplete : function(zip) {
  4.         ComplicatedXamlCreator = zip.save('complicatedXaml.xaml');
  5.         zip.destroy();
  6.     }
  7. });
  8.  
  9. // later, create a xaml object and add it to the XAML hierarchy
  10. var xaml = ComplicatedXamlCreator(true);
  11. root.children.add(xaml);
  12.  
  13. // or create multiple xaml objects and manipulate independently
  14. for (var i = 0; i < 10; i++) {
  15.     var xaml = ComplicatedXamlCreator(true);
  16.  
  17.     // finds a different TextBlock element each iteration
  18.     var label = xaml.findName('label');
  19.     label.text = 'Button Label ' + i;
  20.  
  21.     root.children.add(xaml);
  22. }

My XamlDownloader utility is a simple wrapper for the Downloader class that passes back an instance of a ZipHandler class. The save method grabs a XAML file out of the zip file and returns a XAML creation function that can be run as many times as necessary.

Because the XamlDownloader utility creates a closure around a Downloader instance, you have to destroy that instance or you’ll create a massive memory leak. The WWE project would bring down a fresh instance of Firefox 2 in half an hour easily.

« Developing Silverlight 1.0 RIAs Adding CSS rules with JavaScript to hide content on page load »

One Response to “Developing Silverlight 1.0 RIAs: XAML”

  1. [...] VSM breaking the XAML - Silverlight 2 issue and workaround. Saved by laxkid275 on Thu 02-10-2008 Developing Silverlight 1.0 RIAs: XAML Saved by realcissy on Wed 01-10-2008 VB.NET XAML Classes and Namespaces Saved by cavemanlawyer15 [...]

    Recent Links Tagged With "xaml" - JabberTagsOctober 3rd, 2008 at 4:19 am

Leave a Reply