I'm working on a download manager and need the ability to cancel a specific download in progress. This issue I'm having is that CancelAsync doesn't accept any arguments and I can't seem to figure out how to cancel a certain download when multiple downloads are in progress - when I do click on a cancel button (DataGridViewButton) in the DataGridView - it cancels the last download that was started. I understand why that is, but I'm not yet able to find a way around it.
Code:
Imports System.Net
Imports System.IO
Imports System.ComponentModel
Imports System.Threading
Imports System.Collections.Concurrent
Public Class Form1
Dim webClient As WebClient
Dim url1 As String = "http://87.76.16.10/test100.zip"
Dim url2 As String = "http://chicago.futurehosting.com/test100.zip"
Dim url3 As String = "http://london.futurehosting.com/test100.zip"
Dim url4 As String = "http://lax.futurehosting.com/test100.zip"
Dim url5 As String = "http://mia.futurehosting.com/test100.zip"
Dim path As String = Directory.GetCurrentDirectory()
Const MaxClients As Integer = 4
Dim ClientQueue As New BlockingCollection(Of WebClient)
Dim UrlQueue As New Queue(Of String)()
Dim downloadThread As Thread
Dim url As String
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim ClientQueue As New BlockingCollection(Of WebClient)(MaxClients)
DataGridView1.BackgroundColor = Color.White
DataGridView1.RowHeadersVisible = False
DataGridView1.AllowUserToAddRows = False
Dim column1 As New DataGridViewTextBoxColumn()
column1.HeaderText = "Current Downloads"
column1.Name = "Column1"
column1.Width = 215
DataGridView1.Columns.Add(column1)
Dim column2 As New DataGridViewTextBoxColumn()
column2.HeaderText = "Percent Complete"
column2.Name = "Column2"
column2.Width = 100
DataGridView1.Columns.Add(column2)
Dim column3 As New DataGridViewButtonColumn()
column3.HeaderText = ""
column3.Name = "Column3"
column3.Width = 100
DataGridView1.Columns.Add(column3)
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
UrlQueue.Clear()
While ClientQueue.Count > 0
ClientQueue.TryTake(webclient)
End While
For i As Integer = 0 To MaxClients - 1
Dim cli As New WebClient()
AddHandler cli.DownloadFileCompleted, AddressOf DownloadFileCompleted
AddHandler cli.DownloadProgressChanged, AddressOf DownloadProgressChanged
ClientQueue.Add(cli)
Next
If CheckBox1.Checked = True Then
UrlQueue.Enqueue(url1)
CheckBox1.Checked = False
End If
If CheckBox2.Checked = True Then
UrlQueue.Enqueue(url2)
CheckBox2.Checked = False
End If
If CheckBox3.Checked = True Then
UrlQueue.Enqueue(url3)
CheckBox3.Checked = False
End If
If CheckBox4.Checked = True Then
UrlQueue.Enqueue(url4)
CheckBox4.Checked = False
End If
If CheckBox5.Checked = True Then
UrlQueue.Enqueue(url5)
CheckBox5.Checked = False
End If
downloadThread = New Thread(AddressOf downloadQueue)
downloadThread.IsBackground = True
downloadThread.Start()
End Sub
Public Sub downloadQueue()
While UrlQueue.Count > 0
webclient = ClientQueue.Take()
Dim url As String = UrlQueue.Dequeue()
Dim programInfo As Program = GetProgram(url)
webclient.DownloadFileAsync(New Uri(url), programInfo.sFileName, New DownloadArgs(url, programInfo.sFileName, webclient))
addValue(programInfo.programName)
End While
End Sub
Public Sub addValue(text As String)
If DataGridView1.InvokeRequired Then
DataGridView1.Invoke(New Action(Of String)(AddressOf addValue), text)
Exit Sub
End If
Dim n As Integer = DataGridView1.Rows.Add()
DataGridView1.Rows.Item(n).Cells(0).Value = text
End Sub
Public Sub DownloadProgressChanged(sender As Object, e As DownloadProgressChangedEventArgs)
Dim args As DownloadArgs = DirectCast(e.UserState, DownloadArgs)
Dim url As String = args.Url
Dim programInfo As Program = GetProgram(url)
Dim programName As String = programInfo.programName
Dim percent As String = e.ProgressPercentage.ToString
For Rowindex = DataGridView1.Rows.Count - 1 To 0 Step -1
If DataGridView1.Rows(Rowindex).Cells(0).Value = programName Then
DataGridView1.Rows.Item(Rowindex).Cells(1).Value = percent
DataGridView1.Rows.Item(Rowindex).Cells(2).Value = "Cancel"
End If
Next
End Sub
Public Sub DownloadFileCompleted(sender As Object, e As AsyncCompletedEventArgs)
Dim args As DownloadArgs = DirectCast(e.UserState, DownloadArgs)
Dim url As String = args.Url
Dim programInfo As Program = GetProgram(url)
Dim programName As String = programInfo.programName
Dim savedName As String = programInfo.sFileName
Dim file As New FileInfo(savedName)
Dim sizeInBytes As Long = file.Length
If e.Cancelled = True Then
For Rowindex = DataGridView1.Rows.Count - 1 To 0 Step -1
If DataGridView1.Rows(Rowindex).Cells(0).Value = programName Then
DataGridView1.Rows.Item(Rowindex).Cells(1).Value = "Cancelled"
DataGridView1.Rows.Item(Rowindex).Cells(2).Value = ""
End If
Next
End If
ClientQueue.Add(args.Client)
End Sub
'Handles click events for Cancel DataGridViewButtons
Private Sub CellContentClick(ByVal sender As DataGridView, _
ByVal e As DataGridViewCellEventArgs) _
Handles DataGridView1.CellContentClick
If e.RowIndex >= 0 AndAlso sender.Columns(e.ColumnIndex).GetType() = GetType(DataGridViewButtonColumn) Then
If DataGridView1.Rows.Item(e.RowIndex).Cells(2).Value = "Cancel" Then
webclient.CancelAsync()
'webclient.Dispose()
Else
MessageBox.Show("This download has already been cancelled")
'TODO: hide button - appears to require a wall of code, can't simply use DataGridView1.Rows.Item(e.RowIndex).Cells(2).Visible = False
End If
End If
End Sub
Public Structure Program
Public sFileName As String
Public programName As String
End Structure
Public Function GetProgram(ByVal url As String) As Program
Dim prog As Program = Nothing
prog.sFileName = vbNullString
prog.programName = vbNullString
If url = url1 Then
prog.sFileName = path & "\file1.zip"
prog.programName = "File 1"
End If
If url = url2 Then
prog.sFileName = path & "\file2.zip"
prog.programName = "File 2"
End If
If url = url3 Then
prog.sFileName = path & "\file3.zip"
prog.programName = "File 3"
End If
If url = url4 Then
prog.sFileName = path & "\file4.zip"
prog.programName = "File 4"
End If
If url = url5 Then
prog.sFileName = path & "\file5.zip"
prog.programName = "File 5"
End If
Return prog
End Function
End Class
Public Class DownloadArgs
Public ReadOnly Url As String
Public ReadOnly Filename As String
Public ReadOnly Client As WebClient
Public Sub New(u As String, f As String, c As WebClient)
Url = u
Filename = f
Client = c
End Sub
End Class