Laden...

Dateidownload - Fortschritt über Progressbar

Erstellt von der-Webdesigner vor 15 Jahren Letzter Beitrag vor 15 Jahren 5.102 Views
D
der-Webdesigner Themenstarter:in
54 Beiträge seit 2008
vor 15 Jahren
Dateidownload - Fortschritt über Progressbar

Hey zusammen!

Ich hätte da mal eine Frage. Und zwar habe ich mir eine Downloadklasse geschrieben, wobei pro Download ein eigener Thread ein Objekt dieser Klasse erzeugt und den Download somit startet.

Beim folgenden Code werden bekannterweise die Eventhandler angelegt.

WebClient client = new WebClient();
client.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadFileCompleted);
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgressChanged);

Ich mache nun folgendes:

        private void DownloadProgressChanged(Object sender, DownloadProgressChangedEventArgs e)
        {
            _ProgressBarValue = e.ProgressPercentage;
            updateProgressBarDelegate d = updateProgressBar;
            _PbarDownloadItem.Invoke(d);
        }


        private void DownloadFileCompleted(Object sender, AsyncCompletedEventArgs e)
        {
            
            disposeDownloadDelegate d = disposePanelDownloadItem;
            _PanelDownloadItem.Invoke(d);
        }

        private void updateProgressBar()
        {
            _PbarDownloadItem.Value = _ProgressBarValue;
        }

        private void disposePanelDownloadItem()
        {
            _PanelDownloadItem.Dispose();
        }

Und zwar kann ich aufgrund der Threads nicht auf meine Progressbar zugreifen. Verständlich und über Delegates soweit gelöst.
Jetzt habe ich aber das Problem, dass der Download bei 100% ist, die Progressbar aber immer erst bei der Hälfe - 3/4 der Anzeige. Könnte das durch die Threads kommen? Oder mache ich vom Denken her einen Fehler und bin vielleicht viel zu umständlich an die Sache rangegangen?

Zur Erläuterung: Die Progressbar liegt auf einem Panel. Dieses wird beim beenden des Downloads einfach gelöscht, so dass die Progressbar usw. wieder verschwindet. Doch momentan verschwindet sie noch viel zu früh bzw. ich habe mir über die Messagebox mal ausgeben lassen wann die 100% erreicht sind und das passte definitiv nicht zu dem, was die Progressbar anzeigt.

Vielen Dank für eure Hilfe,
Manuel

R
258 Beiträge seit 2007
vor 15 Jahren

Ich bin mir zwar nicht sicher, aber e.ProgressPercentage; ist ein Wert, den du überprüfen solltest.

Und vllt. gibtst du mal den Wert nicht nur über die Progressbar aus, sondern auch über das Debug-Fenster. Wenn da ein Unterschied auftritt, weisst du, dass es an der PBar oder an der Datenübertragung zu derselbigen liegen muss.

mfg, Rasta

Sogar meine Mailadresse ist .NET 🙂

4.506 Beiträge seit 2004
vor 15 Jahren

Hallo Manuel,

habe ich das richtig verstanden, Du lädst mehrere Dateien in mehreren Threads (immer eine Datei pro Thread) herunter, hast aber nur ein einziges Panel mit einem Fortschrittsbalken?

Woher weiß dann ein einzelner Thread, wie der Gesamtdownloadfortschritt ist?

Entweder ist es so banal, oder ich habe Dich falsch verstanden, was ich eher vemute.

Grüße
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

D
der-Webdesigner Themenstarter:in
54 Beiträge seit 2008
vor 15 Jahren

Danke für die Antworten ihr beiden.

Also ich habe pro Download dann auch eine Progressbar, die angelegt wird und die ich mir dann als Membervar in der Filedownload-Klasse merke. Somit hat jeder Download einen Fortschritt und wenn dieser fertig ist wird dessen Progressbar einfach wieder ausgeblendet.

Ich habe den Wert auch mal ausgegeben, wenn er genau 100 ist, heißt if-Abfrage und getestet wann e.ProgressPercentage == 100 und das als MessageBox ausgeben lassen. Die Box erscheint aber ebenfalls bevor die Progressbar voll ist.

Kann es sein, dass das einfach zu schnell ist? Denn bei eine 3MB Datei kommt er bis 3/4, bei 98kb nur bis 10%. Klar, ich könnte jetzt einfach hingehen und den Wert beim Complete-Event auf 100% setzen, aber wirklich schön finde ich diese Lösung nicht.

Vielen Dank für eure Hilfe,
Manuel

Edit: Wenn ich beim Complete-Event einfach mal nichts mache läuft der Statusbalken brav voll und schaut auch gut aus. Ist vielleicht einfach die Übergabe an die GUI zu langsam? Nur wie sollte ich dann vorgehen?

4.506 Beiträge seit 2004
vor 15 Jahren

