Andrew Roberts
Andrew Roberts

Reputation: 1120

Using DownloadDataAsync with Cookie aware Webclient to get Update Progress

I am using a Webclient with a CookieContainer to download data from a webservice (Sharepoint).

I want to DownloadDataAsync so that I download multiple documents and update a single Progress Bar for each Document as it downloads. The non async version - DownloadData does not send Progress updates.

  1. How do I get the Async version to wait at the doc.BinaryData = xx line before moving on the next Document?
  2. How do I get the byte array from the DownloadFileCompleted event?
  3. How can apply the changes to the progressbar without using DoEvents?

    partial class Form() { void Main() { List urls= new List(); //urls.Add("xxxxxxx"); //get them from somewhere

        for (int i = 0; i < urls.Count; i++)
        {
            var doc = new Document();
            doc.BinaryData = DocumentAsArray(urls.ElementAt(i));
            entities.AddToDocument(doc);
        }
    }
    
    public byte[] DocumentAsArray(string URL)
    {
        byte[] @return = null;
        using (CookieAwareWebClient client = new CookieAwareWebClient())
        {
            client.CookieContainer = spAuthentication.CookieContainer;
    
            // Assign the events to capture the progress percentage
            client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
            client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
    
            client.DownloadDataAsync(new Uri(URL));
        }
    
        return @return;
    }
    
    void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
    {
        progressBarControlFile.Position = e.ProgressPercentage;
        var newPc = string.Format("Downloaded {0} %", e.ProgressPercentage);
    
        labelControlFile.Text = newPc;
        Application.DoEvents();
    }
    
    void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
    {
        progressBarControlFile.Position = progressBarControlFile.Properties.Maximum;
        labelControlFile.Text = string.Format("{0} %", progressBarControlFile.Properties.Maximum);
    
        Application.DoEvents();
    }
    

    }

    public class CookieAwareWebClient : WebClient { public CookieContainer CookieContainer { get; set; }

    protected override WebRequest GetWebRequest(Uri address)
    {
        WebRequest request = base.GetWebRequest(address);
    
        var webRequest = request as HttpWebRequest;
        if (webRequest != null)
        {
            webRequest.CookieContainer = this.CookieContainer;
            webRequest.KeepAlive = false;
        }
    
        return request;
    }
    

    } }

Upvotes: 0

Views: 1260

Answers (1)

Andreas Reiff
Andreas Reiff

Reputation: 8404

  1. You have to wait for the DownloadFileCompleted event. You can either set a volatile bool on which you poll or do this with an event. There are only minor differences performance-wise, but generally probably events are the cleaner solution (I prefer bool-polling). You might in your case actually want to wait for this to happen at the end of DocumentAsArray.
  2. http://msdn.microsoft.com/en-us/library/ms144190.aspx The downloaded data is available in the Result property.
  3. I don't know the DoEvents methods - if I understand correctly what you want to do, you want to update your UI? You would have to call InvokeEx (at least that is the easiest and.. only way I know). Look at this great :) article http://www.andreas-reiff.de/2011/06/accessing-a-winformwpf-control-from-another-thread-through-invoke/ . Alternatively, stay at stackoverflow and look at Best Way to Invoke Any Cross-Threaded Code?.

So to recap, if you just want the async to get the progress-update, simply change your DocumentAsArray function to wait for the DownloadFileCompleted event as outlined in 1. Then all you have to do is to be careful that you do not call your UI code from another thread - you should get an exception in Debug telling you not to do so (it runs fine in Release - most of the time). So use the InvokeEx-call.

Upvotes: 1

Related Questions