How to use .NET WebClient synchronously and still receive progress updates

Recently, I had to implement our custom package deployment mechanism for our UI framework where packages are hosted on a web-accessible URI. During the download process, the UI should display a progress bar which updates the user on the progress the download. After download is complete, the client performs a few more steps including

  • checking file integrity against its md5 hash
  • expanding the compressed file into the destination
  • cleaning up

The implementation uses TPL; for each package that user chose to upgrade, a task is created which performs the steps above. During this process, the UI displays a progress of each task.

The download portion of the process is implemented using WebClient.DownloadFile method. It’s short and sweet, hiding most of the complexity of opening a connection, getting a response stream and reading it one chunk at a time. But its simplicity comes a shortcoming: during download the thread doing the downloading is blocked an no progress is reported, so downloading a large file would cause the user to think that the system froze, because even though the wait spinner is spinning, the progress bar is stuck. What I want is to get updates on the progress of my download while waiting for download to complete.

The WebClient class has convenient DownloadProgressChanged event which seems like a perfect candidate to help me here, so I attached a delegate to it, but it was never called. Upon careful reading of the documentation, I learned that I must use the asynchronous version of the DownloadFile mehtod, DownloadFileAsync to receive any progress updates.

While I could switch my code around to accommodate new asynchronous calling pattern, I did not want to drastically redesign my existing code. It was already relying on the TPL for asynchrony, adding another asynchronous mechanism would add more complexity to my code. So I decided to try to keep the method that deals with WebClient synchronous, but still receive updates. To do this, I would need to call the DownloadFileAsync method, but then block my thread until download completes or fails. Luckily, WebClient has the DownloadFileCompleted event which would come in handy here. Since I would block my thread after calling the DownloadFileAsync method, when the DownloadFileCompleted fires, I would unblock my calling thread. Here’s the code that implements this solution

public void DownloadFile(Uri uri, string desintaion)
  using(var wc = new WebClient())
    wc.DownloadProgressChanged += HandleDownloadProgress;
    wc.DownloadFileCompleted += HandleDownloadComplete;

    var syncObject = new Object();
       wc.DownloadFileAsync(sourceUri, destination, syncObject);
       //This would block the thread until download completes

  //Do more stuff after download was complete

public void HandleDownloadComplete(object sender, AsyncCompletedEventArgs e)
      //releases blocked thread

public void HandleDownloadProgress(object sender, DownloadProgressChangedEventArgs args)
  //Process progress updates here

At the end, I still use WebClient synchronously without losing the benefits that come with its asynchronous usage.

Happy Downloading!


5 thoughts on “How to use .NET WebClient synchronously and still receive progress updates

  1. Tomasz Jagusz

    I was searching over the internet for proper WebClient usage. Everyone is just adding `using`block but You went one step further and You’ve added lock and monitor. Now this works as it should!
    Thanks for sharing.

    I have one question related to WebClient and Your code. I’d like to get real file name from Content-Disposition header before I start saving file. How can I do that before You call `wc.DownloadFileAsync`? Can WebClient get only headers?

  2. Hel Thanatos

    public static void DownloadFile(Uri URL, string musicFilePath)
    WebClient myWebClient = new WebClient();

    myWebClient.DownloadFileAsync(URL, musicFilePath);

    myWebClient.DownloadProgressChanged += DownloadProgressChanged;
    myWebClient.DownloadFileCompleted += DownloadFileCompleted;


    public static void DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
    downloadCompleted = true;
    downloadProgress = 0;
    //Do Stuff Here

    public static void DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
    downloadProgress = e.ProgressPercentage;
    //Console.WriteLine(“{0} downloaded {1} of {2} bytes. {3} % complete…”,
    // (string)e.UserState,
    // e.BytesReceived,
    // e.TotalBytesToReceive,
    // e.ProgressPercentage);

    Could do that?

  3. Benjamin Philipp

    Yesss! This is beautiful, exactly what I was looking for!
    Thanks a million!

    However, there are some typos/mistakes:
    6: “wc.DownloadFileCOmpleted” should be “wc.DownloadFileCompleted”
    8: “var syncObj” should be called “syncObject” (or subsequent references altered)
    20: “AsyncCompletedEventArgs args” should be called “e” (or subsequent references altered)

    Thanks again and have a nice day! 🙂


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s