Entwickler-Ecke
WinForms - Buttonclick während der Laufzeit einer Klasse mitteilen
kmkhl - Do 10.10.13 14:52
Titel: Buttonclick während der Laufzeit einer Klasse mitteilen
MoinMoin
unzwar habe ich vereinfacht gesagt eine WinForm (Form1) und eine Klasse (BeispielKlasse) die Rechnungen macht.
Während BeispielKlasse ihre Rechnungen durchführt, ist Form1 geöffnet und bietet mir die Möglichkeit den Button "Abbrechen" zu drücken.
Ist dieser gedrückt wurden, übergibt er dem bool "abgebrochen" den Wert "true".
Wenn ich jetzt in BeispielKlasse in einer Schleife den Wert von Form1.abgebrochen abfrage, hat dieser immer nur den Anfangswert "false" und wird auch nach einem Klick auf den Button nicht "true" wobei er in der Form1 nach dem Klick "true" wird.
Warum? :shock:
Ich will das die BeispielKlasse bescheid weiß, wenn der Wert von "abgebrochen" "true" wird, damit sie dann aufhört zu rechnen.
Ich danke für Eure Hilfe. :angel:
Ralf Jansen - Do 10.10.13 15:00
a.) Schaut den deine BeispielKlasse auch auf die Form1 Instanz die angezeigt wird oder kennt die nur irgendeine andere?
b.) Es ist eher schlecht das eine Klasse die nur einer Berechnung von irgendwas dient eine bestimmte Klasse des UserInterfaces kennen muß.
Die Form sollte der Klasse bescheid sagen da hat sich was geändert und nicht umgekehrt sich die Info bei der Form abholen müßen. Im klassischen Design ist es ok wenn die Form die Beispielklasse kennt aber nicht umgekehrt.
kmkhl - Do 10.10.13 15:09
"Die Form sollte der Klasse bescheid sagen da hat sich was geändert[...]"
Da wirst du wohl Recht haben, dort werde ich mal anecken und meinen Code umschreiben.
Vielen Dank!
kmkhl - Do 10.10.13 15:24
nein, ich bekomme es einfach nicht hin.
hier mal mein Code:
Form1:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| public partial class AusgabeForm : Form { BeispielKlasse bspKlasse = new BeispielKlasse();
public AusgabeForm() { InitializeComponent(); }
private void abbrechenButton_Click(object sender, EventArgs e) { bspKlasse.abbruch = true; } } |
Ausschnitt BeispielKlasse:
C#-Quelltext
1: 2: 3: 4: 5: 6:
| public bool abbruch = false;
while (restRechnung != 0 && !abbruch) { rechnen(); } |
Moderiert von
Th69: Codeformatierung überarbeitet.
kmkhl - Do 10.10.13 15:38
C#-Quelltext
1: 2: 3: 4: 5:
| private void abbrechenButton_Click(object sender, EventArgs e) { bspKlasse.abbruch = true; Application.DoEvents(); } |
leider hat das nichts geändert
Ralf Jansen - Do 10.10.13 15:49
Setz das DoEvents mal in die While Schleife in der du regelmäßig rechnen aufrufst.
Ich frage mich allerdings wenn du bisher kein DoEvents verwendet hast und rechnen auch nicht in einem eigene Thread stattfand wie konnte dann überhaput jemand den Knopf drücken? Eigentlich sollte solange dein rechnen läuft die UI gesperrt sein weil dein Prozess seine gesammte Rechnenzeit in rechnen() verbrät und nie etwas zum Bearbeiten der UI übrig ist. Der Klick auf den Knopf würde so dann eigentlich erst nachdem rechnen() durch ist verarbeitet werden. Und die visuelle Reaktion (das eindrücken des Buttons) auch erst dann sichtbar sein.
Wenn du gleichzeitig eine UI am laufen halten willst und parallel eine Berechnung ausführen willst ist das eine Aufgabe für Threads. DoEvents ist nur eine ganz notdürtige Notlösung (man beachtet die doppelt auftetende Not).
kmkhl - Do 10.10.13 15:56
auch in dem while bringt mich Application.DoEvents(); nicht zur Lösung. Wäre wohl auch zu einfach gewesen.
Ich werde es mal mit Threading probieren.
kmkhl - Do 10.10.13 17:00
gibt es noch eine andere Möglichkeit?
kmkhl - Do 10.10.13 17:06
mit dem habe ich auch schon gearbeitet, nur nicht dran gedacht das ich ihn auch hierfür verwenden könnte.
Ich werde es mal versuchen, Danke!
kmkhl - Fr 11.10.13 09:42
:(
es läuft einfach nicht..
..oder besser gesagt es läuft trotzdem weiter -.-
Form1:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
| public partial class AusgabeForm : Form { BeispielKlasse bspKlasse = new BeispielKlasse();
public AusgabeForm() { InitializeComponent(); } private void startButton_Click(object sender, EventArgs e) { bspKlasse .bw_DoWork(new DoWorkEventArgs(0)) }
private void abbrechenButton_Click(object sender, EventArgs e) { bspKlasse.bw.CancelAsync(); MessageBox.Show("gestoppt"); abbrechenButton.Enabled = false; } } |
Ausschnitt BeispielKlasse:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| public BackgroundWorker bw = new BackgroundWorker();
public BeispielKlasse() { bw.WorkerSupportsCancellation = true; }
public bool bw_DoWork(DoWorkEventArgs e) { while (restRechnung != 0 && !e.Cancel) { rechnen(); } } |
Ralf Jansen - Fr 11.10.13 09:54
Sieh dir nochmal aus der Hilfe zum Backgroundworker das Codebeispiel an. Insbesondere den Teil wie der Backgroundworker gestartet wird. Du hast zwar einen Backgroundworker erzeugt aber ihn dann gar nicht benutzt sondern einfach die Methode die vom Backgroundworker ausgeführt werden sollte (DoWork) direkt aufgerufen.
jaenicke - Fr 11.10.13 10:11
Das Beispiel ist allerdings etwas lang muss ich sagen. Deshalb kurz zur Erklärung:
Du musst dem Worker sagen was er asynchron machen soll. Das ist dein bw_DoWork, die Initialisierung findest du in InitializeBackgoundWorker.
Im startAsyncButton_Click siehst du dann den Aufruf über den Worker mit RunWorkerAsync.
kmkhl - Fr 11.10.13 11:07
Ich verzweifle jetzt langsam.
Form1:
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:
| public partial class AusgabeForm : Form { BeispielKlasse bspKlasse = new BeispielKlasse();
public AusgabeForm() { InitializeComponent(); }
private void Rechnung(object sender, DoWorkEventArgs e) { bspKlasse.bw.RunWorkerAsync();
if (bspKlasse.rechner(ersteZahl, zweiteZah)) { MessageBox.Show("Rechnung ist richtig"); } } private void abbrechenButton_Click_1(object sender, EventArgs e) { bspKlasse.bw.CancelAsync(); MessageBox.Show("gestoppt"); abbrechenButton.Enabled = false; }
} |
BeispielKlasse:
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:
| public class BeispielKlasse { public BackgroundWorker bw = new BackgroundWorker();
public BeispielKlasse() { InitializeBackgroundWorker(); }
private void InitializeBackgroundWorker() { bw.WorkerSupportsCancellation = true; bw.DoWork += new DoWorkEventHandler(bw_DoWork); bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted); }
private void bw_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker worker = sender as BackgroundWorker;
e.Result = rechner(?, ?, worker, e); }
public bool rechner(int ersteZahl, int zweiteZahl, BackgroundWorker worker, DoWorkEventArgs e) { if (worker.CancellationPending) { e.Cancel = true; } else { try { [...rechnungen...]
while (restGröße != 0) { rechnen(); }
return true;
} catch (Exception Exp) { return false; } } }
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Error != null) { MessageBox.Show(e.Error.Message); } else if (e.Cancelled) { MessageBox.Show("Canceled"); } else { MessageBox.Show(e.Result.ToString()); } } } |
folgende Fehlermeldungen:
in der BeispielKlasse
1. 'public bool rechnung(int ersteZahl, int zweiteZahl, BackgroundWorker worker, DoWorkEventArgs e)': Nicht alle Codepfade geben einen Wert zurück.
2. Keine Überladung für die rechner-Methode nimmt 2 Argumente an.
in Form1
1. Keine Überladung für die Methode 'rechner' nimmt 2-Argumente an.
Ralf Jansen - Fr 11.10.13 11:40
| Zitat: |
| 1. 'public bool rechnung(int ersteZahl, int zweiteZahl, BackgroundWorker worker, DoWorkEventArgs e)': Nicht alle Codepfade geben einen Wert zurück. |
Der Codepfad wo du e.Cancel setzt enthalt keinen return deinen Methode erwartet aber das es einen bool als return gibt.
| Zitat: |
| 2. Keine Überladung für die rechner-Methode nimmt 2 Argumente an. |
| Zitat: |
| 1. Keine Überladung für die Methode 'rechner' nimmt 2-Argumente an. |
stimmt der hast du ja auch 4 Parameter gegeben. Warum wieso kann ich dir nicht beantworten das war deine Entscheidung. Was der Sinn von ersteZahl, zweiteZahl solltest du schon selbst rausfinden und dann passend übergeben. Da du BackgroundWorker und DoWorkEventArgs mit übergibst sieht das stylistisch von hier auch eher merkwürdig aus. Vielleicht hilft es dir wenn du erstmal die rechner Methode wegläßt und den Code aus rechner direkt in DoWork benutzt.
Das canceln solltest du auch in der while Schleife unterbringen und nicht irgendwo vorher.
Das zuweisen von e.result mit einem Bool finde ich merkwürdig. Du berechnest also irgendetwas das so komplex und langdauernd ist das du es in einen Hintergrundthread auslagerst und das Ergebnis dieser Berechnung ist dann ja bzw. nein?
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| private void bw_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker worker = sender as BackgroundWorker;
while (restGröße != 0) { if (worker.CancellationPending) { e.Cancel = true; e.Result = null return; } rechnen(); } } |
Th69 - Fr 11.10.13 11:45
Hallo,
die
BackgroundWorker-Instanz solltest du besser in der Form-Klasse erstellen (und nicht in deiner Logik-Klasse).
Und die Abfrage auf
worker.CancellationPending mußt du natürlich zyklisch innerhalb deiner Rechenmethode durchführen (nicht nur einmalig am Anfang der Methode).
| kmkhl hat folgendes geschrieben: |
| Ich verzweifle jetzt langsam. |
Ich auch ;-)
kmkhl - Fr 11.10.13 12:18
Zwischenfrage: Wenn ich bspKlasse.bw.RunWorkerAsync(); ausführe, beginnt er dann automatisch die bw_DoWork(); auszuführen?
Ralf Jansen - Fr 11.10.13 12:22
RunWorkerAsync erzeigt einen Thread in dem dann der DoWork Event des Backgroundworkers aufgerufen wird. Also wenn deine bw_DoWork dem bw.DoWork Event zugewiesen ist ist die Antwort ja.
Edit: Ich wollte dich schon wieder auf das Codebeispiel in der Hilfe verweisen aber da fehlt dieser Teil dummerweise :( Aber im zweiten Codebeispiel siehst du die InitializeBackgroundWorker Methode wo die Events verdrahtet werden.
kmkhl - Fr 11.10.13 12:27
Das war nur zum testen:
BeispielKlasse:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| private void InitializeBackgroundWorker() { bw.DoWork += new DoWorkEventHandler(bw_DoWork); }
public void bw_DoWork(object sender, DoWorkEventArgs e) { MessageBox.Show("Hallo"); } |
Form1:
C#-Quelltext
1:
| bspKlasse.bw.RunWorkerAsync(); |
zeigt aber nichts an :(
Ich danke Euch für Eure Hilfsbereitschaft!
Ralf Jansen - Fr 11.10.13 12:30
Und InitializeBackgroundWorker wird von dir auch von irgendwo aufgerufen z:b. aus dem Konstruktor deiner Klasse?
kmkhl - Fr 11.10.13 12:32
C#-Quelltext
1: 2: 3: 4:
| public BeispielKlasse() { InitializeBackgroundWorker(); } |
jaenicke - Fr 11.10.13 12:35
Ich glaube es würde Sinn machen, wenn du das ganze Projekt anhängst, wenn das geht. ;-)
kmkhl - Fr 11.10.13 13:28
das denke ich langsam auch
es ist nur leider viel zu komplex
Ralf Jansen - Fr 11.10.13 14:13
| Zitat: |
| das denke ich langsam auch es ist nur leider viel zu komplex |
Dann geht man üblicherweise so vor das man es in einem simplen Beispiel nachstellt. Wenn es dort auch nicht funktioniert kannst du das ja hier entsprechend zeigen. Wenn es funktioniert kannst du dich fragen was du im eigentlichen Projekt anders machst.
kmkhl - Mo 14.10.13 15:17
Habe es jetzt zusammenhängend mit meinem kompletten Code ganz anders mit einem Zweizeiler lösen können. :x
Vielen Dank für Eure Bemühungen!
Ralf Jansen - Mo 14.10.13 16:47
Heißt du kannst und nicht verraten wo das Problem lag? Ich wär schon neugierig.
kmkhl - Mo 14.10.13 17:02
mit Threading konnte ich es zwar unterbrechen, aber die jetztige Lösung ist deutlich kürzer.
Ich übergebe während der Rechnungen, den Fortschritt an meine 'Form1' und 'Form1' gibt den aktuellen Stand an die 'BeispielKlasse', bevor diese eine neue Rechnung beginnt, zurück(damit der Fortschritt dazu und nicht neu berechnet wird). Hierbei übergebe ich dann einfach die Variable, die mir sagt ob der Abbrechen-Button gedrückt wurde oder nicht, mit.
Ich war wohl etwas blind nach 8 Stunden coden. :suspect:
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 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!