[C#] Progressbar kommt nicht hinterner

Dieses Thema im Forum "Programmierung & Entwicklung" wurde erstellt von Mockingbird, 28. Juli 2013 .

Schlagworte:
  1. 28. Juli 2013
    Progressbar kommt nicht hinterner

    Hallo liebes forum!

    mein problem ist evtl ein bisschen unglücklich formuliert aber was besseres fiel mir nicht ein..

    zu meinem Problem(hier der codeausschnitt):
    Spoiler
    Code:
    foreach (string file in parts)
     {
     
     string1 = file.Replace(".zip", string.Empty);
    
     if (Convert.ToInt16(string1) > Convert.ToInt16(ReadVersionTxt()))
     {
     textBox1.AppendText("Downloading Update " + updateNumber + " / " + updateAmount + "\r\n");
     webclient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(webclient_DownloadProgressChanged);
     webclient.DownloadFileAsync(new Uri(downloadLink + file),"./" + "TEMPFILE" + file);
     
     updateNumber++;
     
     }
     }
     textBox1.AppendText("Download complete, extracting files, please wait.\r\n");
     }
    
     void webclient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
     {
     progressBar1.Value = e.ProgressPercentage;
     }

    Wenn ich das programm starte, wird der gesamte code durchgerattert und am ende erscheint dann die nachricht "download complete...[..]" und paar sekunden danach schlägt der progressbar auf 100% um. das ist aber viel zu spät.. ich will, dass der progressbar während des downloads updatet. die downloads dauern auch ein paar sekunden, doch da macht der progressbar einfach nichts.

    um das ganze ein wenig auszubremsen, hatte ich diese while-schleife mit hineingelegt, leider auch ohne erfolg.
    Code:
    while(webclient.isBusy == true)
     {Thread.Sleep(200)}
    ich hoffe jemand hat ne lösung!

    Gruß, Mockingbird!
     
  2. 28. Juli 2013
    AW: Progressbar kommt nicht hinterner

    Hab leider keine Ahnung von C# aber es scheint so, als würde die GUI durch irgendwas blockiert werden. Wie lädst du denn die Dateien runter, machst du das in einem eigenen Thread? Wenn nicht wird der Callback zum EventHandler wahrscheinlich erst ausgeführt, nachdem die Dateien komplett runtergeladen wurden.
     
  3. 28. Juli 2013
    AW: Progressbar kommt nicht hinterner

    hier lade ich die datei runter:
    Code:
    webclient.DownloadFileAsync(new Uri(downloadLink + file),"./" + "TEMPFILE" + file);
    downloadlink + file ist der normale http link(http:// .../file.exe)

    die method, die den progressbar updaten soll habe ich direkt davor/danach aufgerufen(funktioniert bei mir beides nicht )
     
  4. 28. Juli 2013
    Zuletzt bearbeitet: 28. Juli 2013
    AW: Progressbar kommt nicht hinterner

    How to update the progress bar in runtime using c# - Stack Overflow

    Im 2. Post schreibt er, dass man Refresh() auf der Progressbar aufrufen kann, damit sie sich updatet, vielleicht funktioniert das ja. Ansonsten würd ich für jeden Download einen eigenen Thread anlegen, geht 1. schneller, weil die Dateien parallel und nicht nacheinander runtergeladen werden und 2. blockiert die GUI und die Progressbar nicht während des Downloads (falls C# einem das nicht ohnehin schon mit "DownloadFileAsync" abnimmt).
     
  5. 28. Juli 2013
    Zuletzt bearbeitet: 28. Juli 2013
    AW: Progressbar kommt nicht hinterner

    Das kann so doch nicht funktionieren. Du startest mehrere Downloads asynchron und lässt sie alle die Gleiche Progressbar updaten?

    Stell dir das Mal zeitlich vor, dann siehst du ganz schnell wo dein Problem liegt.

    Wenn einer von deinen Downloads bspw. bei 10% ist und der andere vorher bei 20% war, springt das Ding auf 10% zurück, wenn der andere dann auf 21% kommt, springt das Ding wieder auf 21% und danach, wenn der andere 11% erreicht wieder auf 11% etc.

    Außerdem heftest du vor jeden Download einen neuen Listener an, was auch falsch ist. So wird bei jeder veränderung eines Downloades n-Mal (n = Anzahl deiner Schleifen Durchläufe) die Progressbar geupdatet.

    Code:
     webclient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(webclient_DownloadProgressChanged);
    Die Zeile bitte nur einmal außerhalb der Schleife.

    DownloadFileAsync
    Macht eben genau das. Es lagert jeden Download automatisch in einen Thread aus und blockiert nicht. Das Problem hier ist definitiv nicht, dass irgendetwas blockt.
    Das hätte man aber auch in 10 Sekunden Google nachlesen können.
     
  6. 28. Juli 2013
    AW: Progressbar kommt nicht hinterner

    danke für eure antworten! ich habe mich hingesetzt und versucht die dinge soweit umzusetzen, aber leider ohne erfolg. was ich gemacht habe:

    -webclient.downloadprogresschanged wird jetzt nur noch 1 mal durchlaufen
    -ich habe versucht, dateien async und nicht async herunterzuladen
    -ich habe es so eingestellt, dass nur insg 1 datei heruntergeladen wird, sich also der proigressbar nicht überschreiben sollte
    -refresh() eingebunden

    ich habe hier mal die gesamte method aufgelistet(üerflüssige variablenumwandlung rausgenommen):

    was soll passieren?

    im ersten for-durchlauf werden die gesamtKbytes gespeichert und wieviele dateien gedownloadet werden müssen
    in der 2. for schleife werden dann alle dateien heruntergeladen(.zip archieve), entpackt und danach gelöscht.
    Spoiler
    Code:
    private void button2_Click(object sender, EventArgs e)
     {
     WebClient webclient = new WebClient();
     webclient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(webclient_DownloadProgressChanged);
     
     foreach (string file in parts)
     {
     if (Convert.ToInt16(string1) > Convert.ToInt16(ReadVersionTxt()))
     {
     updateAmount++;
     Stream sr = webclient.OpenRead(downloadLink + file);
     overallBytes = overallBytes + (Convert.ToInt64(webclient.ResponseHeaders["Content-Length"]) / 1024);
     sr.Close();
     }
     }
     foreach (string file in parts)
     {
     extracted = false;
    
     if (Convert.ToInt16(string1) > Convert.ToInt16(ReadVersionTxt()))
     {
     webclient.DownloadFile(downloadLink + file, "TEMPFILE" + file);
     // webclient.DownloadFileAsync(new Uri(downloadLink + file),"./" + "TEMPFILE" + file);
    
     while (extracted == false)
     {
     progressBar1.Refresh();
    
     if (webclient.IsBusy == false)
     {
     ZipFile zipfile = ZipFile.Read("TEMPFILE" + file);
     zipfile.ExtractAll(Directory.GetCurrentDirectory(), ExtractExistingFileAction.OverwriteSilently);
     extracted = true;
     zipfile.Dispose();
     File.Delete("TEMPFILE" + file);
     }
     }
     updateNumber++;
     }
     }
    
     
  7. 28. Juli 2013
    AW: Progressbar kommt nicht hinterner

    Erstmal:
    Du musst kein Update() durchführen wenn du den Wert änderst
    Zweitens:
    DownloadFile() ist blockierend. Deine GUI wird sich nicht refreshen (siehe: WebClient.DownloadFile-Methode (String, String) (System.Net))
    Entweder du lädst immer nur eine Datei mittels DownloadFileAsync oder du schreibst eine eigene Funktion die ein Threadsicheres Downloaden ermöglicht und verwendest diese. Der Download muss auf jeden Fall in einem eigenen Thread laufen wenn du den Fortschritt ausgeben willst.
     
  8. 28. Juli 2013
    AW: Progressbar kommt nicht hinterner

    ah sorry, hab vergessen die method von dem event mitzuposten. einfach dazudenken direkt darunter, ausserhalb von der press_button_2 method:

    Code:
    void webclient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
     {
     progressBar1.Value = e.ProgressPercentage;
     }
    das refresh() habe ich also wieder rausgenommen und von downloadfile() wieder auf downloadfileasync() gestellt, aber das hilft auch nicht(ist wieder so wie am anfang).

    so wie es jetzt ist, warte ich ja, dass downloadfileasync() abgeschlossen ist(mit der if bedingung webclient.isBusy == false). aber das hilft halt auch nicht :/
     
  9. 28. Juli 2013
    Zuletzt bearbeitet: 28. Juli 2013
    AW: Progressbar kommt nicht hinterner

    Code:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Net;
    using System.IO;
    using System.Windows.Forms;
    using System.Diagnostics;
    using System.Threading;
    using System.ComponentModel;
    
    namespace ProgressTest
    {
     class MultiDownloader
     {
    
     private ProgressBar progressBar;
    
     private Dictionary<Object, long> knownWebclients = new Dictionary<Object, long>();
    
     private long totalBytes = 0;
     private long totalBytesRecv = 0;
    
     private List<KeyValuePair<Uri, String>> downloads = new List<KeyValuePair<Uri, String>>();
    
     private Boolean isRunning = false;
    
     private int currentDownloads = 0;
     private int completedDownloads = 0;
    
     public MultiDownloader(ProgressBar progressBar)
     {
     this.progressBar = progressBar;
     }
    
     public void Start()
     {
     Thread thread = new Thread(_Start);
     thread.Start();
     }
    
     private void _Start()
     {
     if (isRunning || downloads.Count == 0) return;
    
     Reset();
    
     isRunning = true;
    
     totalBytes = GetTotalBytes();
     
     StartAllDownloads();
    
     }
    
     private void StartAllDownloads()
     {
     foreach (KeyValuePair<Uri, String> download in downloads)
     {
     DownloadUrl(download.Key, download.Value);
     currentDownloads++;
     }
     }
    
     private long GetTotalBytes()
     {
     long totalBytes = 0;
     foreach (KeyValuePair<Uri, String> download in downloads)
     {
     totalBytes += GetDownloadBytes(download.Key);
     }
     return totalBytes;
     }
    
     private void Reset()
     {
     knownWebclients.Clear();
     currentDownloads = 0;
     completedDownloads = 0;
     }
    
     public void AddDownloadUrl(Uri uri, String targetFile)
     {
     downloads.Add(new KeyValuePair<Uri, String>(uri, targetFile));
     }
    
     private void DownloadUrl(Uri uri, String targetFile)
     {
     GetWebClient().DownloadFileAsync(uri, targetFile);
     }
    
     private WebClient GetWebClient()
     {
     WebClient webClient = new WebClient();
     webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgressChanged);
     webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadCompleted);
     return webClient;
     }
    
     private long GetDownloadBytes(Uri uri)
     {
     WebClient webClient = new WebClient();
     Stream stream = webClient.OpenRead(uri);
     try
     {
     return (Convert.ToInt64(webClient.ResponseHeaders["Content-Length"]));
     }
     finally
     {
     stream.Close();
     }
     }
    
     private void DownloadProgressChanged(object webClient, DownloadProgressChangedEventArgs e)
     {
     lock (this)
     {
     if (!knownWebclients.Keys.Contains(webClient)) 
     knownWebclients.Add(webClient, 0);
     
     totalBytesRecv += e.BytesReceived - knownWebclients[webClient];
     knownWebclients[webClient] = e.BytesReceived;
    
     progressBar.Parent.Invoke((MethodInvoker) delegate
     {
     progressBar.Value = (int)((double)totalBytesRecv / (double)totalBytes) * 100;
     });
    
     }
     }
    
     private void DownloadCompleted(object webClient, AsyncCompletedEventArgs e)
     {
     completedDownloads++;
     if (completedDownloads == currentDownloads)
     {
     isRunning = false;
     }
     }
     }
    }
    
    
    Ich habe mal diese Klasse geschrieben um zu verdeutlichen, wie ich das Problem lösen würde. Es kann durchaus sein, dass der Code im Bezug auf C# Code-Konventionen nicht 100%tig ist.

    Getestet habe ich den Code so:

    Code:
     private void frmMain_Click(object sender, EventArgs e)
     {
     MultiDownloader multiDownloader = new MultiDownloader(progressBar);
     multiDownloader.AddDownloadUrl(new Uri("http://ftp5.gwdg.de/pub/linux/archlinux/multilib/os/x86_64/dev86-0.16.19-1-x86_64.pkg.tar.xz"), "test1.file");
     multiDownloader.AddDownloadUrl(new Uri("http://ftp5.gwdg.de/pub/linux/archlinux/multilib/os/x86_64/dev86-0.16.19-1-x86_64.pkg.tar.xz"), "test2.file");
     multiDownloader.AddDownloadUrl(new Uri("http://ftp5.gwdg.de/pub/linux/archlinux/multilib/os/x86_64/dev86-0.16.19-1-x86_64.pkg.tar.xz"), "test3.file");
     multiDownloader.Start();
     }
    Wobei "progressBar" natürlich eine ProgressBar in der Form ist.

    Fragen zum Code einfach hier stellen.

    //Edit:

    Es würde design-technisch mehr Sinn machen, die Klasse um die Möglichkeit zu erweitern eigene Event-Listener anzuhängen und darüber dann die ProgressBar (oder was auch immer) zu steuern. Das wäre dann eine saubere Trennung von Logik und UI.
     
    1 Person gefällt das.
  10. 29. Juli 2013
    Zuletzt bearbeitet: 29. Juli 2013
    AW: Progressbar kommt nicht hinterner

    erstmal danke für die Hilfe, aber um ehrlich zu sein kann ich damit nicht viel anfangen, bin noch ziemlicher neuling was programmieren betrifft... habs mir angeguckt aber werde da nicht so schlau draus, tut mir leid :/

    ich habe mal ein bisschen weiter gelesen und weiß jetzt zumindest, wo mein problem liegt, jedoch nicht wie ich es beheben kann.
    ich habe festgestellt, dass die gesamte ui freezed während die datei downloadet(man kann das fenster nicht verschieben etc.)
    das liegt wohl daran, dass auf den downloadbefehl diese while schleife kommt, deren inhalt erst ausgeführt werden soll, wenn der download fertig ist. der rechner scheint alle resourcen in die durchlaufung der while-schleife zu stecken und der progressbar nicht mehr geupdatet werden kann:

    Code:
    webclient.DownloadFileAsync(new Uri(downloadLink + file), "./" + "TEMPFILE" + file);
    
     while (extracted == false)
     {
    
     if (webclient.IsBusy == false)
     {
     ZipFile zipfile = ZipFile.Read("TEMPFILE" + file);
     zipfile.ExtractAll(Directory.GetCurrentDirectory(), ExtractExistingFileAction.OverwriteSilently);
     extracted = true;
     zipfile.Dispose();
     File.Delete("TEMPFILE" + file);
     }
     }
     updateNumber++;
     }
    denn ich habe mal versucht die while schleife herauszunehmen, und alles läuft. doch leider ist mir das entpacken genauso wichtig wie der download, deswegen kann ich sie nicht weglassen. gibt es eine möglichkeit einen stopper einzubauen, der keine resourcen frisst, sodass die dateien erst entpackt werden, wenn der download abgeschlossen ist?
     
  11. 29. Juli 2013
    AW: Progressbar kommt nicht hinterner

    Die Funktion die du Suchst heißt Application.DoEvents(). Eine einfacher aber evtl. nicht so schöne Lösung (ungetestet)
    Code:
    webclient.DownloadFileAsync(new Uri(downloadLink + file), "./" + "TEMPFILE" + file);
    //ggf. ein Thread.Sleep() mit ein paar ms einfügen, sollte der Status zu langsam toggeln
    while(webclient.isBusy)
    {
     Application.DoEvents();
    }
    ZipFile zipfile = ZipFile.Read("TEMPFILE" + file);
    zipfile.ExtractAll(Directory.GetCurrentDirectory(), ExtractExistingFileAction.OverwriteSilently);
    zipfile.Dispose();
    File.Delete("TEMPFILE" + file);
    Siehe auch: Application.DoEvents-Methode (System.Windows.Forms)
     
    1 Person gefällt das.
  12. 29. Juli 2013
    Zuletzt bearbeitet: 30. Juli 2013
    AW: Progressbar kommt nicht hinterner

    Genau deswegen habe ich vorgeschlagen das sauber in Threads zu packen, damit der TE versteht, was da passiert.

    Zu Application.DoEvents() gibt es hier einen ausführlichen Post, was die Gefahren dabei sind. Das Fazit: "[...] Which is why you shouldn't use DoEvents(). You should use threads."
     
    1 Person gefällt das.
  13. 29. Juli 2013
    AW: Progressbar kommt nicht hinterner

    tausend dank, es läuft endlich!

    Danke nochmal für eure Mühe, jetzt funktioniert alles!

    bw natürlich auch an alle gegeben!
     
  14. Video Script

    Videos zum Themenbereich

    * gefundene Videos auf YouTube, anhand der Überschrift.