Arokh Posted January 9, 2007 Posted January 9, 2007 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? Quote
mskeel Posted January 10, 2007 Posted January 10, 2007 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. Quote
Arokh Posted January 10, 2007 Author Posted January 10, 2007 (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 January 10, 2007 by Arokh Quote
Leaders snarfblam Posted January 10, 2007 Leaders Posted January 10, 2007 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?) Quote [sIGPIC]e[/sIGPIC]
Arokh Posted January 10, 2007 Author Posted January 10, 2007 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. Quote
Leaders snarfblam Posted January 11, 2007 Leaders Posted January 11, 2007 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. Quote [sIGPIC]e[/sIGPIC]
Arokh Posted January 11, 2007 Author Posted January 11, 2007 *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. Quote
MrPaul Posted January 11, 2007 Posted January 11, 2007 (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 January 11, 2007 by MrPaul Quote Never trouble another for what you can do for yourself.
Arokh Posted January 11, 2007 Author Posted January 11, 2007 (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 January 11, 2007 by Arokh Quote
MrPaul Posted January 11, 2007 Posted January 11, 2007 Fixed Apologies, I'm more used to C#. I've edited the post to correct the mistake. Quote Never trouble another for what you can do for yourself.
Arokh Posted January 11, 2007 Author Posted January 11, 2007 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 Quote
Administrators PlausiblyDamp Posted January 11, 2007 Administrators Posted January 11, 2007 If it isn't working when called from a module it could be down to how you refer to the BackGroundWorker. In the line main.bgwrk_Download.ReportProgress(UBound(buffer) / httpRes.ContentLength * 100) where / how is main defined? Quote Posting Guidelines FAQ Post Formatting Intellectuals solve problems; geniuses prevent them. -- Albert Einstein
Arokh Posted January 11, 2007 Author Posted January 11, 2007 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 Quote
mskeel Posted January 11, 2007 Posted January 11, 2007 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? Quote
Leaders dynamic_sysop Posted January 17, 2007 Leaders Posted January 17, 2007 (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 January 17, 2007 by dynamic_sysop Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.