Silverlight 2 brings support for threading to the browser. You can either directly start new threads using System.Threading.Thread and System.Threading.ThreadPool, or you can use the higher-level (and recommended) System.ComponentModel.BackgroundWorker type. The latter encapsulates the concept of executing work in the background (using a thread from the thread-pool) and updating the UI based on progress and/or completion of that work, which means that you can safely update the UI from the related events.

A lesser-known type that we introduced in beta 1 is System.Windows.Threading.Dispatcher. This type lets you execute work on the UI thread - something that's useful when you directly want to update the UI from a background thread. Since Silverlight always has a single UI-thread, there is only a single disatcher instance per Silverlight application. This instance is accessible via any DependencyObject or ScriptObject instances' Dispatcher property. Once you have a reference to a dispatcher, you can use its BeginInvoke method to dispatch your work. In Silverlight we added an overload which takes an Action, which means you don't need to add a cast or anything to help the compiler infer what type of delegate you want to pass:

C#:
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 

var myThread = new Thread(() => {
    // Using a lambda...
    myTextBlock.Dispatcher.BeginInvoke(() => myTextBlock.Text = "Updated from a non-UI thread.");

    // Using an anonymous delegate...
    myTextBlock.Dispatcher.BeginInvoke(delegate {
        myTextBlock.Text = "Updated from a non-UI thread.";
    });
});
myThread.Start();

Please note that you may not be able to find the dispatcher property via intellisense. It's marked as an advanced property, so you either need to update your VS settings to display advanced members, or you just need to ignore intellisense and assume your code will in fact compile regardless of what intellisense implies. The same goes for CheckAccess, which is actually marked as a member that should never be displayed. The main reason these members aren't always visible is because they shouldn't be as common as the other members on a DependencyObject. As I mentioned before, you'll probably want to use a BackgroundWorker most of the time instead.

Gotchas

There are a couple of things to be aware of. The first is that we try to guard against cross-thread invocations when this would potentially be unsafe. For example, we don't allow you to call into the HTML DOM or a JavaScript function from a background thread. The reason for this is that both assume to be invoked on the UI thread. Breaking this assumption can lead to unexpected behavior, including browser crashes.

