Forum: PC-Programmierung C# bidirektionale Kommunikation zweier Klassen


von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

hi,

meine Form1 die sich um die Grafiken kümmert, lädt auch auf Befehl eine 
Textdatei. Diese muss verarbeitete werden. Dazu ist eine 2. Klasse 
(test.cs) notwendig die in einem externen cs-file untergebracht ist - 
also kein Member von Form1 ist. Wenn ich nun eine Instanz von class test 
erzeuge, möchte ich aber auch rückwärts kommunizieren können, sprich 
wieder Information zurück an form1 senden, damit diese auf dem 
Bildschirm dargestellt werden können!
Wie mache ich sowas am sinnvollsten? Reicht es beim Erzeugen von test 
dem Konstruktor eine Referenz  von form1 mit zu übergeben, oder gibt es 
elegantere Methode?

Bsp:
1
this.test1 = new test(this);

EDIT: Habe soeben hier im Forum eine ähnliche Diskussion gefunden, da 
wird meine Methode zwar empfohlen aber später wiederum als "dirty" 
bezeichnet.
Daraufhin wurde auf diesen thread hier verwiesen
http://www.mycsharp.de/wbb2/thread.php?threadid=5960

scheint wohl ein beliebtes thema zu sein!

naja danke trotzdem.

von Christian R. (mrrotzi)


Lesenswert?

Deklariere in der Test Class Events auf die sich die Form anmelden
(eventhandler) kann.
Somit muss Test Class nichts von Form1 wissen und du kannst
Test Class ohne Probleme in einem anderen Projekt auch
wieder verwenden!

Grüße
Christian

von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

Christian R. schrieb:
> Deklariere in der Test Class Events auf die sich die Form anmelden
> (eventhandler) kann.
> Somit muss Test Class nichts von Form1 wissen und du kannst
> Test Class ohne Probleme in einem anderen Projekt auch
> wieder verwenden!
>
> Grüße
> Christian

öhm kannst du das vll ein wenig konkretisieren? was bedeutet anmelden in 
dem kontext?

von Ralf (Gast)


Lesenswert?

Ein EventHandler ist für ereignisbasierende Kommunikation. 
Beispielsweise bei einer seriellen Schnittstelle, wenn etwas empfangen 
wurde, wird das Empfangsereignis ausgelöst. Quasi so eine Art Interrupt.

Eine deiner Klassen stellt also ein Ereignis bereit, mit dem es 
ankommende Daten empfangen kann. Die zweite Klasse löst das Ereignis in 
der ersten Klasse aus und übergibt die Daten.

Ralf

von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

Ja weiss ich ja, aber das ist trivial für einen button klick, aber wie 
wird das Ereignis ausgelöst in diesem fall?

von Christian R. (mrrotzi)


Angehängte Dateien:

Lesenswert?

<c#CrashKurs>

Anbei ein Code-Sample.

WinForm mit 2 Buttons und einer TextBox.

Eine Klasse "Test" die ein event feuern kann.

Button1 -> an das Event anmelden.
Button2 -> Event auslösen.
TextBox wird befüllt wenn Eventhandler aufgerufen wird.

Klasse Test beinhaltet noch eine Klasse für EventArgs. Der quasi 
Übergabeparameter des Eventhandlers.

Schau mal drüber und debug dich mal durch - du wirst dich sicher zurecht 
finden :)

Sollte das Event von Test aus asynchron zum MainThread gefeuert werden, 
muss mit "Invoke" synchronisiert werden. Das erklär ich dir, wenn du's 
brauchst, im nächsten Schritt.

</c#CrashKurs>

von Sam .. (sam1994)


Lesenswert?

Wie wärs denn mit öffentlich Instanzen in der main.cs?

von peterle (Gast)


Lesenswert?

Habe es jetzt erstmal mittels Referenz gelöst. Die Sache mit dem 
EventHandler werde ich mir nochmals genauer anschauen, danke!
Andere Frage: Ist es eigentlich ok, wenn ich alles was mit Grafik, bzw. 
meinem user interface zu tun hat, in der Form1 belassen? Oder lagert man 
so viel es geht in andere Klassen aus?

