Hello World with Silverlight and Silverlight Streaming

I've been tinkering with Silverlight and Silverlight Streaming and thought I'd share some of my discoveries and pointers.

Silverlight is designed to dovetail into a traditional HTML+JavaScript web app.  Your web page loads a Silverlight.js file and constructs a Silverlight object.  What happens after that within the Silverlight objects is determined by a XAML ("zammel") file that defines the contents and code references of the Silverlight object.

Silverlight Streaming is a free high-bandwidth hosting service specifically for Silverlight content.  Note that it only hosts the Silverlight content of your web app (the XAML files, video content, and .NET managed code assemblies to be downloaded and executed on the client), not your HTML web pages themselves.  You'll still need your own web server to serve up your HTML entry page, which can then load up the Silverlight portion of your app from the Silverlight Streaming host.

For a bare bones minimum Silverlight application, you'll need 4 files:  An HTML page, a XAML page for Silverlight, a copy of Silverlight.js, and another JavaScript file of your own creation to bind your Silverlight content to your HTML page.  You can run all this from localhost once you have Silverlight installed on your machine.  For this first run demo, we'll call everything test1: test1.html, test1.xaml, and test1CreateSilverlight.js:

test1.html

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="www.w3.org/1999/xhtml" xml:lang="en">
    <head>
        <title>A Sample HTML page</title>
        <script type="text/javascript" src="Silverlight.js">
        </script>
        <script type="text/javascript" src="test1CreateSilverlight.js">
        </script>
    </head>
    <body>
        <div id="mySilverlightControlHost">
        </div>
    </body>
    <script type="text/javascript">
        var parentElement = document.getElementById("mySilverlightControlHost");
        createMySilverlightControl();        
    </script>
</html>        

 

test1.xaml

 <Canvas xmlns="schemas.microsoft.com/client/2007" 
        xmlns:x="schemas.microsoft.com/winfx/2006/xaml">
    <Rectangle Width="300" Height="70" Canvas.Left="10" Canvas.Top="10">
        <Rectangle.Fill>
            <RadialGradientBrush GradientOrigin="1,0">
                <GradientStop Color="Yellow" Offset="0.0" />
                <GradientStop Color="Red" Offset="0.25" />
                <GradientStop Color="Yellow" Offset="0.75" />
                <GradientStop Color="Red" Offset="1.0" />
            </RadialGradientBrush>
        </Rectangle.Fill>
    </Rectangle>
    <TextBlock FontSize="40" FontFamily="Georgia" FontStyle="Italic" 
        FontWeight="Bold" Canvas.Top="20" Canvas.Left="20">
        <TextBlock.Foreground>
            <LinearGradientBrush>
                <GradientStop Color="Cyan" Offset="0.0" />
                <GradientStop Color="Blue" Offset="1.0" />
            </LinearGradientBrush>
        </TextBlock.Foreground> Hello World!
    </TextBlock>
    <!-- Linear gradients -->
    <Rectangle Width="140" Height="70" Canvas.Left="10" Canvas.Top="90">
        <Rectangle.Fill>
            <LinearGradientBrush>
                <GradientStop Color="Yellow" Offset="0.0" />
                <GradientStop Color="Red" Offset="0.25" />
                <GradientStop Color="Blue" Offset="0.75" />
                <GradientStop Color="LimeGreen" Offset="1.0" />
            </LinearGradientBrush>
        </Rectangle.Fill>
    </Rectangle>
    <Rectangle Width="140" Height="70" Canvas.Left="155" Canvas.Top="90">
        <Rectangle.Fill>
            <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
                <GradientStop Color="Yellow" Offset="0.0" />
                <GradientStop Color="Red" Offset="0.25" />
                <GradientStop Color="Blue" Offset="0.75" />
                <GradientStop Color="LimeGreen" Offset="1.0" />
            </LinearGradientBrush>
        </Rectangle.Fill>
    </Rectangle>
    <!-- Radial gradients -->
    <Rectangle Width="140" Height="70" Canvas.Left="10" Canvas.Top="190">
        <Rectangle.Fill>
            <RadialGradientBrush>
                <GradientStop Color="Yellow" Offset="0.0" />
                <GradientStop Color="Red" Offset="0.25" />
                <GradientStop Color="Blue" Offset="0.75" />
                <GradientStop Color="LimeGreen" Offset="1.0" />
            </RadialGradientBrush>
        </Rectangle.Fill>
    </Rectangle>
    <Rectangle Width="140" Height="70" Canvas.Left="155" Canvas.Top="190">
        <Rectangle.Fill>
            <RadialGradientBrush GradientOrigin="1,0">
                <GradientStop Color="Yellow" Offset="0.0" />
                <GradientStop Color="Red" Offset="0.25" />
                <GradientStop Color="Blue" Offset="0.75" />
                <GradientStop Color="LimeGreen" Offset="1.0" />
            </RadialGradientBrush>
        </Rectangle.Fill>
    </Rectangle>
    <Rectangle Width="140" Height="70" Canvas.Left="300" Canvas.Top="190">
        <Rectangle.Fill>
            <RadialGradientBrush GradientOrigin="1,0">
                <GradientStop Color="Yellow" Offset="0.0" />
                <GradientStop Color="Red" Offset="0.25" />
                <GradientStop Color="Yellow" Offset="0.75" />
                <GradientStop Color="Red" Offset="1.0" />
            </RadialGradientBrush>
        </Rectangle.Fill>
    </Rectangle>
