Entwickler-Ecke
WinForms - Erledigt: C# Blockieren der GUI verhindern ?
Findus - So 21.12.08 02:32
Titel: Erledigt: C# Blockieren der GUI verhindern ?
Hallo zusammen ..
Ich bin ein Blutiger Anfänger in sachen Programmieren und hätte ein paar Fragen die mir auch Google und Co. nach Stundenlanger Suche
nicht beantworten konnten. Mir fehlt als Newcomer einfach noch das Verständnis für viele Sachen.
In meinem Programm lade ich mir Daten auf meinem Rechner mit denen ich dann wieder arbeiten kann. Dazu lese ich ein csv-file ein.
Nun beim Download wird mir in einem neuen Form eine progressbar angezeigt. Der Fortschritt des Downloads bringt die GUI wie nicht anderest zu erwarten zum erliegen bzw. blockiert sie.
Hier erst mal der Code:
C#-Quelltext
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:
| private void button1_Click(object sender, EventArgs e) { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(@"http://www.URL2GO.de/xxx.csv"); request.Method = "GET"; HttpWebResponse hwrs = (HttpWebResponse)request.GetResponse(); BinaryReader br = new BinaryReader(hwrs.GetResponseStream()); FileStream fs = new FileStream(@"xxx.csv", FileMode.Create, FileAccess.Write); BinaryWriter bw = new BinaryWriter(fs);
progressBar1.Minimum = 0; progressBar1.Maximum = (int)hwrs.ContentLength; progressBar1.Step = 1; this.Text = String.Format("Download {0} bytes", hwrs.ContentLength);
try { while (true) { progressBar1.PerformStep(); bw.Write(br.ReadByte()); } } catch (Exception) { bw.Close(); fs.Close(); } Application.Restart(); } |
Im Anhang ist das Form zu sehen wie das ganze aussieht.
So nun meine Frage:
1. Wie könnte ich es anstellen das die GUI auf einfachste Weise nicht mehr blockiert wird. Themen wie Threading und Backgroundworker habe ich gelesen aber leider nicht verstanden :(
Evtl. hat ja jemand ein ganz simples Beispiel für mich.
Was erwarte ich NICHT:
Ich erwarte keine Fertige Lösung, auch wenn das der einfachste Weg ist. :) Nein, ich will das schon auch verstehen.
So, ich hoffe ich habe nicht gleich gegen die Regeln verstoßen und zumindest richtig gepostet.
Gruß Findus
Moderiert von
Christian S.: C#-Tags hinzugefügt
Moderiert von
Christian S.: Topic aus IO, XML und Registry verschoben am So 21.12.2008 um 01:37
Moderiert von
Christian S.: Überflüssige Zeilenumbrüche entfernt
PerryRhodan - So 21.12.08 10:18
Hallo Findus,
auf einfachste Weise würden Dir die Aufrufe
C#-Quelltext
1: 2:
| progressBar1.Invalidate(); progressBar1.Update(); |
schon weiterhelfen.
Deine Programmlogik arbeitet im gleichen Thread wie die GUI. Standardmäßig wird kein neues Zeichnen der Controls erzwungen sondern nur "vermerkt", dass das Control bei nächster Gelegenheit neu gezeichnet werden soll. Dies passiert, wenn Zeit dafür da ist. Da Deine Logik aber alle dem Thread zugeteilte Rechenzeit nutzt, wird die GUI nicht neu gezeichnet.
Durch den Invalidate-Aufruf erzwingst Du, dass das gesamte Control beim nächsten Zeichnen neu gezeichnet werden muss. Durch den Update-Aufruf erzwingst Du dann das Zeichnen selbst.
Grüße
PerryRhodan
JüTho - So 21.12.08 11:36
Der "Unsterbliche" hat recht: mit längeren Arbeiten wird die GUI "immer" blockiert.
Ich empfehle, dass Du Dir den BackgroundWorker nochmals vornimmst. Das einführende Beispiel in der SDK-Doku/MSDN ist meiner Ansicht nach sehr gut geeignet.
Gruß Jürgen
Findus - So 21.12.08 12:57
Hallo Ihr beiden ..
Erst mal thanks für die Info. Ich hab die progressbar Parameter mal mit eingebaut. Leider bringt das nicht den gewünschten Erfolg. Das liegt aber sicher an meinem newbie Programmierstiel :) :)
Mit dem backgroundworker steh ich auf Kriegsfuß. Ich bekomme das einfach nicht gebacken. Ich glaube da muss ein Workshop her .. lol
Einstweilen vielen Dank !
Findus
Moderiert von
Christian S.: Überflüssige Zeilenumbrüche entfernt
JüTho - So 21.12.08 13:27
Tja, wenn Du Deine Versuche mit dem BackgroundWorker zitieren würdest, könnten wir auch dazu helfen.
Wenn es nur darum geht, eine (nicht zu große) Datei zu laden und zu speichern, dann könntest Du auch WebClient.DownloadFile-Methode verwenden. Ich benutze das regelmäßig und vermisse die Fortschrittsanzeige bei 50 kB pro Datei überhaupt nicht.
Jürgen
Findus - So 21.12.08 14:30
JüTho hat folgendes geschrieben : |
Tja, wenn Du Deine Versuche mit dem BackgroundWorker zitieren würdest, könnten wir auch dazu helfen.
Wenn es nur darum geht, eine (nicht zu große) Datei zu laden und zu speichern, dann könntest Du auch WebClient.DownloadFile-Methode verwenden. Ich benutze das regelmäßig und vermisse die Fortschrittsanzeige bei 50 kB pro Datei überhaupt nicht.
Jürgen |
Erst mal wieder Danke Jürgen für die Antwort. Zuerst, ja ich habe auch nur ein File das nur ca. 100 kB groß ist, aber finde es schön wenn sich was bewegt da ich nur eine ISDN Leitung habe und wohl die nächsten Jahre nichts anderes bekomme. Ich werde mich nun nochmal mit dem BackgroundWorker beschäftigen und dann meine Probleme schlildern.
Thanks Findus
Moderiert von
Christian S.: Überflüssige Zeilenumbrüche entfernt
Findus - So 21.12.08 14:52
Hier also meine Erfahrungen/Fehler und Dummheiten die ich so mache.
Also, erst mal ich arbeite mit
MS Visual Studio 2008 und ziehe mir den
backgroundWorker aus der Toolbar auf mein DownloadForm. Nun setze ich die Parameter
WorkerReportsProgress und
WorkerSupportsCancellation des Workers auf
True.
Der Quellcode schaut nun so aus.
C#-Quelltext
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: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86:
| using System; using System.Collections.Generic; using System.ComponentModel; using System.Windows.Forms; using System.IO; using System.Net;
namespace XYZ { public partial class DownloadForm : Form { public DownloadForm() { InitializeComponent(); }
private void button1_Click(object sender, EventArgs e) { this.backgroundWorker1.RunWorkerAsync(); this.button1.Enabled = false;
while (this.backgroundWorker1.IsBusy) { Application.DoEvents(); }
this.button1.Enabled = true; }
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(@"http://URL2GO/xyz.csv"); request.Method = "GET"; HttpWebResponse hwrs = (HttpWebResponse)request.GetResponse(); BinaryReader br = new BinaryReader(hwrs.GetResponseStream()); FileStream fs = new FileStream(@"xyz.csv", FileMode.Create, FileAccess.Write); BinaryWriter bw = new BinaryWriter(fs);
progressBar1.Invalidate(); progressBar1.Minimum = 0; progressBar1.Maximum = (int)hwrs.ContentLength; progressBar1.Step = 1;
this.Text = String.Format("Download {0} bytes", hwrs.ContentLength);
try { while (true) { progressBar1.PerformStep(); progressBar1.Update(); bw.Write(br.ReadByte()); } } catch (Exception) { bw.Close(); fs.Close(); } Application.Restart(); }
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Error == null) { MessageBox.Show("Complete"); } else { MessageBox.Show( "Failed", "Failed", MessageBoxButtons.OK, MessageBoxIcon.Error); } }
private void button2_Click(object sender, EventArgs e) { Close(); } } } |
Nur passieren tut nichts :( Nach einem klick auf OK tut sich garnichts.
Vermutlich ein ganz ganz großer Denkfehler meinerseits. Newbie halt.
Gruß Findus
Moderiert von
Christian S.: Überflüssige Zeilenumbrüche entfernt
JüTho - Mo 22.12.08 19:07
Hallo,
sorry, bisher hatte ich keine Zeit, Deinen Code zu untersuchen.
Du machst vor allem folgenden Fehler - zitiert aus der
SDK-Doku/MSDN:
Zitat: |
BackgroundWorker-Klasse Hinweis
Im DoWork-Ereignishandler dürfen keine Benutzeroberflächenobjekte bearbeitet werden. Verwenden Sie stattdessen zum Kommunizieren mit der Benutzeroberfläche das ProgressChanged-Ereignis und das RunWorkerCompleted-Ereignis. |
Du rufst dort mehrfach die ProgressBar und Textbox auf, das ist falsch. Es geht ja gerade darum, dass die GUI und die Hintergrundarbeit getrennt werden.
Diese Zeile aus dem Beispiel der Doku ist besonders wichtig:
C#-Quelltext
1:
| backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged; |
Die ProgressBar wird eigentlich nur in der
backgroundWorker1_ProgressChanged-Methode benutzt.
Noch ein paar schnelle Bemerkungen:
* DoWork selbst macht fast nichts, sondern die Arbeit wird in eine getrennte Methode ReadWrite (im Beispiel ComputeFibonacci) ausgelagert.
* Die IsBusy-Schleife kann entfallen.
* button1.Enabled = true gehört in das Completed-Ereignis.
Bitte versuchs noch einmal. Jürgen
Findus - Di 23.12.08 01:29
Hallo Jürgen,
also ich hab mich nochmal an die Arbeit gemacht. Bislang leider ohne Erfolg :( Ich denke ich bin auch dem Richtigen weg, aber leider fehlt noch der Erfolg. Hier mal mein derzeitiger Code der allerdings noch nicht vollständig ist. Es scheitert noch an der Fehlerabfrage bei Cancel.
Die Arbeit macht er natürlich auch noch nicht.
C#-Quelltext
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: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120:
| using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.IO; using System.Net; using System.Reflection;
namespace XYL { public partial class DownloadForm : Form { public DownloadForm() { InitializeComponent(); InitializeBackgoundWorker(); }
private void InitializeBackgoundWorker() { backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork); backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler( backgroundWorker1_RunWorkerCompleted); backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler( backgroundWorker1_ProgressChanged); }
private void okButton_Click(object sender, EventArgs e) { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(@"http://URL2GO/xyl.csv"); request.Method = "GET"; HttpWebResponse hwrs = (HttpWebResponse)request.GetResponse(); BinaryReader br = new BinaryReader(hwrs.GetResponseStream()); FileStream fs = new FileStream(@"xyl.csv", FileMode.Create, FileAccess.Write); BinaryWriter bw = new BinaryWriter(fs);
this.okButton.Enabled = false;
this.cancelButton.Enabled = true;
progressBar1.Minimum = 0; progressBar1.Maximum = (int)hwrs.ContentLength; progressBar1.Step = 1;
this.Text = String.Format("Download {0} bytes", hwrs.ContentLength);
backgroundWorker1.RunWorkerAsync(); }
private void cancelButton_Click(object sender, EventArgs e) { this.backgroundWorker1.CancelAsync();
cancelButton.Enabled = false; }
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker worker = sender as BackgroundWorker;
try { while (true) { bw.Write(br.ReadByte()); } } catch (Exception) { bw.Close(); fs.Close(); }
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Error != null) { MessageBox.Show(e.Error.Message); } else if (e.Cancelled) { resultLabel.Text = "Canceled"; } else { resultLabel.Text = "???"; }
okButton.Enabled = true;
cancelButton.Enabled = false;
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { this.progressBar1.Value = e.ProgressPercentage; }
} } |
Was natürlich auch nicht geht ist das schreiben der Datei in DoWork da ich die Variablen ja nicht aus der Methode okButton_Click übernehmen kann. Was die Anzeige der progressbar angeht steht dann wieder auf einem anderen Blatt. Vielleicht könnte man nochmal Hilfestellung geben, Danke ..
Ich hoffe das nun zumindest die Ansätze richtig sind.
Gruß Findus
Moderiert von
Christian S.: Überflüssige Zeilenumbrüche entfernt
JüTho - Di 23.12.08 17:20
So, ich habe das Projekt jetzt selbst bearbeitet. Folgende Änderungen sind erforderlich bzw. sinnvoll.
Der
BackgroundWorker ist in den Tools enthalten. Er kann also im
Designer vorbereitet werden; dann ist die Methode
InitializeBackgoundWorker() in der Designer.cs enthalten.
Der
entscheidende Fehler ist jetzt, dass der Download-Aufruf im
ButtonClick enthalten ist. Das gehört alles in den Arbeitsablauf des BackgroundWorker, wie ich oben schrieb:
JüTho hat folgendes geschrieben : |
Es geht ja gerade darum, dass die GUI und die Hintergrundarbeit getrennt werden. |
ButtonClick enthält nur noch solche Maßnahmen:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23:
| void OkButtonClick(object sender, EventArgs e) { okButton.Enabled = false; cancelButton.Enabled = true;
progressBar1.Minimum = 0; progressBar1.Maximum = 100; progressBar1.Value = 0; progressBar1.Step = 1;
backgroundWorker1.RunWorkerAsync();
while (backgroundWorker1.IsBusy) { Application.DoEvents(); } } |
So kann das
DoWork aussehen:
C#-Quelltext
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:
| void BackgroundWorker1DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker worker = sender as BackgroundWorker;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url.Text); request.Method = "GET"; HttpWebResponse hwrs = (HttpWebResponse)request.GetResponse(); BinaryReader br = new BinaryReader(hwrs.GetResponseStream()); FileStream fs = new FileStream(Path.Combine(Application.CommonAppDataPath, "temp.csv"), FileMode.Create, FileAccess.Write); BinaryWriter bw = new BinaryWriter(fs); long summary = hwrs.ContentLength; int section = (int)summary / 100; int progress = 0; int counter = 0; try { while (true) { bw.Write(br.ReadByte()); counter++; if (counter >= section) { worker.ReportProgress(++progress, summary); counter = 0; } } } catch { } finally { bw.Close(); br.Close(); fs.Close(); hwrs.Close(); } } |
Entscheidend ist, dass die Arbeit in
DoWork enthalten ist und
ReportProgress bewusst aufgerufen wird. Dazu müssen die Bytes mitgezählt werden. (Zu einer Alternative, die mir eigentlich besser gefällt, schreibe ich unten noch etwas. Hier geht es mir erst um eine funktionierende Lösung.)
cancelButton_Click und
backgroundWorker1_RunWorkerCompleted sind in Ordnung.
backgroundWorker1_ProgressChanged habe ich erweitert, damit die Gesamtzahl der Bytes angezeigt werden kann (siehe oben den Parameter summary):
C#-Quelltext
1: 2: 3: 4: 5: 6: 7:
| void BackgroundWorker1ProgressChanged(object sender, ProgressChangedEventArgs e) { if (progressBar1.Value == 0) { Text = String.Format("Download {0} bytes", (long)e.UserState); } progressBar1.Value = e.ProgressPercentage; } |
Weitere Hinweise: Ich hatte ja schon bezweifelt, dass bei einem solchen Download eine ProgressBar sinnvoll ist. Der Arbeitsablauf hat dies bestätigt: Durch GetResponse wird die Datei abgeholt und im Arbeitsspeicher zwischengespeichert; dies erfolgt ohne Anzeige des Fortschritts. Erst beim Abspeichern ist die Anzeige möglich. (Wie es z.B. im Browser gemacht wird, weiß ich nicht; ich kenne nur die wichtigsten Zusammenhänge mit einem WebClient.)
Das Byte-weise Abspeichern ist auch nicht sehr sinnvoll. Besser ist ein Umspeichern in Blöcken, etwa so:
C#-Quelltext
1: 2: 3: 4: 5:
| while (true) { bw.Write(br.ReadBytes(section)); worker.ReportProgress(++progress, summary); } |
Aber entgegen der Beschreibung von ReadBytes wird der letzte (unvollständige) Block nicht gelesen (und ich hatte keine Lust, das auch noch zu prüfen).
Ich hoffe, dass Dir mit diesem Beispiel und den Erläuterungen der Zusammenhang klarer wird.
Viel Erfolg und Frohe Weihnachten! Jürgen
Findus - Di 23.12.08 19:15
Hallo Jürgen,
erst mal vielen vielen Dank für Deine ausführliche Hilfe. So wurde mir Schritt für Schritt klarer um was es geht und auf was es ankommt.
Das ganze Funktioniert nun erst mal. Was mich allerdings wundert ist das die Progressbar 2 mal durchlaufen wird !? Von der Zeit her könnte es auch
fast sein das das File 2 mal geladen wird. Leider fällt mir keine Lösung ein um dies zu testen.
Was noch nicht geht ist das Cancel. Ein Abbruch ist nicht möglich. Aber das bekomme ich denke ich schon gebacken.
Gruß Findus
Moderiert von
Christian S.: Überflüssige Zeilenumbrüche entfernt
JüTho - Di 23.12.08 19:28
Findus hat folgendes geschrieben : |
Was mich allerdings wundert ist das die Progressbar 2 mal durchlaufen wird !? Von der Zeit her könnte es auch fast sein das das File 2 mal geladen wird. Leider fällt mir keine Lösung ein um dies zu testen. |
Dann kontrolliere noch einmal Deinen Code. Vielleicht wird das Einlesen noch in ButtonClick aufgerufen...
Findus hat folgendes geschrieben : |
Was noch nicht geht ist das Cancel. Ein Abbruch ist nicht möglich. Aber das bekomme ich denke ich schon gebacken. |
Ach, das hatte ich vergessen zu testen. Ich hatte es mit eigenem Code verglichen, und mir war nichts aufgefallen. Allerdings musst Du bedenken, dass Windows etwas Zeit braucht, um auf solch eine Anweisung zu reagieren; vielleicht ist die Datei schneller gespeichert...
Gruß Jürgen
Findus - Di 23.12.08 20:03
Hallo nochmal Jürgen ..
Ich habe nun den Code nun nochmal geprüft auf doppelte Aufrufe. Also mir fällt diesbezüglich nichts auf, was nach nichts heisen will. :)
C#-Quelltext
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: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142:
| using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.IO; using System.Net; using System.Reflection;
namespace XYZ { public partial class DownloadForm : Form { public DownloadForm() { InitializeComponent(); InitializeBackgoundWorker(); }
private void InitializeBackgoundWorker() { backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork); backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted); backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged); }
private void okButton_Click(object sender, EventArgs e) { okButton.Enabled = false; cancelButton.Enabled = true;
progressBar1.Minimum = 0; progressBar1.Maximum = 100; progressBar1.Value = 0; progressBar1.Step = 1;
backgroundWorker1.RunWorkerAsync();
while (backgroundWorker1.IsBusy) { Application.DoEvents(); } }
private void cancelButton_Click(object sender, EventArgs e) { this.backgroundWorker1.CancelAsync(); cancelButton.Enabled = false; }
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker worker = sender as BackgroundWorker;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(@"URL2GO/temp.csv"); request.Method = "GET"; HttpWebResponse hwrs = (HttpWebResponse)request.GetResponse(); BinaryReader br = new BinaryReader(hwrs.GetResponseStream()); FileStream fs = new FileStream(@"temp.csv", FileMode.Create, FileAccess.Write); BinaryWriter bw = new BinaryWriter(fs);
long summary = hwrs.ContentLength; int section = (int)summary / 100; int progress = 0; int counter = 0;
try { while (true) { bw.Write(br.ReadByte()); counter++; if (counter >= section) { worker.ReportProgress(++progress, summary); counter = 0; } } } catch { } finally { bw.Close(); br.Close(); fs.Close(); hwrs.Close(); } }
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Error != null) { MessageBox.Show(e.Error.Message); } else if (e.Cancelled) { this.Text = "Canceled"; } else { Close(); }
okButton.Enabled = true;
cancelButton.Enabled = false; }
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { if (progressBar1.Value == 0) { Text = String.Format("Download {0} bytes", (long)e.UserState); } progressBar1.Value = e.ProgressPercentage; }
} } |
Vielleicht kann man ja nochmal einen Blick drauf werfen. Wie gesagt die progressbar läuft zwei mal durch und erweckt den Eindruck das die Datei zwei mal geladen wird.
Gruß Findus
Findus - Mi 24.12.08 00:58
Hallo nochmal @all ..
Ich hab nun nochmal ein paar Tests durchlaufen. Er schreibt das File definitiv zwei mal. Getestet hab ich das einfach indem ich das FileMode.Create durch ein FileMode.Append ersetzt habe. Also, File vorher gelöscht und nochmal auf Download geklick. Resultat ist eine doppelte Dateigröße :(
Gruß Findus
JüTho - Mi 24.12.08 11:59
Hallo,
tut mir leid, ich sehe auch nichts. Das einzige, was mir etwas "kritisch" vorkommt, ist das Close() in RunWorkerCompleted. Aber das dürfte eigentlich so etwas nicht auslösen: Es ist Bestandteil des Formulars (des GUI-Threads) und wird ausgelöst, wenn der BackgroundWorker wirklich abgeschlossen ist; was sollte das also Schlimmes verursachen?
Vielleicht hat einer der anderen NET-Kenner eine Idee?!
Gruß und Frohe Weihnachten! Jürgen
Christian S. - Mi 24.12.08 12:16
Ich glaube, es liegt an der Methode, wie hier entschieden wird, ob ein Fortschritt "gemeldet" wird.
Nehmen wir an, summary ist 190. Dann ist section = (int) summary / 100 nur 1. Das heißt bei jedem Durchlauf der while-Schleife wäre counter >= section. Wenn aber 100mal ein Fortschritt vermeldet wurde (also eigentlich 100% erreicht sein müssten), dann sind noch 90 Bytes zu lesen. Und es wird auch noch 90mal ein Fortschritt vermeldet.
Wenn nun noch (Vermutung!) ReportProgress bei einem Fortschrittswert von > 100 wieder von vorne anfängt, würde die Prpgressbar mehr als einmal durchlaufen.
Findus - Mi 24.12.08 12:55
Hallo Christian S ..
Danke für die Info. Das selbe hab ich allerdings auch schon vermutet und die while-Schleife mal raus genommen. Selber effekt. Nur das man halt nichts mehr sieht :) :)
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| |
Schöne Weihnachten
Findus
Christian S. - Mi 24.12.08 13:05
Dann versuch mal, sowohl den BinaryReader als auch den BinaryWriter wegzulassen. Die brauchst Du eh nicht, weil die Streams eine ReadByte bzw. WriteByte Methode haben.
Findus - Mi 24.12.08 14:56
Christian S. hat folgendes geschrieben : |
Dann versuch mal, sowohl den BinaryReader als auch den BinaryWriter wegzulassen. Die brauchst Du eh nicht, weil die Streams eine ReadByte bzw. WriteByte Methode haben. |
Hallo Christian. Nun muss sich natürlich wieder der Anfänger in mir outen. Ich bekomme das ohne BR und BW nicht gebacken :(
Gruß und Frohes Fest
Findus
Christian S. - Mi 24.12.08 15:18
Den Writer lässt Du einfach weg und da, wo Du im Moment noch bw.Write aufrufst, rufst Du stattdessen fs.WriteByte auf.
Den Reader lässt Du auch weg, und schnappst Dir stattdessen direkt den RepsonseStream (Stream resp = hwrs.GetResponseStream();) Der hat dann die Methode ReadByte.
Findus - Mi 24.12.08 16:34
Christian S. hat folgendes geschrieben : |
Den Writer lässt Du einfach weg und da, wo Du im Moment noch bw.Write aufrufst, rufst Du stattdessen fs.WriteByte auf.
Den Reader lässt Du auch weg, und schnappst Dir stattdessen direkt den RepsonseStream (Stream resp = hwrs.GetResponseStream();) Der hat dann die Methode ReadByte. |
OK Christian, soweit hoffe ich klar. Nun passiert folgendes: Beim Download läuft durch und dann bleibt er hängen bis ein Fehler durch Application.DoEvents(); kommt .. Schau ich mir den Inhalt nun an ist das File um ein vielfaches größer weil noch lauter:
Quelltext
1: 2: 3: 4:
| ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ |
geschieben wurde.
Mein backgroundWorker1_DoWork Methode sieht nun so aus.
C#-Quelltext
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:
| private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker worker = sender as BackgroundWorker;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(@"URL/temp.csv"); request.Method = "GET"; HttpWebResponse hwrs = (HttpWebResponse)request.GetResponse(); FileStream fs = new FileStream(@"temp.csv", FileMode.Create, FileAccess.Write);
long summary = hwrs.ContentLength; int section = (int)summary / 100; int progress = 0; int counter = 0;
Stream data = hwrs.GetResponseStream();
try { while (true) { fs.WriteByte((byte)data.ReadByte()); counter++; if (counter >= section) { worker.ReportProgress(++progress, summary); counter = 0; } if (worker.CancellationPending) { e.Cancel = true; } } } catch { } finally { fs.Close(); hwrs.Close(); } } |
Merry Christmas
Findus
Kha - Mi 24.12.08 17:28
Bei einer Endlosschleife ist auch eine unendlich große Datei kein (Weihnachts-)Wunder :D . Im Gegensatz zum StreamReader wirft MemoryStream.ReadByte keine Exception am Ende:
Zitat: |
Return Value
Type: System.Int32
The byte cast to a Int32, or -1 if the end of the stream has been reached. |
Wenn ReadByte also -1 zurückliefert, musst du die Schleife mit
breakverlassen.
Findus - Do 25.12.08 00:02
Also ich fummel da nun schon ewig rumm. Ich bekomm das einfach nicht gebacken. Man sollte denken das doch ein Ausstieg auch mit folgendem Code möglich sein sollte.
C#-Quelltext
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:
| ...
long summary = hwrs.ContentLength; int section = (int)summary / 100; int progress = 0; int counter = 0;
Stream data = hwrs.GetResponseStream();
try { while (true) { fs.WriteByte((byte)data.ReadByte());
if (counter == summary) { break; }
counter++; if (counter >= section) { worker.ReportProgress(++progress, summary); counter = 0; } if (worker.CancellationPending) { e.Cancel = true; } }
... |
Das mit dem ReadByte -1 versteh ich zwar aber kann es trotz vielen lesens nicht umsetzen. Vielleicht will mir da ja noch jemand helfen ;)
Gruß Findus
Findus - Do 25.12.08 18:03
Da sich das eigentliche Problem mit der blockierenden GUI ja erledigt hat betrachte ich diese Frage mal als beantwortet.
Gruß Findus
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!