von peterle (Gast)


Lesenswert?

Christian R. schrieb:
> <c#CrashKurs>
>....
> </c#CrashKurs>

Achso, danke für das Beispiel!!!

von Christian R. (mrrotzi)


Lesenswert?

Referenzen haben den Nachteil, dass es zu eng verstrickten 
Abhängigkeiten
kommt. Eine Klasse, die eine Logik implementiert, die eigentlich nichts
mit Form1 zu tun hat sollte auch nicht damit verwoben werden.
Deswegen das Vorgehen mit Events / Delegates. Du könntest auch
ein Action<...> delegate implementieren. Wenn es nur genau einen
Empfänger für das Ereignis gibt!

Der Ansatz, Logik und UserInterface zu trennen ist genau richtig!!
Man nennt das Muster: MVC Model View Controller oder MVVM Model View 
ViewModel.

Zur Frage, was man auslagern soll: Alles auslagern, was man wo anders
vielleicht noch mal verwenden könnte.
"Divide et impera" (http://de.wikipedia.org/wiki/Divide_et_impera)
Teile und herrsche:
Oberflächenanteile, die eine bestimmte Funktionalität beinhlaten - kann 
man schön in Controls kapseln. BusinessLogik - eigene Klassen 
definieren, interface dazu und in ein eigenes Assembly. 
Datenbankzugriffsschicht getrennt implementieren. Auf eine saubere 
Trennung kommt es an.

Sicher nicht beim Pimperltool was so in einer halben Stunde 
runtergehackt wird.
Aber wenn die Applikation größer zu werden scheint - bitte vorher 
Gedanken über die Modelle machen und dann hast du's auch leichter das 
Ding zu warten.
Es ist einfach peinlich, die eigene Software nach einem halben Jahr 
Enticklungspause nicht mehr zu verstehen.

Viel Glück!
Christian

von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

Christian R. schrieb:
> <c#CrashKurs>
>
> Anbei ein Code-Sample.
>
> WinForm mit 2 Buttons und einer TextBox.
>
> Eine Klasse "Test" die ein event feuern kann.
>
> Button1 -> an das Event anmelden.
> Button2 -> Event auslösen.
> TextBox wird befüllt wenn Eventhandler aufgerufen wird.
>
> Klasse Test beinhaltet noch eine Klasse für EventArgs. Der quasi
> Übergabeparameter des Eventhandlers.
>
> Schau mal drüber und debug dich mal durch - du wirst dich sicher zurecht
> finden :)
>
> Sollte das Event von Test aus asynchron zum MainThread gefeuert werden,
> muss mit "Invoke" synchronisiert werden. Das erklär ich dir, wenn du's
> brauchst, im nächsten Schritt.
>
> </c#CrashKurs>


Habe das Programm erstmal mit Referenzen gelöst, um zu sehen ob es 
läuft. Ich denke aber, dass deine Methode die Bessere ist. Also habe ich 
mir den Code mal angeschaut.

Ok, als Voraussetzung für ein event handling muss diese Klasse einen 
handler definiert haben?:
1
public event EventHandler<MyArgs> MyEvent;

erst dieser macht es möglich auf events zu reagieren?
Dann erzeuge ich eine Instanz mittels Button1, als Parameter die Methode 
die Aufgerufen werden soll):
1
this.MyTestInstance.MyEvent += new EventHandler<MyArgs>MyTestInstance_MyEvent);

ok, aber der Event auf form 1 ruft ja eine text box die auch auf form1 
liegt auf, dafür muss ich ja nicht über die Test-Klasse gehen. Oder 
sollte das nur ein Bsp. sein? Denn ich will ja, das etwas in der 
Test-Klasse passiert. Also Form1 ruft Test auf und triggert dort ein 
Event (berechne x*y). Beim beenden soll Test etwas auf form1 einblenden 
zB.: "success". Danach wird wieder gewartet bis der nächste Button auf 
form 1 gedrückt wird.