</Canvas>        

Ok, I got a little carried away with gradient fills.  It's fun!

 test1CreateSilverlight.js

 function createMySilverlightControl()
{
    Sys.Silverlight.createObject(
    "test1.xaml",                  // Source property value.
    parentElement,                  // DOM reference to hosting DIV tag.
    "mySilverlightControl",         // Unique control ID value.
    {                               // Control properties.
        width:'400',                
        height:'400',               
        inplaceInstallPrompt:false, // Determines whether to display
                                    // in-place install prompt if
                                    // invalid version detected.
        background:'#D6D6D6',       // Background color of control.
        isWindowless:'false',       
        framerate:'24',             
        version:'0.9'               // Control version to use.
    },
    {
        onError:null,               
        onLoad:null                 
    },
    null);                          // Context value -- event handler function name.
}

 

Put all three files into the root of your local web server (\inetpub\wwwroot for IIS) and copy the Silverlight.js from the SDK or step 1a in the Silverlight Quickstart instructions.  Fire up your browser and see what <localhost/test1.html> looks like.  If your Silverlight runtime is installed correctly and you've configured your local IIS as instructed, you should see this:

Hello World in Silverlight

Whee!  Pretty colors!

Note:  Unlike HTML, entity (tag) names in XAML are case-sensitive.  Pay attention to those camel-capped names like TextBlock and RadialGradientBrush.  If you've grown accustomed to sloppy casing in your HTML, it's time to start unlearning that. 

The good news is that when you get it wrong, Silverlight will tell you what identifier has it confused in a nice error message, instead of just plowing ahead and making the best of an impossible situation as HTML browsers and JavaScript interpreters tend to do.

A Question of Scale

Tinkering with XAML on your localhost web server is very convenient, but eventually you're going to need to think about how to make it real - that is, where you're going to put the stuff so that others can see it.  So before we get too far into this Hello World lets take a look at what it would take to push this demo app into a real production environment.

First, we'll need a web server.  Seems pretty obvious, and there are many options to choose from.  We could set up our own web server on the public (or DMZ) side of our network firewall, or buy into an economy web hosting package from any number of vendors.  Twenty bucks a month will get you a fair amount of server storage, SFTP uploads and a modest network traffic quota.

That'll work fine for testing and friends and family use, but what happens if our quirky little app gets "discovered" and linked by a major news feed or gossip network?  The app content right now is trivially small, but we're only getting started.  If we add rich media such as photos or video content, our cute little app isn't so harmless anymore.  Will our home-built server and residential broadband Internet connection be able to handle 1,000 hits per hour?  Per minute?  If we go the web hosting route, we may survive sudden surges in network traffic, but will we survive the bill that is sure to follow if the surge burns through our traffic quota? 

We've hardly finished "Hello World" and we've already reached the classic conundrum of a web startup:  Can we afford success? 

Silverlight Streaming

Silverlight Streaming offers a solution to that conundrum.  You can offload your bandwidth intensive media content from your server to high-bandwidth Silverlight Streaming servers.  You still need your own web server and domain name to define the context of the web app, but the heavy lifting can be delegated to the Silverlight Streaming network. 

With this solution, you can run a popular web site serving tens of megabytes (even gigabytes) of video content to thousands of visitors per minute on a shoestring server budget.  Your web server needs to be able to serve a small html file to thousands of visitors per minute (not difficult, and not likely to blow your traffic quota), and the rest - loading the media content from the Silverlight servers - happens down on the browser client.

So what is involved in hosting a Silverlight app on the Silverlight Streaming service?  Not much!

First, you need to provision your Silverlight Streaming publisher account on silverlight.live.com.  Login with your LiveID and you'll get back an account ID and an account key.  The account ID you'll need later to tell your app where to get the media content from.  The account key is for... well, I don't know what it's for.  Just keep it safe until we get to the part in this story where an account key is desperately needed. Pretend this is Zork.  Keep everything you find, as it almost certainly will be needed later.

Next, we're going to set up the fully locally hosted Silverlight app to run side by side with the Silverlight Streaming hosted content version of the app. Copy test1.html and test1CreateSilverlight.js in your local web server root directory to test1_hosted.html and test1CreateSilverlight_hosted.js in the same directory. 

