‘Silverlight’ Archive

Developing Silverlight 1.0 RIAs: JavaScript Optimization

(Read the intro here.)

My appreciation and respect for JavaScript increased tremendously while developing Silverlight projects. Not at all because of how easy it was to work with. But its flexibility and power made it interesting and fun to tackle challenges like optimization and application architecture.

Optimization

In Silverlight 1.0, the plugin is only a rendering and animation engine. All of the logic and much of the processing still resides in the browser’s JavaScript interpreter. All web applications need to make optimization a priority, but this is especially true for Silverlight 1.0 applications.

Doing a lot of small things can add up to make a big difference. Swap myArray.push(obj) with myArray[myArray.length] = obj. Avoid string concatenation when possible to give Internet Explorer a little boost.

I made two significant changes to my code between the WWE project and the last Library of Congress project that I believe made a big difference.

Avoid Function.bind

I usually resorted to a Function.bind method (usually the version in prototype.js) to manipulate function scope. This really comes in handy for event listeners:

  1. // inside of a class
  2. function addMouseEvents() {
  3.     this.xaml.addEventListener('MouseEnter', this.onMouseEnter.bind(this));
  4.     this.xaml.addEventListener('MouseLeave', this.onMouseLeave.bind(this));
  5.     this.xaml.addEventListener('MouseLeftButtonDown', this.onMouseDown.bind(this));
  6. }
  7.  
  8. function onMouseEnter(sender, mouseArgs) {
  9.     // "this" still refers to the instance of the class
  10. }

I really like this syntax, even though in an ideal world it wouldn’t be necessary. (Actionscript 3 is an ECMAScript implementation that fixes this). I’ve heard Mozilla might implement it natively, which would be lovely.

The problem with bind() is that it uses apply() internally, which is significantly slower than just executing the function. This makes a big difference for MouseMove or ProgressChanged event handlers.

Fortunately it is easy to avoid, and there are a number of ways to use depending on your personal preference. I usually used:

  1. // inside of a class
  2. function addMouseEvents() {
  3.     var that = this;
  4.     this.xaml.addEventListener('MouseEnter', function(sender, args) {
  5.         that.onMouseEvent(sender, args);
  6.     });
  7.     // .. other events
  8. }

I looked around for any documentation on the effects on bind() on performance and found nothing. This isn’t a Silverlight-specific issue but something other people might run into so it’s probably worth documenting.

Using and Removing Event Listeners

This also isn’t necessarily Silverlight-specific, but it’s also worth mentioning because of a few difference from how HTML DOM event listeners are handled. The crux of the matter is still that adding event listeners is memory intensive and not disposing of them properly can cause leaks and sluggishness.

In HTML you can use event delegation to limit the number of event listeners added. Add a single click event listener to a parent object, and act upon the event based on on the target property of the event, which is usually a child of the parent object. This isn’t possible in Silverlight 1.0 because there isn’t a real event object. The sender argument sent to the listener is always the object to which the event was added.

The only way to combat the overhead from adding event listeners is to reuse objects when possible. For WWE, there is a big grid of thumbnails the user can click on. Instead of creating a whole new grid each page refresh, I just changed the data for each item.

Sometimes reuse isn’t an option, and you’re forced to create and destroy XAML objects repeatedly. Removing event listeners before destroying an object isn’t difficult.

  1. var eventToken = xaml.addEventListener('MouseEnter', function() {});
  2. xaml.removeEventListener('MouseEnter', eventToken);

I came up with a way to store those event tokens in a simple, behind-the-scenes manner, which made this easier to do than not do. I’ll describe this in the next section on JavaScript/Silverlight Architecture.

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

This will be the archive for my advancements in Silverlight 1.0 development, now made pretty much obsolete by the 2.0 Beta 2 plugin released last week.

I’ve worked on four SL 1.0 projects:

All of these projects were developed under extremely tight deadlines and generally had ad-hoc development processes. HSN was a mess because the plugin was released after development had started. The first LOC projects had a lot of UX, creative, and project management complications. WWE actually followed our architecture and development processes very well but it was an overly ambitious project for a technology this immature.

For the last LOC project, I was finally able to piece together techniques developed over the last six months. It was still a very tight deadline and inevitably a bit ad-hoc, but I am still proud of it.

Challenges in Silverlight 1.0 Development

Silverlight 1.0 development is difficult by its reliance on two technologies:

  1. XAML
  2. JavaScript

Of course, that’s all there is to Silverlight 1.0! On top of that, it’s a buggy plugin, but there’s not much we can do about that. Instead, I’ll focus on ways to address the challenges presented by SL 1.0’s core technologies.