Silverlight 2.0: Project template for self-resizing applications

.NET, Silverlight, Technical stuff
See comments

Update: I posted an updated version which doesn’t use JavaScript. See here.

One of the first things I often do when I create a new Silverlight application is to make it self resizing. This is not very difficult, because thanks to the goodness of WPF (of which Silverlight is a subset), resizing a scene only involves one simple operation: a ScaleTransform. This transform applies to a panel (in this case the top Canvas) and to all its children. Using a ScaleTransform, all the distances (dimensions, but also coordinates) are multiplied by a factor, so you don’t need to move anything when the panel is resized. Additionally, since XAML graphics are vector-based, they will resize very gracefully (of course, if your application uses pixel-based graphics such as BMPs, PNGs, movies, etc…, then you will see pixels if you make the application very big).

The operations to make a Silverlight application self-sesizing are not very complicated, but they are annoying when you need to do it many times. This calls for automatization, according to the principles of pragmatic programming. To make this easier, I made a Visual Studio 2008 project template.

Downloading and installing the template

Installing the template is very easy: Simply download this Zip file and copy it without unzipping it to the folder “[MyDocuments]\Visual Studio 2008\Templates\ProjectTemplates\Silverlight\”.

Then, in Visual Studio 2008, select the menu “File / New / Project / Silverlight / SelfResizingSilverlightTemplate”.

File / New / Project / Silverlight / SelfResisingTemplate

The template will automatically be renamed according to the project name you enter in the “New project” dialog. There is just one catch: Visual Studio doesn’t search-and-replace in the JavaScript code. So after you create your new project, you must replace manually the string “SelfResizingSilverlightTemplate” with your project name. Simply use the “Replace” dialog (Ctrl-Shift-H) in Studio.

After you’re done, run the application by pressing Ctrl-F5. The Canvas has a yellow background, and I placed on rectangle in each corner, so that the resizing effect can be observed.

Self-resizing Silverlight application

How does it work?

For a Silverlight application to self-resize, it must get information from JavaScript. To do this, the Canvas and one of its methods are marked with the ScriptableAttribute, to enable JavaScript -> Silverlight communication.

Additionally, the main Canvas must be registered so that a JavaScript proxy is automatically created. This is done by using the method RegisterScriptableObject in WebApplication.Current, in the method Page_Loaded.

The OnResize method (which will be hooked to the Silverlight control’s “onResize” event) takes care of resizing: A scale factor is calculated, and the ScaleX and ScaleY properties of the main Canvas’ ScaleTranform are set accordingly. Additionally, the main Canvas is positioned in the middle of the screen (but that part can safely be removed if needed).

[Scriptable]
public partial class Page : Canvas
{
  private double _initialHeight = -1;
  private double _initialWidth = -1;

  public void Page_Loaded(object o, EventArgs e)
  {
    // Required to initialize variables
    InitializeComponent();

    _initialHeight = this.Height;
    _initialWidth = this.Width;

    WebApplication.Current.RegisterScriptableObject("MainRoot", this);
  }

  [Scriptable]
  public void OnResize(uint pixelWidth, uint pixelHeight)
  {
    double factor = pixelWidth / _initialWidth;

    if (_initialHeight * factor > pixelHeight)
    {
      factor = pixelHeight / _initialHeight;
    }

    (this.RenderTransform as ScaleTransform).ScaleX = factor;
    (this.RenderTransform as ScaleTransform).ScaleY = factor;

    double actualWidth = _initialWidth * factor;
    double newLeft = Math.Floor((pixelWidth - actualWidth) / 2);
    if (newLeft < 0)
    {
      newLeft = 0;
    }
    this.SetValue<double>(Canvas.LeftProperty, newLeft);
  }
}

In the JavaScript code, a method “handleOnResize” is hooked to the “onResize” event of the Silverlight control. This is done in the “handleOnLoad” method. The “handleOnResize” method does nothing but calling the corresponding C# method. So we have:

SelfResizeTemplate.prototype =
{
  handleOnResize : function(sender, eventArgs)
  {
    if (!this._control)
    {
      return;
    }
    
    // Call code-behind
    this._control.content.MainRoot.OnResize(this._control.content.actualWidth,
      this._control.content.actualHeight);
  },

  handleOnLoad : function(control, userContext, rootElement)
  {
    this._control = control;
    control.content.onResize = SelfResizeTemplate.createDelegate(this, this.handleOnResize);
    
    // Call onResize explicitly (needed for Firefox)
    this.handleOnResize(null, null);
  }
}

The rest of the code is just here to take care of a few basic tasks, such as allowing the application to be called from multiple HTML pages, setting the control’s background to a parameterized color, making it windowLess.

I hope that this template will be useful to you as it is to me.

Previous entry | Next blog entry

Comments for Silverlight 2.0: Project template for self-resizing applications