[C#]Threading update progressbar

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

Anoniem: 12795

Topicstarter
Het lijkt allemaal erg standaard en ik ben al drie avonden/nachten aan het lezen en proberen maar ik krijg mijn progressbar niet aan de gang.
De situatie:
Ik heb een form met een GO-knop, een Cancel-knop, een label, een progressbar en een backgroundworker.
Ik heb een class die uit drie hoofddelen bestaat: start-functie, deze maakt een connectie met een database, voert een query uit en returned een datatabel, verder maakt die een bestand aan en vult daar de header van in;
De end- functie, deze functie slaat het bestand op; De body-functie, deze loopt door de datatable heen en doet wat berekeningen en vult de resultaten in het bestand.
De body-functie duurt het langste (minuten) en die heb ik in een aparte thread (backgroundworker) gezet, in de hoop dat ik dan de progressbar kan updaten.
Eerst maar wat relevante code:
C#:
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
38
39
40
41
42
43
44
45
46
47
48
49
50
 this.backgroundWorker1.WorkerReportsProgress = true;
 this.backgroundWorker1.WorkerSupportsCancellation = true;
 this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
 this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);
 this.backgroundWorker1.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.backgroundWorker1_ProgressChanged);

 private void btnGo_Click(object sender, EventArgs e)
 {
 this.lblProgress.Text = "Creating shapefile";
 this.clsWork = new workCode();
 this.clsWork.createFile(this);
 this.lblProgress.Text = "File is created.";
 this.progressBar1.Value += 1;
 this.lblProgress.Text = "Start computing ...";
 this.backgroundWorker1.RunWorkerAsync();
 }
 private void btnCancel_Click(object sender, EventArgs e)
 {
 // Cancel the asynchronous operation:
 this.backgroundWorker1.CancelAsync();
 this.btnCancel.Enabled = false;
 }
 private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
 {
 // Get the BackgroundWorker that raised this event.
 BackgroundWorker worker = sender as BackgroundWorker;
 worker.ReportProgress(1);
 // Loop through all records and fill the file: 
 clsWork.executeQueryThread(worker,e);
 }

 private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
 {
 int percentage = e.ProgressPercentage / this.progressBar1.Maximum * 100;
 if (percentage < 1) percentage = 1;
 this.progressBar1.Value = percentage;
 this.progressBar1.Refresh();
 this.lblProgress.Text = percentage.ToString() + "% completed.";
 this.lblProgress.Refresh();
 }

 private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
 {
 if (e.Result.ToString() == "Finished")
 {
     this.lblProgress.Text = "Saving file";
     this.backgroundWorker1.ReportProgress(this.progressBar1.Maximum);
     this.clsWork.saveFile();
 }
 }


C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 public void executeQueryThread(BackgroundWorker worker, DoWorkEventArgs e)
 {
 foreach (DataRow row in this.qResults.Rows)
 {
     if (worker.CancellationPending)
     {
  e.Cancel = true;
  return;
     }
     [..]
     // Op specifiek interval, update the progressbar:
     // Dit werkt dus niet, er gebeurt niets:
     worker.ReportProgress(numRows);
     numRows++;
 } // foreach (rij in qResults.Rows)
 e.Result = "Finished";
 }


Als ik een breakpoint zet op worker.ReportProgress(numRows); dan komt die er wel, maar gaat niet naar backgroundWorker1_ProgressChanged()

Ik snap het niet meer, ik krijg geen foutmeldingen. Mijn form blijft netjes actief, de Cancel-knop werkt, maar niet de progressbar.
Als iemand me even in de juiste richting wil sturen, dan komt dat mijn nachtrust enorm ten goede ;)

Acties:
  • 0 Henk 'm!

  • Haan
  • Registratie: Februari 2004
  • Laatst online: 11:10

Haan

dotnetter

In principe zou je gewoon
C#:
1
progressBar1.Value = e.ProgressPercentage;

moeten doen, verder niets, om het te laten werken.

[ Voor 15% gewijzigd door Haan op 24-02-2009 09:53 ]

Kater? Eerst water, de rest komt later


Acties:
  • 0 Henk 'm!

  • BM
  • Registratie: September 2001
  • Laatst online: 13:27