The other thing to be aware of is creating deadlocks. Silverlight comes with primitives such as Monitor (encapsulated via the lock construct in C#) and ManualResetEvent which make it trivial to create a deadlock. A deadlock will cause most browsers to hang completely. While technically this isn't very different from some JavaScript that infinitely, it's often easier to accidentally create a deadlock than an infinite loop of code. For example, I've seen several people try to create a synchronous version of HttpWebRequest by letting the current thread wait for a ManualResetEvent to be notified by the response callback. HttpWebRequests however execute their callbacks on the UI thread, which means you have a deadlock right there. While ideally you avoid blocking the UI thread entirely, you should at least consider specifying timeouts when you use a synchronization object. For example, instead of the lock construct in C# (Monitor.Enter/Exit), consider using Monitor.TryEnter/Exit passing in a reasonable timeout, and instead of using ManualResetEvent's parameterless WaitOne, consider using one of the overloads.

There has been a fair amount of feedback regarding the HttpWebRequest type in Silverlight. People have asked or commented about:

  1. The lack of support for synchronous requests;
  2. Being limited to GET/POST requests;
  3. No support for headers in GET requests;
  4. Uploading large files;
  5. Complexity of asynchronous requests.

The response to most of this feedback has been that we are limited by what functionality the browser provides to plugins. For example, the API implemented by all browsers but IE (called the NPAPI, short for Netscape Plugin API) basically has the following support for networking:

  1. NPN_GetURL(Notify). Used to initiate a GET request. This API does not have support for passing in headers. There is also no synchronous version of this API.
  2. NPN_PostURL(Notify). Used to initiate a POST request. This API optionally takes a buffer of in-memory POST data. There is no synchronous version of this API either.

You may be wondering why we were able to support synchronous requests in Silverlight 1.1 alpha, why it did support headers in GET requests, etc. In Silverlight 1.1 alpha we didn't use the aforementioned APIs. Instead, we essentially wrapped the browser's XMLHttpRequest object that you can use in JavaScript today via our HTML/JS bridge. This had a few disadvantages:

  1. No support for cross-domain requests.
  2. Lack of support for dealing with binary data. We were able to get this working in FF, but Safari seemed to have problems handling responses beyond a certain size (couple of KB) wih binary data in them.
  3. Since it depends on interoperability with JavaScript, it's not quite as efficient as directly using native APIs.

There were a few things we could've done (and could still do) to deal with this. We could've used the operating system's networking stack and address most of the feedback we've had so far. The main disadvantage would be that all the requests would execute in their own security context. This means that if a user is authenticated using Windows authentication, any request sent by the Silverlight application would still be unauthenticated. Alternatively we could've created a hybrid, and choose what request type we would use based on the functionality you need. For example, if you don't care about binary data but you do care about headers in GET requests, we could use the browser's XMLHttpRequest object. But if you care about binary data and need authentication, we could use the browser's plugin API. Needless to say this would allow for a rather awkward/inconsistent programming experience.

Until we find a better way to deal with all of these problems in a future release, you will unfortunately have to deal with these shortcomings. To help you out a little though, I've put together some code that basically does create some sort of hybrid between XMLHttpRequest and the current version of HttpWebRequest in Silverlight. In a nutshell, you can write code like this:

C#:
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 

// Based on the request uri, this will either return the Silverlight HttpWebRequest 
// type (which supports binary data and cross-domain requests), or a HttpWebRequestEx 
// type (which supports PUT/HEAD/etc, headers and synchronous requests).
var request = HttpWebRequestEx.Create(requestUri);

// Assume we are requesting a resource on our server. Cast to HttpWebRequestEx 
// such that we can call GetResponse for a synchronous request.
var sameDomainRequest = (HttpWebRequestEx)request;
sameDomainRequest.Headers["foo"] = "bar";
var response = sameDomainRequest.GetResponse();
// ...

I've also put together a type which might make it a little easier to work with HTTP requests. It's basically a wrapper around HttpWebRequestEx and HttpWebRequest and it is implemented as a fluent interface which should make it a little easier to construct a request:

C#:
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 

// Synchronous request.
var response = Request.To("foo.txt").Send().ReadAllText();
HtmlPage.Window.Alert(response);

// Asynchronous request with a header and POST data.
Request.To("foo.txt")
    .WithMethod("POST")
    .WithHeader("foo", "bar")
    .WithBody(writer => writer.Write("hello"))
    .SendAsync(response => HtmlPage.Window.Alert(response.ReadAllText()));

// Asynchronous, cross-domain request.
Request.To("http://services.digg.com/stories/topic/microsoft?count=3&appkey=http%3A%2F%2Fexample.com%2fapplication")
    .SendAsync(response => HtmlPage.Window.Alert(response.ReadAllText()));

You can download the sources and binaries to try this out yourself. Let me know what you think.

During MIX this year Stefan Schackow did a presentation on the HTML/JS bridge in Silverlight 2. (It’s the second part of the presentation; the first part is Chung Webster talking about how you can use the ASP.NET controls to integrate Silveright in your web application.) If you’re interested, you can download the code for this session and see how things were done.

Almost all of the Silverlight demos you see out there are completely client-side, and invoke Web services to post/get data. This approach makes sense, but when you want to use Silverlight to functionally enhance your existing website, you may want to have some kind of integration between Silverlight objects and your server-side controls.

You may also want to enable some kind of Silverlight object composition. For example, my AsyncFileUpload component has two visualizers. It should be easy to add a new visualizer without modifying any existing code.

To address these problems, I’ve put together some experimental code.

Object composition

Each Silverlight instance has its own AppDomain. You can pass managed objects from one Silverlight instance to another through the HTML/JS bridge, but you would only be able to program against it like any other JavaScript object. The ScriptObject.ManagedObject property for such an object would be null if the underlying managed object isn’t part of the current AppDomain. As a result, we ideally compose objects inside a single Silverlight application, such that we can directly reference other objects. For example, the visualizers for our AsyncFileUploader can each reference the actual uploader, handle its events, get a stream to files being uploaded, etc.

By composing the objects inside a single Silverlight instance, we have to solve a new problem, which is visual composition. We are suddenly limited visually to a single 'canvas' if you will. I’ve addressed this to a certain extent by introducing a concept called viewports. Per Silverlight instance there can be one viewport, which will be set as the root visual. All visual objects will be added to this viewport. It’s up to the implementation of the viewport to handle how it adds the visual objects. For example, a StackViewPort may add all visuals sequentially. A GridViewPort on the other hand may add visuals to the grid based on information that was set server-side:

ASP.NET:
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 

<wilco:AsyncFileUploadVisualizer ID="v1" runat="server" UploaderID="AsyncFileUpload1" />
<wilco:AsyncFileUploadProgressVisualizer ID="v2" runat="server" UploaderID="AsyncFileUpload1" />
<wilco:GridViewPort ID="GridViewPort1" runat="server" Height="200px" Width="100%">
    <ColumnDefinitions>
        <wilco:ColumnDefinition Width="2*" />
        <wilco:ColumnDefinition Width="1*" />
    </ColumnDefinitions>
    <Locations>
        <wilco:GridViewPortItem ControlID="v1" Column="0" />
        <wilco:GridViewPortItem ControlID="v2" Column="1" />
    </Locations>
</wilco:GridViewPort>

To do the actual composition on the client, we need a few things. We need is a loader which downloads all the right things. As I mentioned before, we want to be able to implement new components. Those components will be added to their own XAP. Our loader needs to download the XAPs for all the objects before those objects can be loaded.

Once the loader has downloaded the XAPs, we have to load the main assembly from this XAP. We can do this by parsing the AppManifest’s XAML as a deployment object and then find the main assembly. In code this looks like:

C#:
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 

private static Assembly LoadAssemblyFromStream(Stream xapStream) {
    string appManifestString;
    using (var reader = new StreamReader(Application.GetResourceStream(new StreamResourceInfo(xapStream, null), new Uri("AppManifest.xaml", UriKind.Relative)).Stream)) {
        appManifestString = reader.ReadToEnd();
    }

    Assembly entryPointAssembly = null;
    Deployment deployment = (Deployment)XamlReader.Load(appManifestString);
    foreach (var part in deployment.Parts) {
        var streamInfo = Application.GetResourceStream(new StreamResourceInfo(xapStream, null), new Uri(part.Source, UriKind.Relative));
        Assembly assembly = part.Load(streamInfo.Stream);

        if (assembly.FullName.Split(',')[0].Equals(deployment.EntryPointAssembly)) {
            entryPointAssembly = assembly;
        }
    }

    xapStream.Close();
    return entryPointAssembly;
}

(This technique can be useful for any kind of loader. Be careful though about the consequences. For example, if someone can use your loader to load their own XAP, that XAP would essentially impersonate your XAP, and thus be able to access its isolated store, etc. Certain things may also not work, such as resources defined in the delay-loaded XAP.)

After we’ve loaded all the right assemblies into the current AppDomain, we can instantiate every object. The easiest way to do this is if we would simply render XAML directly from our server-side controls. We can then just go through each piece of XAML and load it using the XamlReader.

To allow references between objects, we need to first instantiate all the objects and register them with a unique (user-defined) name. We then need to notify the objects that all objects have been registered such that they can actually get a reference to other objects. The following interface captures this:

C#:
1 
2 
3 
4 
5 
6 
7 
8 

public interface ISilverlightObject {

    string Name {
        get;
    }

    void Initialize();
}

For visual composition we also need a way to add visuals to a viewport if there is one. We can add another interface to take care of this:

C#:
1 
2 
3 
4 

public interface IViewPort : ISilverlightObject {

    void AddVisual(ISilverlightObject visual);
}

All visuals can add themselves to the viewport if there is one by using this interface. The viewport should take care of the rest. Viewports can set themselves as the root visual if there isn’t one already. The non-visual objects don’t need to do anything special. They can just do their thing based on their property values and/or a call to their Initialize method. Now that we have most of the client-side plumbing in place, let’s see what we can do to add some kind of integration with ASP.NET.

ASP.NET integration

So far we have a Silverlight application which is essentially a host for composite objects. To host this application, and also to group composite objects, I introduced a SilverlightDomain ASP.NET control. I also introduced a SilverlightObject base class which you implement for every Silverlight object. This base class will register its instances with the SilverlightDomain that its part of. During rendering, the SilverlightDomain will ask every registered SilverlightObject to render XAML.

The SilverlightObject base type automatically renders a few things. Based on the value of its ClientClrNamespace and ClientTypeName properties it will render the tag-name. All properties marked with a SilverlightPropertyAttribute will be added to the XAML. It will also register callbacks for all of its events marked with the SilverlightEventAttribute. On the client those events do need to be marked with ScriptableMemberAttribute, such that our plumbing code can properly attach a handler to it which initiates the postback. When the client-side event is raised and a postback is initiated, our SilverlightObject’s RaisePostBackEvent method is invoked. By default it will try to invoke a public On<EventName>(EventArgs) method, if one exists. If you don’t want to have such a public method, or you want to raise a more complex event, you can override RaisePostBackEvent and manually raise an event.

As I just mentioned, you need to implement a few abstract properties such as ClientClrNamespace and ClientTypeName. You also need to implement PackageUrl. The SilverlightObject type will automatically render these as part of the XAML it renders. On the client, our package loader will use these attributes to find out what packages need to be downloaded. It then removes those attributes such that it ends up with 'clean XAML' that the XamlReader can load the remaining XAML directly.

A Silverlight powered ASP.NET button

Although there’s usually not much point in using Silverlight just to render primitive controls such as a button, this does concisely illustrate what it takes to put together a simple ASP.NET component that uses Silverlight for rendering. Here’s the client-side code:

C#:
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 

public class Button : System.Windows.Controls.Button, ISilverlightObject {

    [ScriptableMember(ScriptAlias = "click")]
    public new event EventHandler Click;

    public Button() {
        base.Click += SimpleButton_Click;
    }

    void SimpleButton_Click(object sender, RoutedEventArgs e) {
        if (Click != null) {
            Click(sender, e);
        }
    }

    void ISilverlightObject.Initialize() {
        App.Current.LoadVisual(this);
    }
}

And the server-side code looks like this:

C#:
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 
56 
57 

public class Button : SilverlightObject {

    protected internal override string ClientClrNamespace {
        get {
            return "clr-namespace:Wilco.Windows.Browser;assembly=Wilco.Windows.Browser";
        }
    }

    protected internal override string ClientTypeName {
        get {
            return "Button";
        }
    }

    protected internal override string PackageUrl {
        get {
            return Page.ClientScript.GetWebResourceUrl(typeof(Button), "Wilco.Web.Silverlight.Resources.Wilco.Windows.Browser.xap");
        }
    }

    [SilverlightProperty(ClientPropertyName="Background", Converter=typeof(SilverlightColorConverter))]
    public Color BackgroundColor {
        get;
        set;
    }

    [SilverlightProperty(ClientPropertyName="Content")]
    public string Text {
        get;
        set;
    }

    [DefaultValue(0), SilverlightProperty]
    public int Height {
        get;
        set;
    }

    [DefaultValue(0), SilverlightProperty]
    public int Width {
        get;
        set;
    }

    [SilverlightEvent(ClientEventName = "click")]
    public event EventHandler Click;

    public Button() {
        //
    }

   protected override void RaisePostBackEvent(string eventArgument) {
        if (Click != null) {
            Click(this, EventArgs.Empty);
        }
    }
}

To use this on our page, we can write the following code in ASP.NET:

ASP.NET:
1 
2 
3 
4 

<wilco:SilverlightDomain runat="server">
    <wilco:Button ID="button1" runat="server" BackgroundColor="Red" OnClick="button_Click" Text="Button 1" />
    <wilco:GridViewPort runat="server" Height="300" Width="100%" />
</wilco:SilverlightDomain>

To see a demo of this, go to this page with a few Silverlight buttons on it. For the source code, you can download Wilco.Web.Silverlight.zip.

A little less than a year ago I wrote about an AsyncFileUpload component for Silverlight/ASP.NET. Several things have happened since then, so I decided to rewrite it and address some of the limitations it had along the way. To make sure we’re on the same page, let me outline the goals of this proof-of-concept:

  1. Demonstrate how OpenFileDialog and HttpWebRequest can be used to upload (large) files.
  2. Show how isolated storage can be used to enable file resumes (even across browsers on the same machine).
  3. Share some experimental object model to simplify ASP.NET/Silverlight component development.

The only real non-goal is building a file upload component of production-quality.

The result

There are a few different demos you can take a look at:

  1. AsyncFileUpload with the default template (pure HTML visualization);
  2. AsyncFileUpload with a custom template (includes two Silverlight visualizers);
  3. AsyncFileUpload used from JavaScript. Although this demo still uses ASP.NET, you could easily do the same thing from other languages/frameworks/web-servers as well.

You can also download the source code for the actual library behind all of this.

Uploading large files

Silverlight 2’s HttpWebRequest buffers the request data in memory entirely, because the underlying API it uses (provided by the browser) doesn’t support streaming. This means that – at least at the time of writing – you can’t just upload large files.

We can work around this by uploading files in chunks. It does mean we need some special handling on the server, but a basic implementation requires little effort. We just need to make sure we send some meta-data with each chunk, such that the server knows where a chunk is supposed to go. There is some overhead involved here, but we can make up for this by showing accurate and visual feedback.

By sending chunks to the server, we get support for resumes almost for free. All we need to do is keep track of some information, such as something that identifies a file and the location of the next chunk we need to send. When we store this information in isolated storage, we can use it from every browser on the user’s machine. This means you can resume uploading a file in any of the installed browsers, even if you uploaded part of the file in a different browser.

While we’re uploading chunks to the server, we know exactly how much we’ve progressed and how much there is left. We can also easily find out how fast we’re uploading. This data can be used to give feedback to the user about the progress, speed, anticipated time left, and so forth.

On the server we need to know a few things per chunk:

  1. Something that identifies the file;
  2. Something that identifies the user;
  3. The chunk’s location;
  4. The actual data.

When we have this information, we can process each chunk fairly easily. We could additionally return a checksum of the file and verify on the client that the server processed the right data. If it didn’t, the client could try resending the chunk. After the client is done uploading, it could submit a form to the server. On the server you would again need something that identifies the user (such as a session ID) to find out what exactly has been uploaded.

ASP.NET/Silverlight component development

I will go over this in a separate post, probably tomorrow.

Increasing quotas

Isolated storage is a facility that lets you read/write (transient) data without worrying about other applications accessing this data. We first introduced this in Silverlight about a year ago with the alpha release. Its support was fairly basic though, and you couldn't store more than 1mb of data per application. We have given this feature an upgrade in Silverlight 2 beta 1. Although the default limit is less than it was before (100kb instead of 1mb), you can now ask the user for a larger quota. You can do this by calling IsolatedStorageFile.TryIncreaseQuotaTo(long):

C#:
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 

void OnMouseLeftButtonDown(…) {
    using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication()) {
        if (store.TryIncreaseQuotaTo(/* 2mb */ 2 * 1024 * 1024)) {
            // The user accepted our request.
        }
        else {
            // Quota not increased… Deal with it.
        }
    }
}