Modify test1_hosted.html in your favorite text editor to replace the script references.  Silverlight.js becomes agappdom.net/g/silverlight.js and test1CreateSilverlight.js becomes test1CreateSilverlight_hosted.js.  Like this:

test1_hosted.html

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="www.w3.org/1999/xhtml" xml:lang="en">
    <head>
        <title>A Sample HTML page</title>
        <script type="text/javascript" src="agappdom.net/g/silverlight.js">
        </script>
        <script type="text/javascript" src="test1CreateSilverlight_hosted.js">
        </script>
    </head>
    <body>
        <div id="mySilverlightControlHost">
        </div>
    </body>
    <script type="text/javascript">
        var parentElement = document.getElementById("mySilverlightControlHost");
        createMySilverlightControl();        
    </script>
</html>        

 Now modify test1CreateSilverlight_hosted.js to change the Sys.Silverlight.createObject to Sys.Silverlight.createHostedObjectEx and delete all but the source and parentElement object parameters.  For the source parameter, change "test1.xaml" to "streaming:/xxxx/test1", where xxxx is your Silverlight Streaming application ID that you got when you created your account in step 1, and test1 is the name of the application slot we're going to create in Silverlight Streaming when we upload this stuff in a moment.

test1CreateSilverlight_hosted.js

 function createMySilverlightControl()
{
    Sys.Silverlight.createHostedObjectEx(
    { source: "streaming:/xxxx/test1",        // xxxx is your app id
      parentElement: parentElement            // DOM reference to hosting DIV tag.
    });    
} 

So where did all those parameters disappear to?  They move to a new file required by the Silverlight Streaming hosting environment called manifest.xml.  This file tells Silverlight how to initialize itself.  It's always called manifest.xml, and inside it looks like this:

manifest.xml

 <SilverlightApp>
    <source>test1.xaml</source>    
    <width>500</width>    
    <height>300</height>
    <inplaceInstallPrompt>false</inplaceInstallPrompt>
    <background>#D6D6D6</background>
    <framerate>24</framerate>
    <version>0.9</version>
    <isWindowless>false</isWindowless>
</SilverlightApp>

See?  Same data bits as in test1CreateSilverlight.js

Before uploading this app content to the Silverlight Streaming servers, we need to bundle everything up into a zip file.  Create a zip file / compressed folder (test1.zip) on your local drive (in your wwwroot directory is fine) and drag manifest.xml and test1.xaml into that test1.zip file.  Leave test1_hosted.html and test1CreateSilverlight_hosted.js on your local machine.

Finally, go to silverlight.live.com and click on the "Manage Applications" link on the left.  Click on "Upload a Silverlight Application".  Enter "test1" as the Application Name (this must match the last part of the streaming: URL in your call to Sys.Silverlight.createHostedObjectEx) and enter the path to your test1.zip file and upload it.

Count to 3 slowly in your head, then fire up <localhost/test1_hosted.html> in your browser.  Surprise!  It looks just like test1.html.  To double check that you're actually running the Silverlight content served from Silverlight Streaming, use View: Source to check that the HTML is referencing agappdom.net/g/silverlight.js and test1CreateSilverlight_hosted.js.  If you're still skeptical, shut down your network connection, purge your browser cache and reload the page.  If it's being served from your localhost, it will not be affected by the network outage.  If the page doesn't load, then that's pretty strong proof that the content is coming from the Silverlight Streaming servers.  Congratulations!  Now, plug your network back in before you miss something important on the Knight Rider gossip chat room.

Look Ma, No Bandwidth!

You've now got a fully functional rich media application that has a sophisticated, high bandwidth content distribution network running under the hood.  The main HTML page and JavaScript glue code are served from your web server, while the potentially bandwidth-heavy rich media content is served from the high bandwidth Silverlight Streaming network. 

Oh, and did I mention that Silverlight Streaming is free?  Must have slipped my mind.  Free streaming and hosting for up to 4GB of server storage.  See the FAQ for details.

You can make modifications to the xaml file locally and immediately test with <localhost/test1.html>.  When you're ready to upload to "production", copy the xaml file into the test.zip file, then select "Upload Updated Application" on the test1 Application Properties page under Manage Applications on silverlight.live.com and upload your new test1.zip to the server.  Upload your test1_hosted.html and test1CreateSilverlight_hosted.js to your production web server, and your app is deployed!

Crawl -> Walk -> Run

The next stage in our Silverlight crawl/walk/run voyage of discovery will be to either drop in some media content such as video or start tinkering with the real black magic inside Silverlight:  writing .NET managed code that executes inside the browser, deployed to the client via Silverlight Streaming.  I don't have a lot of video sitting around here, but boy, have I got code...