BM

Moderator Spielerij
Wat me opvalt is dat je de progress% pas berekend in je ProgressChanged event, ipv in de methode die de progress report. Ik weet niet of numrows toevallig > 100 is, maar volgens mij accepteerd ReportProgress een maximale waarde van 100, als ik de msdn mag geloven:
Parameters
percentProgress
Type: System..::.Int32
The percentage, from 0 to 100, of the background operation that is complete.
Wat ik zou proberen is dus je progress al berekenen in je methode zelf, en dan enkel het percentage reporten. Zoals hierboven ook gezegt is kun je dan ook direct de progress toekennen aan je progressbar.

[ Voor 20% gewijzigd door BM op 24-02-2009 09:52 ]

Xbox
Even the dark has a silver lining | I'm all you can imagine times infinity, times three


Acties:
  • 0 Henk 'm!

  • Haan
  • Registratie: Februari 2004
  • Laatst online: 11:10

Haan

dotnetter

BM schreef op dinsdag 24 februari 2009 @ 09:51:
Wat ik zou proberen is dus je progress al berekenen in je methode zelf, en dan enkel het percentage reporten. Zoals hierboven ook gezegt is kun je dan ook direct de progress toekennen aan je progressbar.
Dat is inderdaad precies wat je moet doen.

Kater? Eerst water, de rest komt later


Acties:
  • 0 Henk 'm!

Anoniem: 12795

Topicstarter
Ik zal jullie voorstel uitproberen, overigens is de maximum van de progressbar gelijk aan het aantal records in de datatable. Percentage zal dus nooit groter zijn dan 100.
Ik heb op dit moment niet de beschikking over VS2008, het uitproberen wordt dus pas vanavond/

Bedankt allen.

Acties:
  • 0 Henk 'm!

  • Adion
  • Registratie: Januari 2001
  • Laatst online: 05-07 16:32
Haan schreef op dinsdag 24 februari 2009 @ 09:50:
In principe zou je gewoon
C#:
1
progressBar1.Value = e.ProgressPercentage;

moeten doen, verder niets, om het te laten werken.
Ik weet niet hoe het met een progress bar zit, maar van labels/text boxes, ... weet ik vrij zeker dat je ze niet rechtstreeks vanuit een andere thread rechtstreeks kan aanpassen. Daarvoor heb je dus een delegate nodig.

VirtualDJ 2024 - Fast Image Resizer - Instagram


Acties:
  • 0 Henk 'm!

  • Haan
  • Registratie: Februari 2004
  • Laatst online: 11:10

Haan

dotnetter

Die regel staat in de backgroundWorker1_ProgressChanged methode, dat gaat zeker goed. Wat je zegt over Controls updaten vanuit een andere thread is inderdaad ook een goed punt, dat kan niet, maar dat merk je vanzelf wel als je een exception krijgt :P Blijkbaar kan het in de methode wel, anders had de TS het al gemerkt denk ik.

Kater? Eerst water, de rest komt later


Acties:
  • 0 Henk 'm!

Anoniem: 12795

Topicstarter
Wat ik begrepen heb uit de kilometers documentatie die ik de afgelopen dagen heb gelezen, zorgt de worker.ReportProgress() ervoor dat het event backgroundWorker1_ProgressChanged() wordt getriggerd en dan zit je weer op de Main thread en mag je labels ed. wel aanpassen.

Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 13:03
Adion schreef op dinsdag 24 februari 2009 @ 10:14:
[...]