Weisst du was ich meine?

von Christian R. (mrrotzi)


Angehängte Dateien:

Lesenswert?

>
> Ok, als Voraussetzung für ein event handling muss diese Klasse einen
> handler definiert haben?:

Den Handler hat der, der das Event konsumiert. Bei der Testklasse wird
das event implementiert. Das event ist das, was signalisieren kann.


> ok, aber der Event auf form 1 ruft ja eine text box die auch auf form1
> liegt auf, dafür muss ich ja nicht über die Test-Klasse gehen. Oder
> sollte das nur ein Bsp. sein?


ja, ganz genau. war nur mal auf die Schnell hingehackt - damit du dir
was vorstellen kannst. Natürlich wäre so eine Implementierung nicht 
recht
schlau (schon gar nicht, die Klasse TEST zu nennen ;o) ) aber zum Lernen
sollte es passen.

> Denn ich will ja, das etwas in der
> Test-Klasse passiert. Also Form1 ruft Test auf und triggert dort ein
> Event (berechne x*y). Beim beenden soll Test etwas auf form1 einblenden
> zB.: "success". Danach wird wieder gewartet bis der nächste Button auf
> form 1 gedrückt wird.
>
> Weisst du was ich meine?

ja, weiß ich! Deswegen das nächste Beispiel. Ein wenig umgeändert.

Button1 -> Handler mit Event verbinden
Button2 -> Startet eine Berechnung, die ich mit Sleep(...) künstlich in
die Länge gezogen habe.

Ist die Berechnung fertig - wird das event gefeuert.

VORSICHT: da die Berechnung und das feuern des events in einem anderen
Thread abspielt, muss synchronisiert werden, wenn man in diesem auf 
controls
in der Form zugreifen möchte!! Das Beispiel zeigt das, wie das mit 
Invoke
gemacht werden kann.

Viel Erfolg,
Christian

von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

danke schön soweit, aber irgendwie stellt sich mir da direkt eine Frage, 
muss ich denn hier überhaupt threading betreiben? Um die 
synchronisierung zu vernachlässigen und wenn ich gleichzeitig festlege, 
das keine andere Aktion in dieser Zeit vom user aufgerufen werden kann, 
so könnte ich doch strikt sequentiell arbeiten - also single threaded?!!

Oder ist multi threading hier zwangsläufig nötig?

von Sven H. (dsb_sven)


Lesenswert?

Du könntest auch Delegaten verwenden.

Das sind im Prinzip Funktionszeiger in c#.

von Christian R. (mrrotzi)


Lesenswert?

Peterle Anonym schrieb:
> danke schön soweit, aber irgendwie stellt sich mir da direkt eine Frage,
> muss ich denn hier überhaupt threading betreiben? Um die
> synchronisierung zu vernachlässigen und wenn ich gleichzeitig festlege,
> das keine andere Aktion in dieser Zeit vom user aufgerufen werden kann,
> so könnte ich doch strikt sequentiell arbeiten - also single threaded?!!
>
> Oder ist multi threading hier zwangsläufig nötig?

Das hängt davon ab ob deine Aktivität länger dauert und ob du das dem
Benutzer zumuten kannst.
Während deine Aktivität (zB Berechnung) läuft und du singel threaded nur
im MainThread arbeitest, steht natürlich deine WinForm.
Wenn das eine halbe Sekunde dauert - dann ist das u.U. auch ok.
Sollte die Aktivität doch länger dauern - dann wird der Benutzer
ungeduldig und beginnt wild herum zu klicken.

Theoretisch kannst du dir den ganzen event & callback Mechanismus im 
Fall 1
sparen! Weil du in der Testklasse einfach eine Methode implementierst,
die als return Wert das zurück liefert, was du zum Anzeigen in der Form
brauchst.
1
this.textBox1.Text = new TestClass().DoWas().ToString();
(bitte das ist nu ein schlechtes Beispiel zur Veranschaulichung!!)

