Skip Navigation LinksHome > View Post

Enhance your file uploads with Silverlight

UPDATE - this has now been updated for SL2 RTW.

Many web sites support the ability to upload files (including this one, that's how we get the images up here) and most use HTML's <input type="file"> control to achieve this.

There's one for you to play with right now.

There are a couple of limitations to this control though, including:

  • It can only upload one file at a time
  • No way to specify the extension filters, e.g. Jpeg File (*.jpg|*.jpeg)
  • Can't specify the title of the dialog
  • Can only send the data via HTTP POST (which is a very messy business)
This can be a real pain, especially the limitation to one file and this is a particular annoyance when posting a lot of images to this blog.

Fortunately, Silverlight 1.1 contains an OpenFileDialog class that blows away these limitations and I set about creating a Silverlight uploader for theJoyOfCode.com. And here's what it looks like:

Uploader control and HTML button to initiate an upload:

User clicks Upload and selects some files

User clicks Upload and selects some files

Upload starts and progress is indicated using an animated progress bar:

Upload starts and progress is indicated using an animated progress bar

If the file already exists on the server, the user is prompted to overwrite:

If the file already exists on the server, the user is prompted to overwrite

Finshed! Dialog shows how many files were uploaded:

Finshed! control shows how many files were uploaded

Most of the code, at least the mechanics, are lifted directly from this File Upload Quickstart on silverlight.net including the use of a .asmx HttpPost reciever on the server. Going forward we'd probably use SOAP (and maybe even WCF with MTOM) as support is added to 1.1, this is one of the features missing from the alpha at the time of writing.

The big differences are that our uploader writes the files to disk on the server, prompts the user to overwrite, supports multiple files and provides progress updates.

Hmmm, well, let's be more truthful about that last statement. Because we're using the BrowserHttpWebRequest object to post the data and this doesn't support updates we report progress by file because we upload one file at a time.

If you've read the quickstart I referenced earlier then you've already got a good idea of what can be done with the OpenFileDialog and for this reason, I think the most interesting bits to discuss here are some of the fudges and workarounds we had to use to get this working in the current 1.1 alpha.

Fudge 1: Using the HtmlTimer to implement Asynchronicity

The HtmlTimer is currently marked obsolete and will probably be replaced in the next refresh of 1.1. However, because I wanted to update the progress bar visuals during the uploading loop I wanted to make the upload asynchronous. And this is easy enough because the System.Threading classes are mostly available (including the ThreadPool) but the main problem is that there is currently no way to marshal a call back to the UI thread. Therefore I decided to change tack and push all the files to be uploaded onto a Queue<>. The HtmlTimer (which always fires on the UI thread, and is therefore not truly asynchronous) could then check this queue and start uploading the next file. The short gap in between the timer ticking will allow the UI to catch up and re-render. It works pretty well and given that the BrowserHttpWebRequest can currently only be invoked from the UI thread, this might be the optimum approach unless this fact changes.

Fudge 2: Calling javascript's window.confirm from managed code

When I prompt the user "The file 'filename.ext' already exists on the server. Overwrite?" I use the window.confirm dialog that's readily available in Javascript. However, in the alpha version of silverlight there is no way to directly invoke Javascript from the managed silverlight world (I believe this will change in future releases of 1.1). For now, we have to register a managed type such that it can be called from Javascript, like so:

WebApplication.Current.RegisterScriptableObject("DialogsEntryPoint", _jsDialogs);

This allows the Javascript to access the following class (which must be marked [Scriptable]).

[Scriptable]
public class JSDialogs
{
[Scriptable]
public event EventHandler<MessageEventArgs> Query;

public bool Confirm(string message)
{
     MessageEventArgs mea = new MessageEventArgs(message);
     if (Query != null)
         Query(this, mea);
     return mea.Confirmed;
}
}

Now, from Javascript I can wire up a Javascript method to the Query event. Note how we pass the result of window.confirm back on to the MessageEventArgs.Confirmed property.

// this is wired in as the Silverlight control's onLoad event
function loaded(sender, args, root)
{
_root = root;
_root.getHost().content.DialogsEntryPoint.Query = jsQuery;
}

function jsQuery(sender, args)
{
args.Confirmed = window.confirm(args.Message);
}

Now whenever we need to prompt the user from C# we can call the JSDialog's Confirm method shown above:

if (_jsDialogs.Confirm("Are you sure you want to do this?"))
{
    DoThis();
}

Limitations

There are a few limitations to this little tool, the main one being that if you try to upload a large file (about 5MB+?) then you'll likely get an OutOfMemoryException or the BrowserHttpWebRequest will throw an InvalidOperationException. This is because the whole file is loaded into memory and converted to a Base64 string which is taken from the quickstart mentioned above. Not perfect but it works for now.

Download

I've posted a sample so you can poke around this implementation. Once again, this is SAMPLE CODE and has not been optimised. It has been written in a short time for a personal project and as such hasn't been subject to a great deal of testing. The content is provided "as is" without warranty of any kind, either expressed or implied, including but not limited to the implied warranties of merchantability and/or fitness for a particular purpose.

Furthermore, it is highly likely that this implementation will no longer work under any future refreshes of 1.1. You may ask why it was the effort given the inevitably short lifetime of the workarounds and fudges I've put in place. For one, I can now upload multiple files to my blog which is a big pain-saver. Also, it was great fun getting to see the limitations and really understand the Silverlight plumbing.

PS - You'll need Visual Studio 2008 (currently in Beta 2) to open the solution.

Tags: Silverlight

 
Josh Post By Josh Twist
4:12 AM
25 Aug 2007

» Next Post: WPF for Line of Business Applications
« Previous Post: Part VI. Creating the Report

Comments are closed for this post.

Posted by HOONS @ 01 Oct 2007 7:00 PM
Thanks~ for your lecture.
I'm Microsoft Visual C# MVP in korea.
At now, I write book about silverlight.
Do you feel when support WCF with MTOM?

Posted by Josh @ 01 Oct 2007 11:53 PM
I'm afraid it's not just a question of 'When?' but also a question of 'If?'.

There's a lot to squeeze into the tiny 5 megs of space that the silverlight team are working toward. I'm not sure this will make it in.

Posted by Leo @ 06 Feb 2008 2:25 PM
Any plans to port this to RTM VS08?

Posted by Josh @ 07 Feb 2008 2:03 AM
Probably not since SL1.1 alpha (which this targets) has no go-live support.

I'll probably revisit it once Silverlight 2.0 (new name for 1.1) goes RTM or a late beta if there's any demand.

Posted by Mark Hildreth @ 08 Nov 2008 5:28 PM
The example on the silverlight website is down as is your sample code. Can you repost the code?

Posted by sherwin @ 29 Dec 2010 10:56 PM
wala e

Posted by sherwin @ 29 Dec 2010 10:58 PM
maganda 2

© 2005 - 2014 Josh Twist - All Rights Reserved.