Ik weet niet hoe het met een progress bar zit, maar van labels/text boxes, ... weet ik vrij zeker dat je ze niet rechtstreeks vanuit een andere thread rechtstreeks kan aanpassen. Daarvoor heb je dus een delegate nodig.
BackgroundWorker abstraheert dit een beetje, en zorgt er voor dat de ProgressChanged indien nodig op de UI thread geinvoked wordt.

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 25-06 12:50
Hier in elk geval een eenvoudig PoC'je hoe het bij mij werkt:
C#:
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
    public partial class Form1 : Form
    {
        BackgroundWorker bw = new BackgroundWorker();

        public Form1()
        {
            InitializeComponent();

            bw.DoWork += new DoWorkEventHandler(bw_DoWork);
            bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
            bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
            bw.WorkerReportsProgress = true;
            bw.WorkerSupportsCancellation = true;
        }

        void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            for (int i = 1; i <= 100 && !bw.CancellationPending; i++)
            {
                    Thread.Sleep(100);
                    bw.ReportProgress(i);
            }

            if (bw.CancellationPending)
            {
                e.Cancel = true;
            }
        }

        void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (!e.Cancelled)
            {
                MessageBox.Show("Klaar!");
            }
        }

        void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar1.Value = e.ProgressPercentage;
        }


        private void button1_Click(object sender, EventArgs e)
        {
            bw.RunWorkerAsync();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            bw.CancelAsync();
        }
    }

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


Acties:
  • 0 Henk 'm!

Anoniem: 12795

Topicstarter
Ik ben al weer wat verder. Het bleek dat ik sowieso steeds de waarde 0 door gaf aan de progressbar.
Ik heb nu mijn code in mijn class aangepast naar:
C#:
1
worker.ReportProgress((int)((double)numRows / (double)maxRows * 100));
waarbij maxRows in dit geval 77039 is.
Mijn backgroundWorker1_ProgressChanged() heb ik verandert zoals voorgesteld in
C#:
1
this.progressBar1.Value = e.ProgressPercentage;

Als ik nu op beide regels een breakpoint zet, dan zie ik dat er netjes een waarde wordt overgestuurd. Als ik dan bekijk via QuickWatch wat de value is van de progressbar dan zie ik daar de juiste waarde staan. Maar mijn progressbar doet nog steeds niets.
Nog iets beter gekeken naar de QuickWatch en het maximum van de progressbar was gezet op maxRows (dus 77039). Het werkte dus wel, maar er viel niets te tonen.
Ik heb het maximum op 100 gezet en nu werkt het :O
Kan ik vanavond eindelijk rustig slapen :z

Acties:
  • 0 Henk 'm!

  • Sebazzz
  • Registratie: September 2006
  • Laatst online: 05-07 17:35

Sebazzz

3dp

riezebosch schreef op dinsdag 24 februari 2009 @ 13:24:
Hier in elk geval een eenvoudig PoC'je hoe het bij mij werkt:
C#:
1
2
3
4
5
6
7
8
9
10
            for (int i = 1; i <= 100 && !bw.CancellationPending; i++)
            {
                    Thread.Sleep(100);
                    bw.ReportProgress(i);
            }

            if (bw.CancellationPending)
            {
                e.Cancel = true;
            }
Dat heeft zin ;) Zet het (het = if (bw.Cancellation....)) dan in de loop waar daarwerkelijk wat gebeurt, dus in de for().

[ Voor 3% gewijzigd door Sebazzz op 24-02-2009 21:45 ]

[Te koop: 3D printers] [Website] Agile tools: [Return: retrospectives] [Pokertime: planning poker]


Acties:
  • 0 Henk 'm!

  • BM
  • Registratie: September 2001
  • Laatst online: 13:27

BM

Moderator Spielerij
Sebazzz schreef op dinsdag 24 februari 2009 @ 21:44:
[...]
Dat heeft zin ;) Zet het (het = if (bw.Cancellation....)) dan in de loop waar daarwerkelijk wat gebeurt, dus in de for().
Indien CancellationPending op true staat, word de loop toch afgebroken? :)
True, ik zou het ook anders doen, maar het werkt wel :)

[ Voor 8% gewijzigd door BM op 24-02-2009 22:12 ]

Xbox
Even the dark has a silver lining | I'm all you can imagine times infinity, times three


Acties:
  • 0 Henk 'm!

  • Sebazzz
  • Registratie: September 2006
  • Laatst online: 05-07 17:35

Sebazzz

3dp

Nee. Je moet in je thread controleren of CancellationPending op true staat en op basis daarvan acties nemen, zoals het sluiten van bestandsstromen en het afbreken van de loop. Niet vergeten op e.Cancel op true te zetten.

[Te koop: 3D printers] [Website] Agile tools: [Return: retrospectives] [Pokertime: planning poker]

Pagina: 1