Grüße,
Christian

von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

Christian R. schrieb:
> Peterle Anonym schrieb:
>> danke schön soweit, aber irgendwie stellt sich mir da direkt eine Frage,
>> muss ich denn hier überhaupt threading betreiben? Um die
>> synchronisierung zu vernachlässigen und wenn ich gleichzeitig festlege,
>> das keine andere Aktion in dieser Zeit vom user aufgerufen werden kann,
>> so könnte ich doch strikt sequentiell arbeiten - also single threaded?!!
>>
>> Oder ist multi threading hier zwangsläufig nötig?
>
> Das hängt davon ab ob deine Aktivität länger dauert und ob du das dem
> Benutzer zumuten kannst.
> Während deine Aktivität (zB Berechnung) läuft und du singel threaded nur
> im MainThread arbeitest, steht natürlich deine WinForm.
> Wenn das eine halbe Sekunde dauert - dann ist das u.U. auch ok.
> Sollte die Aktivität doch länger dauern - dann wird der Benutzer
> ungeduldig und beginnt wild herum zu klicken.
>
> Theoretisch kannst du dir den ganzen event & callback Mechanismus im
> Fall 1
> sparen! Weil du in der Testklasse einfach eine Methode implementierst,
> die als return Wert das zurück liefert, was du zum Anzeigen in der Form
> brauchst.
>
>
1
this.textBox1.Text = new TestClass().DoWas().ToString();
> (bitte das ist nu ein schlechtes Beispiel zur Veranschaulichung!!)
>
> Grüße,
> Christian



Sagen wir's so, ich bin mir sicher, dass während der Berechnung der User 
nicht ungeduldig wird, denn er weiss, dass das Programm nur diese eine 
Sache kann!
Somit ist MT nicht nötig in meinem Fall und würde nur zu overhead 
seitens des Codes führen.
Somit würde ich die test klasse ohne MT implementieren und auch die 
Form1. aber dann tritt das problem auf , das "OnCalcFinished" eine 
exception wirfst, wie du es ja beschrieben hast!

EDIT: bei mir wirft es keine exception wenn ich deinen Code ohne MT 
implementiere...

von Christian R. (mrrotzi)


Lesenswert?

Wer triggert die Aktion?
Also wenn du auf eine Button-Click reagierst und die Funktion der
Klasse Test ausführst brauchst du auch kein Event oder einen 
Eventhandler dazu!
Werte doch einen Return werd der Funktion aus (siehe Beispiel oben) und
du bist alle Sorgen los!

oder nicht?

von c# mensch (Gast)


Lesenswert?

Peterle Anonym schrieb:
> Sagen wir's so, ich bin mir sicher, dass während der Berechnung der User
> nicht ungeduldig wird, denn er weiss, dass das Programm nur diese eine
> Sache kann!

Dann mach es ST und setz den Cursor auf "Wait"!

Du hast hier soviel Möglichkeiten aufgezeigt bekommen, langsam wirds 
Zeit mal in Buch zu schauen! :

http://openbook.galileocomputing.de/visual_csharp/visual_csharp_09_001.htm#mj3a47b94a0eedec3c16959ded7da58115

von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

Christian R. schrieb:
> Wer triggert die Aktion?
> Also wenn du auf eine Button-Click reagierst und die Funktion der
> Klasse Test ausführst brauchst du auch kein Event oder einen
> Eventhandler dazu!
> Werte doch einen Return werd der Funktion aus (siehe Beispiel oben) und
> du bist alle Sorgen los!
>
> oder nicht?


Hab mal etwas rumgefummelt und es hinbekommen:

habe in der "Testklasse" einen thread gestartet, der eine queue gefüllt 
hat, danach wurde ein event auf form1 ausgelöst welches wiederum einen 
delegaten benutzt hat um die queue zu lesen und auf die form zu 
schreiben.
Danke nochmal, hast mir sehr geholfen!! Auch dein Beitrag über MVC war 
hilfreich!!

