[VB.Net] ProgressBar "loopt" niet

Pagina: 1
Acties:
  • 265 views sinds 30-01-2008
  • Reageer

  • F-Tim
  • Registratie: November 2003
  • Laatst online: 11:17
Aloha mede tweakers, op deze mooie zonnige dag ben ik helaas bezig met het maken van laadscherm voor mijn applicatie. Op dit laadscherm wil ik een ProgressBar in Marquee stijl hebben. Alles leuk en wel, progressbar op het scherm geplaatst, aanroepen overal goed gezet, maar als het wordt gerund, dan blijft dr progressbar stilstaan. Ik heb dit nodig omdat ik als ik van Tab wissel dat er dan een grote riedel data moet worden geraadpleegd, en dit soms wel tot 5 seconden kan duren. Het is dan vervelend als de user geen feedback krijgt. Vandaar mijn keuze voor het laadscherm, want de code heb ik al zo goed mogelijk geoptimaliseerd.

Als ik meerdere keren een tabwissel doe, dan beweegt de ProgressBar wel 1 positie, maar hij loopt niet als hij op het scherm staat.

Ik heb al een BackgroundWorker, een Timer en een Thread geprobeerd. Ook heb ik Application. EnableVisualStyles en Application.DoEvents geprobeerd.Maar dat werkt ook niet :(

Een BackgroundWorker wordt totaal niet aangeroepen, die "doet" gewoon niets, en als ik zeg dat de BackgroundWorker ASynchroon moet worden gestart, dan roept hij de code aan, maar dan krijg ik hetzelfde probleem als wat ik bij een Timer of Thread krijg. Hij geeft dan de melding dat de ProgressBar al door een andere Thread wordt gebruikt, en ik hem dus niet kan aanpassen.

Mijn vraag is nu of iemand weet hoe dit op te lossen is?

Wanna play?


  • whoami
  • Registratie: December 2000
  • Laatst online: 16:46
Windows Controls mogen enkel door de UI thread ge-updated worden, vandaar waarschijnlijk jouw foutmelding.
Die BackGroundWorker zorgt daar normaal gezien voor; echter, je moet die wel op de goeie manier gebruiken. Een BackGroundWorker voert iets uit op een andere thread, en dat zorgt ervoor dat je UI responsive blifjt. Je BackGroundWorker heeft ook een event dat je kan gebruiken om je UI te updaten (injouw geval bv, de progressbar updaten). De BackGroundworker zorgt ervoor dat dit op de goede thread gebeurt.

https://fgheysels.github.io/


  • F-Tim
  • Registratie: November 2003
  • Laatst online: 11:17
Als ik in MSDN naar de BackGroundWorker class kijk, en dan met name naar het Example, dan interpreteer ik dit als volgt voor mijn ProgressBar

Visual Basic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Public Sub StartThread()
   Dim tThread = New Threading.Thread(New Threading.ThreadStart(AddressOf ThreadProcSafe))
   tThread.Start()
End Sub

Private Sub ThreadProcSafe()
   RefreshBar()
End Sub

Delegate Sub RefreshBarCallback()

Private Sub RefreshBar()
   If ProgressBar1.InvokeRequired Then
      Dim d As New RefreshBarCallback(AddressOf RefreshBar)
      Me.Invoke(d, New Object() {})
   Else
      ProgressBar1.Update()
   End If
End Sub


Ik weet dat de ProgressBar1.Update() regel gedaan wordt, als ik nl. een breakpoint op die regel zet, dan stopt hij er ook netjes... Maar de ProgressBar zelf loopt niet... ;( Waar zit mijn fout in de code of in mijn denkpatroon?

Ik weet niet of het nodig is om te vermelden, maar ik maak gebruik van een Form, dat ik op een ander form aanroep. Ik pas dan de size en de location aan, en zet een Owner zodat het laadscherm bovenop het andere scherm getoond wordt, maar niet altijd bovenop blijft. Als ik de TopMost property gebruik, dan blijft het scherm nl. ook nog over de debugger staan.

Wanna play?


  • whoami
  • Registratie: December 2000
  • Laatst online: 16:46
De Thread moet de progressbar niet updaten; de thread moet de job doen (je gegevens inladen), en een notificatie geven als de progress-bar moet geupdated worden.

https://fgheysels.github.io/


  • CodeIT
  • Registratie: Juni 2002
  • Laatst online: 17-02 18:52

CodeIT

Code IT

Gokje uit de losse pols. Nadat je de progressbar update het volgende statement neerzetten:
Visual Basic .NET:
1
Application.DoEvents()
De Thread moet de progressbar niet updaten; de thread moet de job doen (je gegevens inladen), en een notificatie geven als de progress-bar moet geupdated worden.
Niet eens gezien. Al het werk wordt nog steeds door je UI thread gedaan waardoor deze geen "tijd" meer over heeft voor het updaten van je UI.

[ Voor 56% gewijzigd door CodeIT op 11-05-2006 15:48 ]


  • whoami
  • Registratie: December 2000
  • Laatst online: 16:46
JanTenHove schreef op donderdag 11 mei 2006 @ 15:46:
Gokje uit de losse pols. Nadat je de progressbar update het volgende statement neerzetten:
Visual Basic .NET:
1
Application.DoEvents()


[...]

Niet eens gezien. Al het werk wordt nog steeds door je UI thread gedaan waardoor deze geen "tijd" meer over heeft voor het update van je UI.
Als je die taak op een andere thread uitvoert (door gebruik te maken van bv een backgroundworker), heeft die DoEvents geen nut.
Niet eens gezien. Al het werk wordt nog steeds door je UI thread gedaan waardoor deze geen "tijd" meer over heeft voor het updaten van je UI.
Als je gebruik maakt van een BackGroundWorker, wordt het werk op een andere thread gedaan, en niet op de UI thread. Het Updaten van de progressbar wordt wel door de UI thread gedaan, omdat dat moet. (gebeurt dmv invoke).

[ Voor 27% gewijzigd door whoami op 11-05-2006 15:49 ]

https://fgheysels.github.io/


  • F-Tim
  • Registratie: November 2003
  • Laatst online: 11:17
Hmmmmm, als ik het andersom implementeer, en dus de thread gebruik om de gegevens te updaten, dan moet ik de code als volgt interpreteren. (Al is het volgens mij dan niet met een BackGroundWorker, omdat ik een Thread gebruik). De ProgressBar loopt dan overigens wel 2 stappen, dus er is vooruitgang, maar ergens zit er nog een fout in mijn code. Ook weet ik wel dat de Overlay niet op de juiste plek staat, deze moet ergens anders komen, maar dan moet ik eerst een procedure aanroepen, dus dat komt wel als de ProgressBar werkt, anders moet ik de code weer teveel uit elkaar trekken ;)

Visual Basic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Private Sub ChangeTab(ByVal Sender As System.Object, ByVal e As System.EventArgs)
    Tab = Sender
    Overlay.Size = New System.Drawing.Size(Me.Width, Me.Height - 50)
    Overlay.Location = New System.Drawing.Point(0, 50)
    Overlay.Owner = Me
    Overlay.Show()

    Dim tThread = New Thread(New Threading.ThreadStart(AddressOf ChangeTabThreadedLoadData))
    tThread.Start()
End Sub

Private Sub ChangeTabThreadedLoadData()
    ChangeTabLoadData()
End Sub

Delegate Sub ChangeTabLoadDataCallback()

Private Sub ChangeTabLoadData()
    If Me.InvokeRequired Then
        Dim d As New ChangeTabLoadDataCallback(AddressOf ChangeTabLoadData)
        Me.Invoke(d, New Object() {})
    Else
        SetDatabaseFolder(Microsoft.VisualBasic.Right(Tab.Name, Len(Tab.Name) - 3) + "\")
        If Not InfoBar Is Nothing Then
            InfoBar.Dispose() : InfoBar = Nothing
        End If
        LoadInfoBar() : InfoBar.Parent = pnlBottom
        ButtonMenu.Enabled = False
        If Not KentekenSchil Is Nothing Then
            KentekenSchil.Dispose() : KentekenSchil = Nothing
        End If
        LoadKentekenSchil(False) : KentekenSchil.Parent = pnlCenter

        RefreshTimer.Stop() : RefreshTimer.Start()
        Overlay.Hide()
    End If
End Sub


Of ik dan nu op het Overlay form wél of niet een BGWorker gebruik die ProgressBar1.Update, Me.Update of Application.DoEvents in het DoWork event neerzet, de ProgressBar vertoont geen extra leven helaas :{

Wanna play?


  • whoami
  • Registratie: December 2000
  • Laatst online: 16:46
Een BackGroundWorker gebruikt een thread.
Die backgroundworker hebben ze net gemaakt zodat je threads kon gebruiken, zonder dat je rekening hoefde te houden met het invoken van events, en dergelijke... Die BGW doet dat allemaal voor jou.

https://fgheysels.github.io/


  • F-Tim
  • Registratie: November 2003
  • Laatst online: 11:17
Als ik een BackGroundWorker toevoeg op het formulier, en de code als volgt aanpas, de ChangeTab wijzig ik overigens vrijwel niet. Ik doe alleen een RunWorkerASync ipv de code om de Thread aan te roepen:

Visual Basic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Private Sub BGWorker_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BGWorker.DoWork
    SetDatabaseFolder(Microsoft.VisualBasic.Right(Tab.Name, Len(Tab.Name) - 3) + "\")
    If Not InfoBar Is Nothing Then
        InfoBar.Dispose() : InfoBar = Nothing
    End If
    LoadInfoBar() : InfoBar.Parent = pnlBottom
    ButtonMenu.Enabled = False
    If Not KentekenSchil Is Nothing Then
        KentekenSchil.Dispose() : KentekenSchil = Nothing
    End If
    LoadKentekenSchil(False) : KentekenSchil.Parent = pnlCenter
End Sub

Private Sub BGWorker_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BGWorker.RunWorkerCompleted
    RefreshTimer.Stop() : RefreshTimer.Start()
    Overlay.Hide()
End Sub


dan krijg ik de foutmelding dat de InfoBar al door een andere thread bewerkt wordt. Terwijl dat niet zo is, want ik heb op dat moment alleen de Overlay in gebruik én deze code....

Dit is de specifieke foutmelding:
Het is niet toegestaan een bewerking uit te voeren via verschillende threads: er werd vanaf een andere thread toegang gekregen tot het besturingselement ucInfoBar dan de thread waarop het element is gemaakt.

Wanna play?


  • F-Tim
  • Registratie: November 2003
  • Laatst online: 11:17
Zucht, ik krijg langzaam de neiging om een Splashscreen toe te voegen aan m'n applicatie en deze op dat moment gewoon op te roepen.... daar werkt tenminste de ProgressBar op |:(

[ Voor 4% gewijzigd door F-Tim op 11-05-2006 17:01 ]

Wanna play?


  • whoami
  • Registratie: December 2000
  • Laatst online: 16:46
Je moet vanuit je DoWork ook die progressbar niet rechtstreeks proberen te updaten.
Die BackGroundWorker heeft een event 'ProgressChanged'. Daar moet je een event-handler aan hangen die je progress-bar wijzigt.
In de method die je werk doet, kan je een method oproepen die die ProgressChanged event raised. (OnProgressChange ofzo).

hoe simpel kan het zijn.
Ipv direct te gaan prutsen, is het meestal handig om eerst eens even in de MSDN te duiken, en daar eens eea te lezen over de class die je wilt gebruiken.

https://fgheysels.github.io/


  • F-Tim
  • Registratie: November 2003
  • Laatst online: 11:17
Ik update de progressbar ook niet vanuit de DoWork. Een Marquee style progressbar behoort toch uit zichzelf te lopen, daar hoef ik in principe geen code voor aan te roepen.

In de DoWork doe ik alleen alle werk (de data laden). Ik zal dan wel eens kijken in hoeverre ik de UI Thread kan laten Invoken op de ProgressChanged event van de BGW... kijken of dat bruikbare broncode oplevert :)

Edit:
Hmmm, als ik de volgende situatie gebruik, loopt de ProgressBar, mr krijg ik een error dat er al door een andere Thread gebruik wordt gemaakt van een besturingselement. En dan bij de code waarbij ik de Parent van een usercontrol instel op een panel, de fout gaat dan over de panel... Conclusie, m'n code is niet thread-safe ;( Hoe kan ik em wel threadsafe maken?

Visual Basic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Private Sub ChangeTab(ByVal Sender As System.Object, ByVal e As System.EventArgs)
    Tab = Sender
    Overlay.Size = New System.Drawing.Size(Me.Width, Me.Height - 50)
    Overlay.Location = New System.Drawing.Point(0, 50)
    Overlay.Owner = Me
    Overlay.Show()

    If Not InfoBar Is Nothing Then
        InfoBar.Dispose() : InfoBar = Nothing
    End If
    If Not KentekenSchil Is Nothing Then
        KentekenSchil.Dispose() : KentekenSchil = Nothing
    End If

    BGWorker.RunWorkerAsync()
End Sub

Private Sub BGWorker_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BGWorker.DoWork
    If Overlay.InvokeRequired Then
        Dim d As New ChangeTabLoadDataCallback(AddressOf ChangeTabLoadData)
        Overlay.Invoke(d, New Object() {})
    Else
        SetDatabaseFolder(Microsoft.VisualBasic.Right(Tab.Name, Len(Tab.Name) - 3) + "\")
        LoadInfoBar() : InfoBar.Parent = pnlBottom
        ButtonMenu.Enabled = False
        LoadKentekenSchil(False) : KentekenSchil.Parent = pnlCenter
    End If
End Sub


Als ik in MSDN kijk zie ik daar wel een stukje over staan... eens kijken in hoeverre dat bruikbaar is ;)

Edit2:
En dan issie wl thread-safe, maar dan loopt de ProgressBar weer niet 8)7
Heeft wel een hoop extra code met zich meegebracht :/ ChangeTab daarentegen is onveranderd gebleven:

Visual Basic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Delegate Sub BGWorker_DoWorkCallback(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs)

Private Sub BGWorker_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BGWorker.DoWork
    If Overlay.InvokeRequired Then
        Dim d As New BGWorker_DoWorkCallback(AddressOf BGWorker_DoWork)
        Overlay.Invoke(d, New Object() {})
    Else
        SetDatabaseFolder(Microsoft.VisualBasic.Right(Tab.Name, Len(Tab.Name) - 3) + "\")
        LoadInfoBar() : SetPanelAsParent(InfoBar, pnlBottom)
        ButtonMenu.Enabled = False
        LoadKentekenSchil(False) : SetPanelAsParent(KentekenSchil, pnlCenter)
    End If
End Sub

Delegate Sub SetParentToPanelCallback(ByVal UserControl As System.Object, ByVal Panel As Panel)

Private Sub SetPanelAsParent(ByVal UserControl As System.Object, ByVal Panel As Panel)
    If Panel.InvokeRequired Then
        Dim d As New SetParentToPanelCallback(AddressOf SetPanelAsParent)
        Panel.Invoke(d, New Object() {UserControl, Panel})
    Else
        UserControl.Parent = Panel
    End If
End Sub

Private Sub BGWorker_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BGWorker.RunWorkerCompleted
    RefreshTimer.Stop() : RefreshTimer.Start()
    Overlay.Hide()
End Sub


Zit ik zo op de goede weg? Of ben ik alsnog een verkeerde richting in geslagen? En ls ik in de goede richting zit, waar moet ik het probleem dan ongeveer zoeken? :?

[ Voor 106% gewijzigd door F-Tim op 12-05-2006 09:29 . Reden: Broncode verbeteringen... hopelijk ]

Wanna play?


  • F-Tim
  • Registratie: November 2003
  • Laatst online: 11:17
En verder spitten op MSDN geeft aan dat je WorkerReportProgress op True moet zetten, dát in de code als trigger moet gebruiken, en dan werkt het wel allemaal met een BackGroundWorker :) (En nog ASynchroon ook ;))

* Case Closed :+ *

[ Voor 3% gewijzigd door F-Tim op 12-05-2006 12:25 ]

Wanna play?


  • PolarBear
  • Registratie: Februari 2001
  • Niet online
Ranzig maar soms wel afdoende:

Visual Basic .NET:
1
        Control.CheckForIllegalCrossThreadCalls = False
Pagina: 1