Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asynchronous File Download with Progress Bar

I am attempting to have a progress bar's progress change as the WebClient download progress changes. This code still downloads the file yet when I call startDownload() the window freezes as it downloads the file. I would like for the user to be able to see the progress change as the splash screen loads. Is there any way to fix this so that the user can see the progress of progressBar2 change?

private void startDownload()
{
    WebClient client = new WebClient();
    client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
    client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
    client.DownloadFileAsync(new Uri("http://joshua-ferrara.com/luahelper/lua.syn"), @"C:\LUAHelper\Syntax Files\lua.syn");
}
void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
    double bytesIn = double.Parse(e.BytesReceived.ToString());
    double totalBytes = double.Parse(e.TotalBytesToReceive.ToString());
    double percentage = bytesIn / totalBytes * 100;
    label2.Text = "Downloaded " + e.BytesReceived + " of " + e.TotalBytesToReceive;
    progressBar1.Value = int.Parse(Math.Truncate(percentage).ToString());
}
void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
    label2.Text = "Completed";
}
like image 604
Josh Ferrara Avatar asked Feb 27 '12 02:02

Josh Ferrara


3 Answers

UI Thread will be freezed when you click startDownload(). If you don't want get form freezed, you use startDownload() in another thread and make progress updating in cross-threaded. One way,

private void startDownload()
{
    Thread thread = new Thread(() => {
          WebClient client = new WebClient();
          client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
          client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
          client.DownloadFileAsync(new Uri("http://joshua-ferrara.com/luahelper/lua.syn"), @"C:\LUAHelper\Syntax Files\lua.syn");
    });
    thread.Start();
}
void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
    this.BeginInvoke((MethodInvoker) delegate {
        double bytesIn = double.Parse(e.BytesReceived.ToString());
        double totalBytes = double.Parse(e.TotalBytesToReceive.ToString());
        double percentage = bytesIn / totalBytes * 100;
        label2.Text = "Downloaded " + e.BytesReceived + " of " + e.TotalBytesToReceive;
        progressBar1.Value = int.Parse(Math.Truncate(percentage).ToString());
    });
}
void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
    this.BeginInvoke((MethodInvoker) delegate {
         label2.Text = "Completed";
    }); 
}

Read more multi-threading in Google like this http://msdn.microsoft.com/en-us/library/ms951089.aspx

-Fixed missing close ); to the bgThread declaration

like image 110
Peter PAD Avatar answered Nov 14 '22 09:11

Peter PAD


You should call startDownload() from the UI thread. The whole idea of WebClient.DownloadFileAsync() is that it will spawn a worker thread for you automatically without blocking the calling thread. In startDownload(), you specified callbacks that modify controls which I assume were created by the UI thread. Thus if you call startDownload() from a background thread it will cause problems, because a thread can only modify UI elements it created.

The way it is supposed to work is you call startDownload() from the UI thread, startDownload() as you defined it sets up event call backs that are handled by the UI thread. It then starts the download asynchronously and returns immediately. The UI thread will be notified when the progress changes and the code responsible for updating the progress bar control will execute on the UI thread, and there shouldn't be any problems.

like image 17
Bojin Li Avatar answered Nov 14 '22 08:11

Bojin Li


 public class ProgressEventArgsEx
{
    public int Percentage { get; set; }
    public string Text { get; set; }
}
public async static Task<string> DownloadStraingAsyncronous(string url, IProgress<ProgressEventArgsEx> progress)
{
    WebClient c = new WebClient();
    byte[] buffer = new byte[1024];
    var bytes = 0;
    var all = String.Empty;
    using (var stream = await c.OpenReadTaskAsync(url))
    {
        int total = -1;
        Int32.TryParse(c.ResponseHeaders[HttpRequestHeader.ContentLength], out total);
        for (; ; )
        {
            int len = await stream.ReadAsync(buffer, 0, buffer.Length);
            if (len == 0)
                break;
            string text = c.Encoding.GetString(buffer, 0, len);

            bytes += len;
            all += text;
            if (progress != null)
            {
                var args = new ProgressEventArgsEx();
                args.Percentage = (total <= 0 ? 0 : (100 * bytes) / total);
                progress.Report(args);
            }
        }
    }
    return all;
}
// Sample
private async void Bttn_Click(object sender, RoutedEventArgs e)
{
    //construct Progress<T>, passing ReportProgress as the Action<T> 
    var progressIndicator = new Progress<ProgressEventArgsEx>(ReportProgress);
    await TaskLoader.DownloadStraingAsyncronous(tbx.Text, progressIndicator);
}
private void ReportProgress(ProgressEventArgsEx args)
{
    this.statusText.Text = args.Text + " " + args.Percentage;
}
like image 2
guest Avatar answered Nov 14 '22 09:11

guest