gruß



> Dann mach es ST und setz den Cursor auf "Wait"!
>
> Du hast hier soviel Möglichkeiten aufgezeigt bekommen, langsam wirds
> Zeit mal in Buch zu schauen! :
>
> http://openbook.galileocomputing.de/visual_csharp/...

wat? wer bist du denn?

von Christian R. (mrrotzi)


Lesenswert?

gern geholfen!

möchtest du dein Resultat posten? ich würde gerne sehen, wie du
das letztendlich umgesetzt hast!

Beste Grüße,
Christian

von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

ungern öffentlich da Arbeit :) email?

von Sven H. (dsb_sven)


Lesenswert?

Häng doch einfach die Quellcodedatei an. Vielleicht haben andere ein 
ähnliches Problem.

von peterle (Gast)


Lesenswert?

Ja werde sie natürlich hier reinstellen, aber erstmal sicherstellen das 
alles richtig implementiert wurde. Will ja nichts falsches posten!

von Sven H. (dsb_sven)


Lesenswert?

Das wiederum ist sehr lobenswert!

von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

Ich schiebe trotzdem nochmal zwei Frage hinterher:

1) Angenommen ich möchte über mehrere Klassenebenen hinweg einen Event 
bereitstellen, wie zB:
1
class1.class2.MessageReceived     += new EentHandler<EventArgs>(event_method);

ist es dann immer noch eine "saubere" Methode? Oder lege ich lieber 
einen Zwischen-Event an?

2) Die Queue sollte nicht in der aufgerufenen Klasse, sondern in der 
aufrufenden Klasse erzeugt werden, um den Verlust der Daten zu 
vermeiden, falls die Klasse de-initialisiert wird oder? Übergebe ich 
dann einfach eine Referenz der Queue mit an die erzeugte Klasse?

von Sven H. (dsb_sven)


Lesenswert?

zu 1. Du meinst du möchtest den EventHandler für eine Funktion in einer 
Instanz der Klasse class2 innerhalb einer Instanz der Klasse class1 
zuweisen? Und alternativ in der Klasse class1 eine Art Zwischenebene 
einbauen? Ist das nicht dann eher ein Umweg?

Welche Variante aus dem Bild hast du denn implementiert?


zu 2. Die Daten gehen erst dann verloren, wenn der Garbage Collector der 
Meinung ist, die Daten werde nicht mehr gebraucht.

von Sven H. (dsb_sven)


Angehängte Dateien:

Lesenswert?

Hier das Bild. Irgendwie kann man bei Bearbeiten keine Bilder mehr 
hochladen.

Also, entweder die linke Variante mit der verschachtelten Klasse oder 
die rechte mit zwei unabhängigen Klassen?

von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

Es wäre das erste Beispiel, wobei der Aufruf in Form 1 passiert, also 
letztendlich 3 Klassen involviert sind!

1)

Form1.cs
1
//Passiert im Kostruktor der Form 1:
2
testclass class1 = new class1();
3
4
// class1 enthält irgendeine class2
5
class1.class2.MessageReceived     += new EentHandler<EventArgs>(event_method);

Form1->class1->class2->MessageReceived()

Damit würde ich in class2 einen Event in der Form1 auslösen. Und meine 
Frage war, ob das so in Ordnung geht?

2) Und wann ist er dieser Meinung, also was genau muss passieren? Hätte 
jetzt die Queue in der übergeordneten Klasse erzeugt und nach unten 
durchgereicht?!

von Sven H. (dsb_sven)


Lesenswert?

zu 1. Damit sehe ich kein Problem. Ist Geschmackssache, denk ich.

zu 2. Genau genommen weiß ich das nicht. Allerdings hatte ich noch nie 
Probleme damit. Wenn dus genau wissen willst, musst du mal nach Garbage 
Collector suchen und dich rein lesen.

von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

ok danke trotzdem

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.