This method can only be called from a Silverlight initiated action, such as a mouse-down event on a WPF object. There's no cross-browser way to tell if a user clicked on a HTML object, so unfortunately this isn't supported. We decided to only support TryIncreaseQuotaTo in response to user-initiated actions because without this restriction an app could ask for more data at arbitrary, unexpected moments. This can be both annoying and unsafe – a user could accidentally accept a request for a larger quota if the dialog suddenly takes away focus.

Silverlight will only ask the user for one of the pre-defined quotas: 100kb, 1mb, 5mb, 10mb or unlimited. This means that if you ask for 2mb, the user will actually be asked for 5mb. We do this to avoid asking the user every other second if it's ok to increase the quota by another few bytes - something the average user probably wouldn't understand.

Level of isolation

Each Silverlight application gets its own isolated store. This means that if you have two applications in the same directory, they will each get their own isolated store. This store will be accessible from every browser that's supported by Silverlight, which means you can write data in FF and read back this data in IE.

We considered using isolation per page that hosts a Silverlight application. Unfortunately this doesn't work well in shared hosting scenarios. A page at www.contoso.com/joe/foo.html can have an iframe with www.contoso.com/john/bar.html in it, and access its DOM. This means it can inject its own Silverlight application into that DOM, and thus get access to the isolated store of the other website on the same domain.

The HTML 5 spec runs into exactly this problem as well. For this type of scenario it recommends you simply do not use offline storage. Given the fact that by default you can't just execute your own code within a different Silverlight application's application domain, Silverlight should not be prone to this kind of cross-site attack.

There is a downside to Silverlight's implementation though. When hosting a Silverlight application on a server and letting third-party websites add this to their page, they would all share the same isolated store. This might not be desirable. You could work around this by using URL rewriting on the server, but you need to be careful about the potential security implications this can have.

Finally, while isolated stores are shared per application, quotas are shared per domain. This is done in anticipation of a configuration dialog we will likely be adding in a future build. Having quotas per application rather than per domain means it would make configuration a lot harder for users.