j0h4nn3s
j0h4nn3s

Reputation: 2076

Wait for download to complete in BackgroundWorker

I have a Dialog with a progressbar. A Backgroundworker should download two files (with the WebClient) and copy them to a specified location automatically, when the Dialog is shown. How can I wait for the files to be downloaded before copying the new files.

I have tried to do something with await, but I cant change the Backgroundworker to a async method. How can I wait in the worker for the download to complete?

Code to run the worker:

private void fmUpdateingDatabaseDialog_Shown(object sender, EventArgs e)
{
    device.Connect();
    lbInformation.Text = "uploading database to " + device.FriendlyName;
    device.Disconnect();

    worker.WorkerReportsProgress = true;
    worker.WorkerSupportsCancellation = true;
    worker.DoWork += new DoWorkEventHandler(worker_DoWork);
    worker.ProgressChanged += 
        new ProgressChangedEventHandler(worker_ProgressChanged);
    worker.RunWorkerCompleted += 
        new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
    worker.RunWorkerAsync();
}

Code in DoWork handler (paths are not empty in actual code):

private void worker_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = sender as BackgroundWorker;

    //download files temporary
    WebClient client = new WebClient();
    client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
    client.DownloadFileTaskAsync(new Uri(""), Path.Combine(tempPath + ""));

    WebClient client2 = new WebClient();
    client2.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client2_DownloadProgressChanged);
    client2.DownloadFileTaskAsync(new Uri(""), Path.Combine(tempPath + ""));

    //upload files to phone
    device.Connect();
        device.TransferContentToDevice(Path.Combine(tempPath+""), folder.Id, folder, true);
        device.TransferContentToDevice(Path.Combine(tempPath+""), folder.Id, folder, true);
    device.Disconnect();
}

Upvotes: 3

Views: 1459

Answers (2)

Stephen Cleary
Stephen Cleary

Reputation: 456917

You can either use the synchronous WebClient methods (e.g., DownloadFile instead of DownloadFileTaskAsync), or you can just use async/await directly instead of BackgroundWorker. In this case, you're doing mainly I/O, so async is a better fit than BackgroundWorker.

An async solution would look something like this:

private async void fmUpdateingDatabaseDialog_Shown(object sender, EventArgs e)
{
  device.Connect();
  lbInformation.Text = "uploading database to " + device.FriendlyName;
  device.Disconnect();

  var progress = new Progress<T>(data =>
  {
    // TODO: move worker_ProgressChanged code into here.
  });
  await DownloadAsync(progress);
  // TODO: move worker_RunWorkerCompleted code here.
}

private async Task DownloadAsync(IProgress<T> progress)
{
  //download files temporary
  WebClient client = new WebClient();
  client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
  await client.DownloadFileTaskAsync(new Uri(""), Path.Combine(tempPath + ""));

  WebClient client2 = new WebClient();
  client2.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client2_DownloadProgressChanged);
  await client2.DownloadFileTaskAsync(new Uri(""), Path.Combine(tempPath + ""));

  //upload files to phone
  // TODO: Check for Async versions of these methods that you can await.
  //  If there aren't any, consider using Task.Run.
  device.Connect();
  device.TransferContentToDevice(Path.Combine(tempPath+""), folder.Id, folder, true);
  device.TransferContentToDevice(Path.Combine(tempPath+""), folder.Id, folder, true);
  device.Disconnect();
}

Upvotes: 2

rory.ap
rory.ap

Reputation: 35318

You can use something like this, which uses a "wait and pulse" mechanism to delay the code until your download operation has completed:

var locker = new object(); 

Thread t = new Thread(new ThreadStart(() =>
{
    lock (locker)
    {
        //peform your downloading operation, and wait for it to finish.
        client.DownloadFileTaskAsync(new Uri(""), Path.Combine(tempPath + ""));
        while (/* not yet downloaded */) { }; 
        //inform the parent thread that the download has finished.
        Monitor.Pulse(locker);
    }
}));

t.Start();

lock(locker)
{
    Monitor.Wait(locker);
}

However, if you have the resources, I'd suggest refactoring your code to entirely use an async-await approach (thus obviating the background worker). The background worker is one of the legacy asynchronous approaches while the recommended approach is TAP.

See Stephen Cleary's answer for an example of how to do this.

Upvotes: 1

Related Questions