1

i use some code to download mutiple files, which works good. while downloading a lot of files, i would like to be able to cancel them. Besides the UI with a cancel button i use as global variables:

private WebClient client = null; 
private CancellationToken cts = new CancellationToken();

Within the download function i use:

// The Object SourceId is only used to be able 
// to pass the ID (index) of datasource to callback functions
var current = new SourceId();
current.Id = sourceList.IndexOf(current); 
cts = new CancellationToken();
//i tried to use (cancellationToken.Register(() => webClient.CancelAsync())
using (client = new WebClient())
using (cts.Register(() => wc_Cancel()))
{
   client.DownloadProgressChanged += wc_DownloadProgressChanged;
   client.DownloadFileCompleted += wc_DownloadFileCompleted;
   client.DownloadFileAsync(new Uri(driver.Filelink), targetFile, current);
}

"current" is an object, which contains the source ID of the original Datasource, which i use within wc_DownloadProgressChanged and wc_DownloadFileCompleted to determine the correct Data from my datasource.

Within the function wc_DownloadFileCompleted i use if (e.Cancelled) to determine if the download was canceled.

I try to cancel a specific Download. The cancelDownload Function is called by a button after getting the corresponding source Id.

How can i cancel one specificed async download, which means how to pass the needed argument for cancel function ??

    private void cancelDownload(int id)
    {
        // Here i would like to send the ID to cancel to specific download
        // and wait for it to finish. How can i do this ?
        wc_Cancel();
    }

    public virtual void wc_Cancel()
    {
        if (this.client != null)
            this.client.CancelAsync();
    }

How can i achieve that ?

biohell
  • 304
  • 3
  • 14

1 Answers1

1

finally i found a solution for this. For those with the same problem here is my solution.

The use of the CancellationToken is not necessary to get this work. Also the functions cancelDownload and wc_Cancel are not needed. I use a download class, with all needed informations like FileLink, Name etc. I did expand that class with these properties:

// HelperClass to pass for callbacks
public class DownId
{
   public int Id { get; set; }
}

// DownloadClass with all needed properties. Should be expanded for your needs.
public class Downloads
{
    public Driver()
    {
    }
    public string Title { get; set; }
    public string Filelink { get; set; }
    public string FileName { get; set; }
    public bool IsCanceling { get; set; }
    public WebClient Client { get; set; }
    public int Retry { get; set; }
    public int Progress { get; set; }
    public long valBytesRec { get; set; }
    public long valBytesTotal { get; set; }
}

The Class now contains a property for the WebClient and bool parameter for canceling. A global Variable downloadlist of type List<Downloads> is used to access the needed properties within all functions.

In the download function I use a parameter currentdriver, which is the indexnumber of the current download within the list (used as paramter for download):

DownId currentSourceId = new DownId();
currentSourceId.Id = currentdriver;
using (downloadlist[current].Client = new WebClient())
{
    downloadlist[current].Client.DownloadProgressChanged += wc_DownloadProgressChanged;
    downloadlist[current].Client.DownloadFileCompleted += wc_DownloadFileCompleted;
    downloadlist[current].Client.DownloadFileAsync(new Uri(driver.Filelink), targetFile, currentSourceId);
}

To be able to pass the currentdrive to the DownloadFileAsync, it must be used within an object. for this purpose is the DownId Class is used. This way every download will use its own WebClient, which can be access quite easy.

For canceling it is only necessary to set the IsCanceling property within the button_Click event handler:

driverlist[currentdriver].IsCanceling = true;

In the wc_DownloadProgressChanged, I use this code to determine, if Canceling is necessary:

var currentDriver = e.UserState as DownId;
if (downloadlist[current].IsCanceling)
{              
    downloadlist[current].IsCanceling = false;
    downloadlist[current].Client.CancelAsync();
}
else
{
  //Update progress etc.
  // beware of not using UI Elements here, caused UI Lags
  // and can be a problem within threads
  // if necessary use  Invoke(new MethodInvoker(delegate {  ...}));
}

Hope this helps.. I did search for quite a while to find a solution.

VMAtm
  • 27,943
  • 17
  • 79
  • 125
biohell
  • 304
  • 3
  • 14