I’ve been talking technology for a while here, but it’s about time I start adding some value. I already mentioned the JUXtapose blog, so get over there for podcasts on User Experience and the technologies I use to make my UX and UI better.
Today, I want to talk about Silverlight 2 (Beta 1). One of the things that always frustrated me when creating a new application is that nobody ever wrote a good example of how to get my Silverlight application to scale with the random sizes of my user’s browsers. As long as I stay vector based, this application should look the same at 640×480 and 1280×1024. And if it makes sense to have it on a mobile phone, then let’s use the same application there too.
Today, my friends, I’ve got the simple steps you need to make your Silverlight 2 application scale effectively. I’ll start this tutorial after you’ve created your default application (there’s a great walkthrough on that here by Jesse Liberty).
I am using Visual Studio 2008, and Microsoft Silverlight Tools Beta 1 for Visual Studio 2008. My .entire solution can be downloaded here, so you can compare the changes I have made to a default project.
If you followed the usual steps, your project should look something like mine.
We’re only going to be working in two files for this: Page.xaml, and its code-behind file, Page.xaml.cs. First, let’s take a look at the Page.xaml file, because more of our work will be code, not markup. Below is a shot of the entire XAML file (click to enlarge).
The only additions I have made to this file are the entire <canvas.rendertransform> section. I’ve also made the rootCanvas red, so that we can see it. One other thing that I have to credit Matt Casto for finding…you MUST define an initial size for your RootCanvas. Mine is 800×600, but you can use any size you’d like.
Now we’ve got a little code to write, but it’s honestly pretty straightforward. We’ve got four things to add:
- an event handler for when the browser is resized.
- a Page_Loaded method to intialize everything.
- a proxy method for resizing our Silverlight control initially.
- a Resize() method for doing the heavy lifting.
Here’s a screenshot of my entire application namespace within the page.xaml.cs file (click to enlarge).
First, we need to create a couple of variables. primarily, I need variables for the original height and width of my control before I get started. You’ll see why in a moment, but it’s basically to retain the “aspect ratio” of our control.
Second, in my “main” method, Page(), I route a new RoutedEventHandler to my Page_Loaded method when the XAML loads.
this.Loaded += new RoutedEventHandler(Page_Loaded);
Third, I have to create my Page_Loaded method. Here, I need to capture the original height and width, and I also add a new event handler to my application when the “host.content” = browser is resized. Finally, I call my Resize() method.
void Page_Loaded(object sender, RoutedEventArgs e)
_originalWidth = RootCanvas.Width;
_originalHeight = RootCanvas.Height;
Application.Current.Host.Content.Resized += new EventHandler(Content_Resized);
Fourth, now that I have a new event handler pointing to Content_Resized(), I should probably write that method too. It just passes the call along to my Resize() method.
void Content_Resized(object sender, EventArgs e)
Fifth and finally, I need to write my Resize() method. This is slightly more complicated than you might expect, but there’s a reason. We need to capture the width and height of the new browser size. That’s what currentWidth and currentHeight are for. uniformScaleAmount is what makes this complicated. We want to scale this thing uniformly, that is to say, if the application was originally 800×600, we want to keep a 4:3 ratio at all times, rather than stretching the app to fill the space allowed. Once we have determined the appropriate scale constant, we then also want to keep this app centered both vertically and horizontally in the browser as well. The last 4 lines of the code handle this, by altering the size of the overall control.
double currentWidth = Application.Current.Host.Content.ActualWidth;
double currentHeight = Application.Current.Host.Content.ActualHeight;
double uniformScaleAmount = Math.Min((currentWidth / _originalWidth), (currentHeight / _originalHeight));
RootCanvasScaleTransform.ScaleX = uniformScaleAmount;
RootCanvasScaleTransform.ScaleY = uniformScaleAmount;
double scaledWidth = Math.Min(_originalWidth * uniformScaleAmount, currentWidth);
double scaledHeight = Math.Min(_originalHeight * uniformScaleAmount, currentHeight);
RootCanvasTranslateTransform.X = (Math.Min(RootCanvas.ActualWidth, currentWidth) – scaledWidth) / 2d;
RootCanvasTranslateTransform.Y = (Math.Min(RootCanvas.ActualHeight, currentHeight) – scaledHeight) / 2d;
Please give this a try on your next application. You should be able to use this on any size Silverlight application, and as long as your creative assets can handle scaling to any size (read: vector), your application should look great at all sizes. Please leave me a comment if you give this a try…I want to know that I helped someone out!