Search code examples
c#download

Wait for async download to be completed


This is my whole source code. I want to download x files (lets assume we now have 2 files we want to download).

My main Point is to wait for all Downloads to complete and then proceed. I tried several points.

Task.WhenAll(List) -> Not working in my way of implementation

await -> impossibly in MainWindow function without async.

Workflow should be the following. Download started -> All Files Downloaded -> Show the Debug.Print

Atm: Source Code Output is -> All Downloads done "Download completed" "Download completed"

Should be: "Download completed" "Download completed" All Downloads done

Working for hours and no tiny step further to solve the problem. It is a WPF Application.

    private volatile bool _download_completed;
    public bool DownloadCompleted { get { return _download_completed; } }


    public MainWindow()
    {

        InitializeComponent();
        ProcessService.FillProcessData();
        ProcessService.CloseAllProcesses();
        DownloadHelper.AddDownloadFiles();

        foreach(KeyValuePair<string,Uri> file in DownloadHelper.DownloadFiles)
        {
            DownloadFile(file.Key, file.Value);
        }
        Debug.Print("All Downloads done.");

    }

  private void DownloadFile(string Filename, Uri Uri)
    {

        Console.WriteLine(Filename + " " + Uri);
        _download_completed = false;
        WebClient client = new WebClient();
        client.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadFileCompleted);
        client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgressCallback);
        client.DownloadFileTaskAsync(Uri, Filename);

    }

private void DownloadProgressCallback(object sender, DownloadProgressChangedEventArgs e)
    {
        this.Dispatcher.Invoke(() => {

            progressBar.Value = e.ProgressPercentage;
            StatusLabel.Content = e.ProgressPercentage + " % complete... ( " + e.BytesReceived + " / " + e.TotalBytesToReceive + ")";
        });
    }

    private void DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
    {
        Console.WriteLine("Download completed");
        this.Dispatcher.Invoke(() =>
        {
            StatusLabel.Content = "Download Finished";
        });

        _download_completed = true;

    }


}

Solution

  • You need to use tasks to achieve this

    First make your download function async:

        private async TaskDownloadFile(string Filename, Uri Uri)
            {
        
                Console.WriteLine(Filename + " " + Uri);
                _download_completed = false;
                WebClient client = new WebClient();
                client.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadFileCompleted);
                client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgressCallback);
             
          await   client.DownloadFileTaskAsync(Uri, Filename);
        
            }
    

    Make the method async and await the DownloadFileTaskAsync method.

    I would suggest to remove the events for DownloadFileCompeted and DownloadProgressChanged if you will do multiple downloads they don't make much sense.

    Now that you have made the method async do the following:

    var file1DownloadTask = TaskDownloadFile("filename","url");
    var file2DownloadTask = TaskDownloadFile("filename","url");
    

    At this point your have two tasks:

    You can use the Task.WaitAll(list_of_taks_goes_here)

    Task.WaitAll(file1DownloadTask,file2DownloadTask )
    

    The wait all method will wait for all the tasks to finished so the next line of code is called after those two tasks have finished:

    Then you can do change the UI or whatever you need to do after the Task.WaitAll line:

    You could also do the following:

    await TaskDownloadFile("filename","url");
    await TaskDownloadFile("filename2","url");
    

    Which will wait for the downloads to finish before moving on to next line but this way you have to know all the files at the time your write the code, with the first method you could have a list of files from which you could build the tasks and pass them to Task.WaitAll.