Jump to content
Xtreme .Net Talk

Recommended Posts

Posted

Hi

As described in the Topic I'm having some problems with changing the Progressbar (used in a statusstrip).

 

The DoWork sub calls a Downloadfunction, where while it downloads, the progress percentage is calculated and "reported".

Then the ProgressChanged Sub is called and the percentage is assigned to the progressbar value,

but nothing happens, not even an error.

 

I've copy-pasted an example from die MSDN and it worked perfectly,

but I can't find the differences which would mine not work.

 

Relevent Code:

    Public Sub GetAddons()
       LogEvent("Getting available ace addons...")

       THArgument.DownloadType = 0
       THArgument.Arg = "http://wowace.com/files"
       If Not main.bgwrk_Download.IsBusy Then
           main.bgwrk_Download.RunWorkerAsync(THArgument)
       End If
   End Sub

    Private Sub Downloader(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bgwrk_Download.DoWork

       Dim Result As DLTStrc = Nothing
       Select Case e.Argument.DownloadType
           Case 0
               Result.DownloadType = 0
               Result.Arg = GetIndex(e.Argument.Arg)
       End Select
       e.Result = Result
   End Sub

   Private Sub Download_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles bgwrk_Download.ProgressChanged
       ProgressDisplay.Value = e.ProgressPercentage
   End Sub

   Private Sub Download_Completed(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bgwrk_Download.RunWorkerCompleted
       ProcessDownload(e.Result)
       btn_ReloadIndex.Enabled = True

       ProgressDisplay.Visible = False
       ProgressLabel.Visible = False
       LogEvent("Got Addons")
       AddonListReady = True
   End Sub

    Public Function GetIndex(ByVal URL As String) As String
       Dim httpReq As Net.HttpWebRequest
       Dim httpRes As Net.HttpWebResponse
       Dim buffer(0) As Byte
       Dim temp As Short = 0
       Debug.Print("Getindex start: " & Date.Now.ToLongTimeString)
       httpReq = System.Net.WebRequest.Create(URL & "/descript.ion")
       httpRes = DirectCast(httpReq.GetResponse(), System.Net.WebResponse)

       temp = httpRes.GetResponseStream.ReadByte()
       While temp <> -1
           If UBound(buffer) Mod 2000 = 0 Then
               main.bgwrk_Download.ReportProgress(UBound(buffer) / httpRes.ContentLength * 100)
               Debug.Print(Int(UBound(buffer) / httpRes.ContentLength * 100))
           End If

           buffer(UBound(buffer)) = temp
           ReDim Preserve buffer(UBound(buffer) + 1)
           temp = httpRes.GetResponseStream.ReadByte()
       End While


       Debug.Print("Getindex done: " & Date.Now.ToLongTimeString)
       Return System.Text.Encoding.UTF8.GetString(buffer)
   End Function

 

Did I miss something?

Posted

You have to report progress using BackgroundWorker.ReportProgress(int) for there to be progress handled by the ProgressChanged event.

 

Take a look at the sample code here for some more info. Take a look here if you need some help translating the sample code from C# to VB.Net.

Posted (edited)

I'm reporting the progress in the GetIndex Sub:

main.bgwrk_Download.ReportProgress(UBound(buffer) / httpRes.ContentLength * 100)

 

And it sends the value correctly to the Download_ProgressChanged Sub (Debug.Print shows the progress correctly).

But as soon as I want to change controls on the main form within it, it doesn't work. (As in nothing happens)

 

I tried your example and it works as it should, but as with the example from MSDN,

I can't spot any differences which would cause my problem in my project.

 

[update]

 

I've tried the other way in your example as well (the delegate thing to add lines to the listbox).

But I'm having the exact problem again, nothing happens.

 

    Delegate Sub SetTextCallback(ByVal Message As String)
   Private Sub THLogEvent(ByVal Message As String)
       If main.lst_Log.InvokeRequired Then
           main.Invoke(New SetTextCallback(AddressOf LogEvent), Message)
       Else
           LogEvent(Message)
       End If
   End Sub

   Public Sub LogEvent(ByVal Message As String)
       main.lst_Log.Items.Add("[" & Date.Now.ToLongTimeString & "]: " & Message)
   End Sub

 

The sub THLogEvent is called when it is going through the GetIndex sub.

Edited by Arokh
  • Leaders
Posted

I'm sorry, but what do you mean when you say "change controls on the main form within it?"

 

Also, what happens if you use Debug.Print in the ProgressChanged event handler? (Or, in other words, does program control ever reach the event handler?)

[sIGPIC]e[/sIGPIC]
Posted

When the Sub Download_ProgressChanged is called and I try to do anything to the Controls,

adding text to the Listbox or changing the ProgressBar.Value, nothing happens.

This line has no effect:

ProgressDisplay.Value = e.ProgressPercentage

When I use Debug.Print(e.ProgressPercentage) instead the Value is printed as it should.

 

The example mskeel has given me does the same thing (as far I can see),

but there it succeeds and the Progressbar Control changes accordingly with the progressvalue it has been given.

 

 

I hope this clears things up.

  • Leaders
Posted

And you are calling Debug.Print inside the event handler? Do you have WorkerReportsProgress set to true? I copied and pasted your code, stripped out all the web functionality, changed a couple of things because I don't have your form with your controls, and I ended up with this:


[Color=Blue] Private Sub[/Color] Downloader([Color=Blue]ByVal [/Color]sender [Color=Blue]As [/Color]System.Object, [Color=Blue]ByVal [/Color]e [Color=Blue]As [/Color]System.ComponentModel.DoWorkEventArgs) [Color=Blue]Handles [/Color]BackgroundWorker1.DoWork
GetIndex()
[Color=Blue]End Sub[/Color]

[Color=Blue]Private Sub [/Color]Download_ProgressChanged([Color=Blue]ByVal [/Color]sender [Color=Blue]As [/Color]Object, [Color=Blue]ByVal [/Color]e [Color=Blue]As [/Color]System.ComponentModel.ProgressChangedEventArgs) [Color=Blue]Handles [/Color]BackgroundWorker1.ProgressChanged
ProgressBar1.Value = e.ProgressPercentage
[Color=Blue]End Sub[/Color]

[Color=Blue]Private Sub [/Color]Download_Completed([Color=Blue]ByVal [/Color]sender [Color=Blue]As [/Color]Object, [Color=Blue]ByVal [/Color]e [Color=Blue]As [/Color]System.ComponentModel.RunWorkerCompletedEventArgs) [Color=Blue]Handles [/Color]BackgroundWorker1.RunWorkerCompleted
Text = "COMPLETE"
[Color=Blue]End Sub[/Color]

[Color=Blue]Public Sub[/Color] GetIndex()
[Color=Blue]For [/Color]i [Color=Blue]As[/Color] Integer = 0 [Color=Blue]To [/Color]99
BackgroundWorker1.ReportProgress(i)
System.Threading.Thread.Sleep(100)
[Color=Blue]Next
End Sub[/Color]
[/Code]

It looks pretty different, but the multi-threading aspect is identical, and it works just fine. Perhaps you have a property set wrong or an event handler that you forgot to attach, or something along those lines.

[sIGPIC]e[/sIGPIC]
Posted

*confused*

I just found the reason its not working:

I have the Sub GetIndex in a seperate module,

as soon as I copy the sub into the windowsform (with the subs from the Backgroundworker) it works.

 

What I don't understand is why.

Posted (edited)

UI thread marshalling?

 

As a guess I would say this strange behaviour might be caused by the event not being raised on the GUI thread. I'd be interested to know if your original code works with the following event handler:

 

Private Sub Download_ProgressChanged( _
     ByVal sender As Object, _
     ByVal e As System.ComponentModel.ProgressChangedEventArgs _
   ) Handles bgwrk_Download.ProgressChanged

   If Me.InvokeRequired Then
       Dim evHandler As ProgressChangedEventHandler = AddressOf Download_ProgressChanged
       Me.Invoke(evHandler, sender, e)
   Else
       ProgressDisplay.Value = e.ProgressPercentage 
   End If
End Sub

 

Try that out.

Edited by MrPaul
Never trouble another for what you can do for yourself.
Posted (edited)

'AddressOf' expression cannot be converted to 'System.Delegate' because type 'System.Delegate' is declared 'MustInherit' and cannot be created.

 

[update]

As long as Backgroundworker.reportprogress is called from the WindowsForm code it works,

when it is called in the module it doesn't

Edited by Arokh
Posted

I had to change "ProgressChangedEventHandler" to "System.ComponentModel.ProgressChangedEventHandler" (I guess there is no difference)

 

But even after that the result is the same as with just having

    Private Sub Download_ProgressChanged( _
         ByVal sender As Object, _
         ByVal e As System.ComponentModel.ProgressChangedEventArgs _
       ) Handles bgwrk_Download.ProgressChanged

       ProgressDisplay.Value = e.ProgressPercentage
   End Sub

 

Me.InvokeRequired is always false

Posted

main is my Windows Form Class (it is defined as Public if it matters) which contains all the Controls and

its event handlers. (Was created by the standard "Windows Form" template.)

 

I've stripped my code to the necessary code and attached it here.

When GetIndex is in the module the progressbar doesn't change after hitting the button,

it works when it is in the Windos Form Class (main).

Debug.zip

Posted

This is interesting...

 

If you make these changes...

Private Sub Downloader(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bgwrk_Download.DoWork
       GetIndex([b]Me[/b])
   End Sub

 

 

and in the module...

    Public Function GetIndex([b]ByVal parent As main[/b]) As String
       Dim I As Int16
       THLogEvent("Bla")

       Debug.Print("Getindex start: " & Date.Now.ToLongTimeString)
       For I = 0 To 10
           Threading.Thread.Sleep(200)
           [b]parent[/b].bgwrk_Download.ReportProgress(I * 10)
       Next
       Debug.Print("Getindex done: " & Date.Now.ToLongTimeString)
       Return ""
   End Function

It works.

 

The event seems to be raised and captured so I'm not really sure why it wouldn't update the progress. Instance versus object maybe?

  • Leaders
Posted (edited)

when using a BackgroundWorker, you may want to look at creating a Delegate Sub to handle the progress etc... rather than using the DoWork function.

eg:

   '/// the backgroundworker...
   Private WithEvents bgworker As BackgroundWorker
   '/// the Delegate to handle progress...
   Private Delegate Sub download(ByVal wRequest As HttpWebRequest, ByVal wResponse As WebResponse, ByVal bar As ProgressBar)
   Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
       bgworker = New BackgroundWorker
       bgworker.RunWorkerAsync(Me)
   End Sub
   Private Sub bgworker_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bgworker.DoWork
       Dim imgPath As String = "[url]http://www.google.com/images/logo.gif[/url]"
       Dim wRequest As HttpWebRequest = DirectCast(HttpWebRequest.Create(imgPath), HttpWebRequest)
       Dim wResponse As WebResponse = DirectCast(wRequest.GetResponse(), WebResponse)
       Dim dl As New download(AddressOf downloadimage)
       '/// make an array of the arguments required.
       Dim objArgs As Object() = {wRequest, wResponse, ProgressBar1}
       '/// invoke the delegate sub to handle the download / progress.
       Me.Invoke(dl, objArgs)
   End Sub
   Private Sub downloadimage(ByVal wRequest As HttpWebRequest, ByVal wResponse As WebResponse, ByVal pBar As ProgressBar)
       Try
           Dim sWriter As FileStream = New FileStream("new.gif", FileMode.OpenOrCreate)
           Dim ContentLength As Integer = wResponse.ContentLength
           Dim sizeInKB As String = (ContentLength / 1024).ToString()
           Me.Text = "The Size of the Image is: " & sizeInKB.Substring(0, sizeInKB.IndexOf(".") + 3) & "KB"
           Dim buffer(ContentLength) As Byte
           Dim length As Integer = ContentLength
           Dim position As Integer = 0
           Dim complete As Integer = 1
           Dim returned As Integer = 0
           pBar.Value = 0 '/// clear the progressbar.
           pBar.Maximum = ContentLength '/// set it's length.
           While Not complete = 0
               position = wResponse.GetResponseStream().Read(buffer, returned, length)
               sWriter.Write(buffer, returned, position)
               complete = position
               returned += position
               length -= position
               pBar.Step = returned
               pBar.PerformStep()
           End While
           Me.Text = "Completed download"
           sWriter.Close()
           wRequest = Nothing
           MessageBox.Show("Complete!")
       Catch ex As Exception
           Console.WriteLine(ex.Message)
       End Try
   End Sub

hope it helps.

Edited by dynamic_sysop

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...