(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

Silverlight Desktop Drag and Drop + Silver Sky

(7 votes)
Braulio Diez
>
Braulio Diez
Joined Dec 14, 2009
Articles:   7
Comments:   3
More Articles
6 comments   /   posted on Jan 18, 2010
Categories:   General

This article is compatible with the latest version of Silverlight.


Introduction

Some years ago (pre-silverlight times) a client ask me for a weird requirement…he wanted to drag files from his local explorer and drop them on their ASP .net grid application without using any ActiveX help… I thought, umm… is that possible?

With the advent of Silverlight 2 we got the same request again, … we knew this time it was not a technology limitation, it was a sandbox security limitation, how does the Ms chaps can offer this feature without exposing a security hole? Well it seems that they have managed to “open the box” for this feature in Version 4 on a secure and very easy to use way.

In this article we will cover the basics of this functionality, dig into a more complex sample, and give you guidance on some common drag and drop scenarios.

Note: This article is co-authored with Jose Almoguera.

Step 1: The basics

Desktop drag and drop… What are we talking about? Allow Dragging and dropping any file from your desktop or windows explorer to a Silverlight app, just the same way you with desktop applications (e.g. drag from a windows explorer some files and drop it in another one).

Is that hard to implement? Not at all… have we only needed to perform the following steps:

  1. Choose the elemento where we want to allow the drop (e.g. a stackpanel).
  2. In that element set to true the flag “AllowDropElement”.
  3. Hook to the “Drop” event.
  4. Whenever that event is fired, we will get as parameter the list of files dropped, we only need to iterate through each of the file and process them.

Let’s start with a basic sample, we are going to create a silverlight app that:

  • Has an area defined to drop text files.
  • Once the user drops a file(s), the content is shown in a multiline textbox.

img1

 

The only thing you need to define in your XAML is where you want to allow file dropping (in this case in the orange background stack panel), and the method that will be hooked to the file(s) dropped event:

 <StackPanel x:Name="LayoutRoot" Background="White" Orientation="Vertical">
     <!-- AllowDrop on this stack panel, and specify the method to call when the file(s) are dropped -->
     <StackPanel Height="150" Margin="5" Background="Orange" AllowDrop="True" Drop="StackPanel_Drop">
         <TextBlock Text="Drop text files here from your desktop" HorizontalAlignment="Center" Margin="0,60,0,0" FontSize="18" />            
     </StackPanel>
     <TextBlock Text="Results shown here:"/>
     <!-- In this textbox we will show the results -->
     <TextBox x:Name="txContent" Height="180"  AcceptsReturn="True" VerticalScrollBarVisibility="Auto"/>
 </StackPanel>

On the codebehind we will implement the StackPanel_Drop method, here we will receive as a parameter the list of files dropped, and we just only need to iterate through each of the files, read the content and append it to our multiline text box:

 /// <summary>
 /// This event is called whenever an user drags a file or files from his desktop (or e.g. windows explorer) and drops it
 /// into the selected stack panel . 
 /// </summary>
 /// <param name="sender"></param>
 /// <param name="e"></param>
 private void StackPanel_Drop(object sender, DragEventArgs e)
 {
     StringBuilder contentBuilder = new StringBuilder();           
     string content = string.Empty;
     // Let's check that the drop contains data
     if (e.Data != null)
     {
         // The user can drop one or more files
         FileInfo[] files = e.Data.GetData(DataFormats.FileDrop) as FileInfo[];
         // In our case we expect text files, for the sake of simplicity error handling has been removed
         // you should add here try / catch blocks
         foreach (FileInfo fi in files)
         {                   
             // Read each text file
             using (StreamReader sr = fi.OpenText())
             {
                 // Add the content to the stringbuilder container
                 contentBuilder.Append(sr.ReadToEnd());
                 contentBuilder.Append("\n");
             }                    
         }
     }
     // Dump all the content to the multiline textbox
     txContent.Text = contentBuilder.ToString();
 }

Step 2: Silver Sky

Now that we have covered the basics, let’s go a for a more advanced sample, Do you have an Ms Sky Drive account? It offers great file storage on the “cloud”, you just select a set of files and upload them to your private drive space, some time ago they implemented a really useful feature, instead of use the file open dialog, let the users upload files by dragging them from their desktop to this web application (see snapshot below).

 

SL Sky Drive

Why not implement the same feature using Silverlight? We have called this sample “Silver Sky”:

 

SL Sky Drive

 

The scenarios we have implemented:

 

Scenarios

 

- Browser for server files: At application startup we read the content of the server file folder and show it in our Silver Sky app.

 

Server Content

 

- Upload Files: this one is the most interesting scenario, user drags from their desktop a selection of files and drop them in the box “Upload New File To Server”, the upload process starts and a progress bar indicates us how is being performed the operation.

 

Uploading Files

 

- Download file(s): A server file is dragged from the server file list and dropped into the Drop here to download box, the file is downloaded to the user’s local hard disk.

 

Downloading Files

Let’s get our hands dirty and focus on the code that has been implemented to cover the upload case: Drag & Drop mechanism works the same way as in the previous sample (allow drop + drop event, args list FileInfo), the tricky part comes when you want to upload the file to the server, in some cases users will upload big files, and we have to take into account that:

  • User Interface thread should not get busy,
  • Users should be notified of the progress of the upload process.

 

We need to perform this upload in an asynchronous way, to do that we have implemented the following flow:

Flow Diagram

 

The most interesting parts of this flow implementation are:

Start Uploading file:

 /// <summary>
 /// Upload the file specified
 /// </summary>
 /// <param name="file"></param>
 public void UploadFile(FileInfo file)
 {
     File = file;
     UploadFileEx();
 }
 /// <summary>
 /// Start a request to upload a chunk of file.
 /// 
 /// Once the server is ready the WriteCallback method will be called (here we extract that chunk and
 /// add it to the request stream)
 /// </summary>
 private void UploadFileEx()
 {
     Status = FileUploadStatus.Uploading;
     long temp = FileLength - BytesUploaded;
     UriBuilder ub = new UriBuilder(_urlServer);
     bool complete = temp <= ChunkSize;
     ub.Query = string.Format("{3}filename={0}&StartByte={1}&Complete={2}", File.Name, BytesUploaded, complete, string.IsNullOrEmpty(ub.Query) ? "" : ub.Query.Remove(0, 1) + "&");
     HttpWebRequest webrequest = (HttpWebRequest)WebRequest.Create(ub.Uri);
     webrequest.Method = "POST";
     // Begins an asynchronous request for a Stream object to use to write data.
     // The asynchronous callback method uses the EndGetRequestStream method to return the actual stream.
     // This means
     webrequest.BeginGetRequestStream(new AsyncCallback(WriteCallback), webrequest);
 }

Write the current chunk of file to server, wait for response send the next chunk

 /// <summary>
 /// Request to write...
 /// - Get the current chunk of file to send.
 /// - Add it to the requestStream
 /// - Send an event to notify listeners that the chunk of file has been sent (e.g. progress bar on the UI)
 /// - Send it
 /// - Whenever the operation is ready call the ReadCallback method (when this method
 /// is called we know that the chunk of file has arrived succesfully to the server,
 /// it's time to process the next chunk).
 /// </summary>
 /// <param name="asynchronousResult"></param>
 private void WriteCallback(IAsyncResult asynchronousResult)
 {
     HttpWebRequest webrequest = (HttpWebRequest)asynchronousResult.AsyncState;
     // End the operation.
     // Here we obtain the stream to write the request
     Stream requestStream = webrequest.EndGetRequestStream(asynchronousResult);
     byte[] buffer = new Byte[4096];
     int bytesRead = 0;
     int tempTotal = 0;
     Stream fileStream = File.OpenRead();
     // Get the current chunk of file to send.
     fileStream.Position = BytesUploaded;
     while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0 && tempTotal + bytesRead < ChunkSize)
     {
         // Feed the request Stream with the chunk of file we want to send
         requestStream.Write(buffer, 0, bytesRead);
         requestStream.Flush();
         BytesUploaded += bytesRead;
         tempTotal += bytesRead;
         // Send an event to notify listeners that the chunk of file has been sent (e.g. progress bar on the UI)
         if (UploadProgressChanged != null)
         {
             int percent = (int)(((double)BytesUploaded / (double)FileLength) * 100);
             UploadProgressChangedEventArgs args = new UploadProgressChangedEventArgs(percent, bytesRead, BytesUploaded, FileLength, File.Name);
             this.Dispatcher.BeginInvoke(delegate()
             {
                 UploadProgressChanged(this, args);
             });
         }
     }
     fileStream.Close();
     // Chunk of data sent
     requestStream.Close();
     /// Whenever the operation is ready call the ReadCallback method (when this method
     /// is called we know that the chunk of file has arrived succesfully to the server,
     /// it's time to process the next chunk).
     webrequest.BeginGetResponse(new AsyncCallback(ReadCallback), webrequest);
 }

 File chunk sent, acknowledge from the server received, let’s send the next chunk (or jump to the next file to upload).

 /// <summary>
 /// File chunk send to the server and read successfully ? 
 /// Yes... do we still have chunks of files to send?
 /// Yes... let's send them
 /// No... Change the FileUploadStatus to Complete (this will fire a check 
 /// to start the upload of the next file in the qeue)
 /// when the the web reponse is recieved we can send a new part of the file
 /// </summary>
 /// <param name="asynchronousResult"></param>
 private void ReadCallback(IAsyncResult asynchronousResult)
 {
     // We could use this response to handle errors (e.g. corrupted packet or...)
     /*
   HttpWebRequest webrequest = (HttpWebRequest)asynchronousResult.AsyncState;
   HttpWebResponse response = (HttpWebResponse)webrequest.EndGetResponse(asynchronousResult);
   StreamReader reader = new StreamReader(response.GetResponseStream());
  string responsestring = reader.ReadToEnd();
  reader.Close();
    */
      // Yes... do we still have chunks of files to send?
      if (BytesUploaded < FileLength)
          // Yes... let's send them
          UploadFileEx();
      else
      {
          // No... Change the FileUploadStatus to Complete (this will fire a check 
          // to start the upload of the next file in the qeue)
          Status = FileUploadStatus.Complete;
      }
  }

