Drag and drop files to WPF application and asynchronously upload to ASP.NET Web API - StrathWeb

Strath

June 17th, 2012

Drag and drop files to WPF application and asynchronously upload to ASP.NET Web API

Using the same ApiController you would use for browser based uploads

A while ago, I wrote a post on uploading files to ASP.NET Web API by dragging and dropping them to the browser. Today I will continue with the same topic, but instead, let’s build a WPF application to which you can drag and drop files, and they’ll get uploaded to ASP.NET Web API.

The important thing, is that we will use the *exact* same Web API upload controller as before, meaning the same controller will be handling uploads from the browser and from the client (WPF) application. The client will use the new HttpClient and it’s client-side DelegatingHandlers.

The Plan

So the plan for today as follows:
1. User starts a WPF client application and drags and drops a number of files to it.
2. The application reads the files, displays them in a list and submits to ASP.NET Web API
3. The progress bar indicates to percentage of upload progress
4. Once the files are successfully uploaded, the list of files is updated with the information which file has been uploaded and what is its size

Upload controller

As mentioned, the upload controller is the same as in this post. If you have your own implementation you can use it, if not just grab the source project from that post and run that.

The idea is that I will have this Web API application running locally, and WPF application will submit files to it over HTTP. Of course in normal scenario the two apps wouldn’t be run on the same machine, but since they will be communicating over HTTP only, that’s enough for this demo. If you read (or at least skimmed) the previous post, the Web API controller saves the uploaded files to c:/uploads.

WPF client

The upload client is very simple:

It contains a drop area – Listbox – which will also list the files after they have been dropped, and a status bar, which will display helpful messages and a progress bar in a bottom right corner.

XAML for the application is as follows:

This is very basic, so not much to discuss really. One basic Grid, a TexBlock, ListBox and a StatusBar. So far so good. Let’ go to the code behind.

First of all, notice in the XAML that the ListBox is bound to a collection called Files.

That’s a public property of the MainWindow, and will be used to keep track of our uploaded files. While in this simple example we don’t use a view model at all, it is still nicer to use a bound collection rather than manually add and remove items from the listbox. To make this work, we need to set the DataContext of the MainWindow to this, and we do that in the constructor.

Code behind – drag events

First thing to do programatically is to handle three drag events – over, leave and drop. These events are attached to the Listbox element.

1. Drag over

On drag over, we want to indicate to the user that copy is possible, so we change the cursor to “copy” and change the color of the listbox.

2. Drag leave

On drag leave, we just want to set the background color back to what it was initially.

3. Drag drop

On drop, we read the list of file paths from the event arguments, and add them to the aforementioned files ObservableCollection which we are using to bind the UI and codebehind together.

Then we call the UploadFiles method, the core of our application, and set the background color back (since the drag leave event won’t happen anymore).

Uploading files

Now let’s look at the method responsible for the upload of files.

The request will be despatched by the new HttpClient and pass through a ProgressMessageHandler, which will, in turn, track the progress of the file upload. Please note that this feature is not part of the standard System.Net.Http you’d normally reference from a WPF application, as it’s shipping with ASP.NET Web API. The solution is to include in the project system.Net.Http.Formatting from ASP.NET Web API package on Nuget (you might as well grab entire Web API client libraries, since we’ll be using JSON.NET later on for deserialization anyway so it will come in useful). However, this ProgressMessageHandler is relatively new, and hasn’t made it into the Web API RC (but will be part of the final release), so to use this you’d need daily build from Nuget.

Henrik Nielsen has great instructions on how to set it up, it’s very easy, and with Web API RC, there is no GAC anymore.

One more thing worth noting, since we are uploading files, the content for our HTTP request will be MultipartFormDataContent.

So a quick walkthrough of what is happening above:
1. We create the progress handler, wire up the upload event (I’ll show the event code in a moment), create an empty HttpRequestMessage and it’s MultipartFormDataContent