Hallo der-Webdesigner,

kann es sein, dass Du zu oft eine GUI-Aktualisierung vornehmen möchtest? Wie oft und wann schickst Du denn eine aktualisierung des Fortschrittbalkens?

Dann ist es so, dass er mit der Event-Verarbeitung irgendwann nicht mehr hinterherkommt, da die GUI in der Regel viel langsamer als der Download Thread arbeitet.

Grüße
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

J
163 Beiträge seit 2007
vor 15 Jahren

Benuzt du Vista?

D
der-Webdesigner Themenstarter:in
54 Beiträge seit 2008
vor 15 Jahren

Ja, ich verwende Vista. Scheinbar ist das aber auch unter XP ein Problem. Könnte ich gleich aber auch mal kurz testen.

Die GUI aktualisiere ich einfach bei jedem Aufruf des Events. Wie oft das vorkommt weiss ich nicht, aber ich denke das ist von der Größe des Downloads abhängig. Aber selbst wenn ich beim DownloadFileCompleted-Event den Wert der ProgressBar auf 100 setze, ist die GUI langsamer als dass die ProgressBar gelöscht wird. Daher werde ich es wahrscheinlich einfach so machen, dass die Leisten einfach sichtbar bleiben bis der Nutzer sie selbst wegklickt. Dann bestände das Problem auch nicht. 🙂

Dankeschön für eure Hilfe,
Manuel

4.506 Beiträge seit 2004
vor 15 Jahren

Hallo der-Webdesigner,

nochmal trotzdem die Frage, aufgrund von Windows-System-Messages-Überlastung würde ich Dir raten zu überprüfen wie oft Du Prozessfortschritte per Event übermittelst, z.B. wäre es überflüssig 10x den Prozentschritt 10% zu übermitteln.

Als ich das erste Mal solch eine Anforderung umsetzte bin ich auf so etwas reingefallen (systematisch, syntaktisch inkorrekt!):



byte[] buffer = new byte[128];
long fileLength = GetFileLength();
int i = 1;

while (read.EOF)
{
   // Nächsten Pufferblock lesen
   ReadNextBuffer(ref buffer);

   // Berechnung des aktuellen Standes;
   RaiseProgressUpdate(i * byte[].Length() / fileLength);

   // Zähler erhöhen
   i++;
}


Was dann zur Folge hatte, dass er bei Dateien, die z.B. 1 MB groß waren ca. 8120 Mal das Event ausgelöst hat, was deutlich zu viel war und die GUI überhaupt nicht hinterherkam.

Lösung ist, ein wenig Intelligenz einzubauen, so dass das Event z.B. nur alle Sekunde gefeuert wird, oder dass nur wenn sich der tatsächliche Prozentwert ändert etc.

Grüße
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

P
66 Beiträge seit 2006
vor 15 Jahren

Ich habe vor kurzem ein ähnliches Testprogramm geschrieben, mit dem ich Updates über den WebClient herunterladen. Ich hänge den Code mal an.


public partial class Form1 : Form {
		private WebClient client;

		public Form1() {
			InitializeComponent();

			client = new WebClient();
			client.DownloadFileCompleted += new AsyncCompletedEventHandler( client_DownloadFileCompleted );
			client.DownloadProgressChanged += new DownloadProgressChangedEventHandler( client_DownloadProgressChanged );
            client.DownloadFileAsync( "http://www.update.de/update.exe", @"C:\temp\update.exe" );
		}

		void client_DownloadProgressChanged( object sender, DownloadProgressChangedEventArgs e ) {
			progressBar1.Value = e.ProgressPercentage;
			label1.Text = e.BytesReceived + " / " + e.TotalBytesToReceive;
			label2.Text = e.ProgressPercentage + "%";
		}

		void client_DownloadFileCompleted( object sender, AsyncCompletedEventArgs e ) {
			DialogResult result = MessageBox.Show( "Update installieren?", "Update installieren", MessageBoxButtons.YesNo );

			if ( result == DialogResult.Yes ) {
				Process p = new Process();
				p.StartInfo = new ProcessStartInfo( @"C:\temp\update.exe" );
				p.Start();
			}
		}
}

Da der WebClient das Asynchronous-Pattern (vgl. dotnetpro 12/2007 oder http://msdn.microsoft.com/de-de/library/wewwczdw(VS.80).aspx) implementiert, vgl. BackgroundWorker, musst du keine Invoke-Aufrufe machen um die Progressbar anzusprechen. Dies wird direkt im GUI-Thread ausgeführt, wenn die WebClient-Instanz im GUI-Thread erstellt wurde.

In deinem Fall, so wie ich das sehe, musst du nur für jeden Download eine WebClient-Instanz und eine zugehörige Progressbar erstellen und die Progressbar über die Ereignisse des WebClient aktualisieren.

Vielleicht hilft dir das etwas weiter.