On the server side we use a custom HTTP Handler to process the petitions. You can download the full sample here.

About the sample, it’s more oriented to understand and learn how this type of upload works, for the sake of simplicity it doesn’t contain code to check errors, or validate the uploaded files, in the next section you can find good links about fully implemented file upload applications.

 

What’s next?

We have checked in this article that enabling desktop file drag and drop it’s quite easy, but once you’ve got the files, comes the hard part, process the content of that files. Here you have some tips for common drag and drop scenarios:

- File Uploading: If you want to further develop this sample, or make your own research, there are some useful links that can help you:

  • File Upload in Silverlight : Basics about how to upload a file using SL.
  • Simple Silverlight Uploader with Progress Bar using HttpRequest or WCF: complete sample about how to perform an async upload via HTTPRequest or WCF.
  • Codeplex Silverlight file upload: Excellent uploading project, supports multi upload, rich error handling, worth to download and play with the code.

- Processing pictures and video: Dragging and dropping multimedia files to our application is great, Silverlight has great support for them, you can for instance: let an user quick build an image catalog, or play a webcast / video by dragging from his desktop.

  • Silverlight 4’s new Drag and Drop Support: Simple and useful sample, drop a picture and shows it in a picture catalog.

- Processing Excel files: If you are a LOB app developer, this request will be quite familiar for you… the complicated part of this operation comes when you have to parse / understand the excel content. Reading this type of file is quite harder than generating an output in excel, things that you can try:

  • Simple solution… just support CSV format. Reading CSV format is simplifies a lot the parsing scenario, on the other hand users will complain and will ask for real Excel processing, about CSV reading.
  • There is an open source project available (ExcelReader) that is relatively easy to port to Silverlight, it’s able to read both binary and xml format. This can be a good starting point, but it would be a good idea to limit the file types supported to 2007 format, and I would get good knowledge of Open XML format and LINQ to XML, just to extend the library if needed and be able to fix any possible bug.
  • Another possibility is doing all yourself (pain in the neck?), grab CSharpZLib, unzip the xlsx file and use LINQ To XML to read the file. Something that could help us to reduce the complexity is to make use of Excel Tables (by using this you would separate layout from data, and ensure that the data you are going to read is tabular).
  • If you have budget, something to check are third parties components (e.g. infragistics, ….), a good practice is to download the trials of that products and check if it really fits your needs, then decide whether to purchase it or not.
  • If you are application is going to be used by very few users and all that user are going to have the same environment, you can try the new SL 4 com interop.Your app will need to ask for elevated permissions, and it can be as well something prone to issues (e.g. user changes Excel version, or removes it… your application relies on the software installed on a client machine).