2. We iterate through the list of file paths, and read all of them using FileStream, and then append to the request body

3. We add additional request information, such as HttpMethod and our Web API’s URL. We display a status bar information that files are being uploaded. This is done through a helper method which I will get back to in a moment.

4. We construct the HttpClient using HttpClientFactory and invoke a request using SendAsync. This is the point when our files get sent to the Web API. The method is async, so whatever we want to do later, we need to wrap in ContinueWith (note, in .NET 4.5 it’s much easier, with await modifier).

5. If the response is successful, we read what the Web API has returned to us and update the status bar accordingly. If you had a look at the HTML5 upload tutorial, where the Web API controller is being described in detail, you’d know that the API returns a list of JSON serialized List of simple custom FileDesc objects, which expose three properties: name, size and path. Since we want to unwrap JSON into strongly typed object, I add the following class to the project.

Next, we iterate through the returned collection of files, and try to match them, by filename, to the files we already have in our observable collection bound to the listbox. If the match is found, it means the file has been successfully uploaded, so we update the collection by adding appropriate information (file size, and info that it’s uploaded).

To do that we need to use Application.Current.Dispatcher, since the thread running the callback on the async upload cannot directly update the “files” observable collection. If we tried to do that, we’d run into “This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.” exception.

6. If there is an error, we display it in the status bar. We wrap everything in try catch, since there are plenty of possible exceptions to handle, but I will not be focusing on them here.

Finally, let’s have a look at the two things we skipped. First, the progress handler.

The ability to track upload and download progress just like that, is a wonderful addition to the ASP.NET Web API (or rather to the System.Net.Http, since as you can see here you can use it outside Web API as well). It exposes all kinds of useful information for up- and download of data when communicating with the HTTP endpoint.

In our case, we leverage on HttpProgressEventArgs.ProgressPercentage which can be directly assigned to WPF’s ProgressBar and utilized to visually track upload progress. Again, due to threading safety, we use Dispatcher to asynchronously invoke the update of the UI. If tried to access ProgressBar directly, we’d run into “The calling thread cannot access this object because a different thread owns it” exception.

The last method to look at is the private void ThreadSafeUpdateStatus(string status). As you have seen, we’ve used it all across the board to update the status bar.

Trying it out

Let’s try if the application works. First, we need to make sure the Web API application is running.

Now, let’s run our beautiful UI.

And drag some files into it. The status bar shows the file count.

The progress bar will be indicating progress (especially useful with larger files or many files).

If upload is successful, the list indicates that beside each file. The status bar indicates successful upload.

And, just to verify, the files end up where they should have (c:/uploads).

Summary and source code

This is a second part of this mini drag and drop series, I hoped you enjoyed the WPF one, as much as the HTML5 one. I hope someone finds some use for this technique in real life projects. Also, you may note that the upload code can be very reusable, for example you might use it to sync files in some folder with those on the Web API server, suing i.e. Windows Service.

Anyway, the source code is attached, as always:
Source on github

Be Sociable, Share!

  • chouteau

    Very great article, thanks

  • Pingback: Dew Drop – June 18, 2012 (#1,347) | Alvin Ashcraft's Morning Dew

  • phenibut

    Very interesting info! Perfect just what I was searching for!

  • Matt Shubert

    This is pretty cool, but it seems to fail on a collection of files that are 100MB or larger. The task.Result seems to show up as “NotFound”, which is odd.

    • Matt Shubert

      Whoops, never mind – it was a Web.config issue, just had to change the default MaxRequestLength.

      My fault, great article! :)

      • Matt Shubert

        And for those who may run into the same issue, the default is actually 4MB, which is pretty useless if you want a tool like this to upload pictures, large PDFs, videos, etc. But it’s easy to fix.

      • Filip W

        Yes, that’s that :) I forgot to mention it in the post, but if you download the source attached to the HTML5 upload post referenced here, I actually set it to 10MB in the config there (kind of “mailboxy” size limit). Thanks for the kind comment!