For sure you will have new exciting ideas, let’s use the comments section to further discuss them.


Subscribe

Comments

  • -_-

    RE: Silverlight 4 Desktop Drag and Drop + Silver Sky


    posted by Valentin Stoychev on Jan 18, 2010 17:50
    Nice article! Just to mention that this functionality is now available with Telerik Upload for Silverlight. We are working on enabling the drag/drop from desktop with the official launch of SL4.
  • -_-

    RE: Silverlight 4 Desktop Drag and Drop + Silver Sky


    posted by Braulio Diez on Jan 18, 2010 22:23

    Hi Valentin,

    Telerik controls are a good option if you are developing a commercial product. You can always download a trial check the functionallity and if  it fits your needs, compare development cost + bug fixing of a custom component versus purchase it in a box, well... and spend some time convincing your boss of investing on a custom componente (that's the fun part).

    A good excercise as well is to learn a bit about some inners, develop a POC, and then figure out... how the hell have manage this Telerik guys to make such a cool control? :)

     

     

  • -_-

    RE: Silverlight 4 Desktop Drag and Drop + Silver Sky


    posted by Shai Petel on Mar 03, 2010 21:36

    Eh... Sorry to be a party pooper... But what about dragging file(s) from my browser to my desktop?

    Going the other way around, to perform the download is something that is missing and no one (except for us and our customers) seems to care... :(

    Does anyone have any idea if this feature will be coming?

  • -_-

    RE: Silverlight 4 Desktop Drag and Drop + Silver Sky


    posted by Braulio on Apr 20, 2010 13:59

    Thanks for the feedback, about drag and drop the other way around, I guess is not implemented just more because a security concern, user could just drag and drop a virus... I know with save as dialog you can do the same but... the user will know that he is downloading something to his hard disk.

    What do you think?

  • -_-

    RE: Silverlight 4 Desktop Drag and Drop + Silver Sky


    posted by rory@cnet.ie on May 17, 2010 14:40
    Its a lot easier and quicker to map a drive to your SkyDrive Account,
  • rameshbyna

    Re: Silverlight Desktop Drag and Drop + Silver Sky


    posted by rameshbyna on Jun 16, 2011 09:01

    Nice one,but am face one problem i.e when ever am drag the file from desktop to application,

    the browser catch the file, not application dataGrid(my requirement)

    how i can solve the problem plz help me...am setting

    Drop="DataGridDragDropTarget_Drop" 
                                            AllowDrop="True" also

    the drop event does't fire when am tring to drop item to Datagrid from desktop,

    browser catch the file and show the information....


Add Comment

Login to comment:
  *      *