Kommunikation zwischen mehreren Forms per Event

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 49 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Kommunikation zwischen mehreren Forms per Event

    Hallo,

    ich habe mir zum besseren Verständnis mal eine kleine Anwendung mit 3 Forms und etwas Eventhandling untereinander erstellt.
    Die will ich hier mal vorstellen und würde mich über (konstruktive) Kritik darüber freuen, ob ich auf dem Holzweg bin oder durchaus auf dem richtigen Weg.
    Ungeachtet der Sinnhaftigkeit des Projekts verdeutliche ich mir daran die Mechanismen zur formübergreifenden Eventkommunikation.

    Form1 öffnet in seiner "MyBase.Load" das Form2, hat also unmittelbar Kenntnis von Form2 und kann dadurch Events von Form2 einfach mit "WithEvents" abonnieren.
    Klicke ich in Form2 auf den "btnForm2Event" wird dadurch ein Event ausgelöst, welches Form1 veranlasst den Zustand von "chkForm2" auf "chkForm1" zu übertragen.



    Form2 öffnet durch klicken auf "btnForm2OpenForm3" das Form3.
    Klicke ich "btnForm3ReadChkBoxForm1" holt sich Form3 den Zustand der "chkForm1" und setzt damit seine eigene "chkForm3".
    Da Form3 zunächst erst mal keine Kenntnis darüber hat, ob Form1 überhaupt existiert wird erst mal mittels "My.Application.OpenForms" danach gesucht und dann darauf verwiesen.

    Umgekehrt soll auch Form3 etwas an Form1 senden und zwar den Zustand der Variablen "bStatusCheckBox" sobald "btnForm3EvtSendbStatusCheckBoxVariableToForm1" gedrückt wird.
    "bStatusCheckBox" setzt dabei entsprechend den RadioButton auf Form1.
    Per Event, das von Form1 direkt von Form3 abonniert wird, geht das ja nicht, da beim Erstellen von Form1 das Form3 ja noch nicht existiert und auch nicht von Form 1 erstellt wird.
    Form1 weiß also gar nichts von Form3. Daher abonniert Form1 per AddHandler das Event "evtForm3" welches sich im Modul1 befindet. Form3 ruft also "evtForm3Mod" auf, übergibt die notwendigen Parameter und erst dort wird dann das Event ausgelöst, welches den Variablenwert als EventArgs an Form1 übergibt.



    Projekt kann hier runter geladen werden: WindowsApplication2.rar


    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „roepke“ ()

    Entferne den bin und obj Ordner aus dem Verzeichnis und lade das Projekt nochmal hoch.
    Das Verteilen von .exe Dateien auch als .zip oder sonst was, ist außerhalb des Showrooms nicht erlaubt.


    Edit:
    Wurde erledigt

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „EaranMaleasi“ ()

    Ja, ok, kannst Du machen. Solltest Du aber nicht. Wenn Form1 und Form3 sich nicht gegenseitig kennen, weil ein anderes Form dazwischen liegt, sollte es auch so bleiben, dass die beiden sich nicht kennen. Ich würde keinen Sinn darin sehen, soetwas wie formüberspringende Kenntnis haben zu wollen. Lass den Datenfluss über Form2 laufen. Wenn Form3 was von Form1 will, soll es einen Request (per Event) an Form2 schicken und das schickt einen Request an Form1. Wenn Form1 was von Form3 will, ruft Form1 eine Function von Form2 auf, die wiederum sich die Daten von Form3 besorgt. Ansonsten wissen Komponenten voneinander was, was sie nicht wissen sollten. Das erhöht die Klassenkopplung und das System wird starrer. Und Starrsinnheit = Unflexibilität. Diese Kenntnis voneinander erfordert dann auch Aufrufe von OpenForms. Das sehe ich (fast) immer als böses Omen.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    ich kann .rar nicht öffnen.
    Ansonsten frag ich, ob du wirklich so ein komplexes Messaging benötigst (und wie oft), oder ob das jetzt nur eine Fingerübung ist.
    Man kanns durchaus hinkriegen, dass Komponenten, die sich nicht kennen, trotzdem miteinander kommunizieren.
    Will man solch öfter einsetzen empfiehlt sich aber, bisserl Infrastruktur dafür zu schreiben, um es komfortabel abhandeln zu können.
    Wie gesagt: schade, dass das Sample .rar ist.
    Das ganze lässt sich bestimmt auch wie folgt implementieren, habs allerdings nicht ausprobiert.

    1: Klasse XY mit benötigten Properties schreiben
    2: INotifyPropertyChanged in dieser Klasse implementieren
    3: in Form 2 Ein neues Objekt aus dieser Klasse instanzieren und an Form 1 und Form 3 übergeben (z.B. via Konstruktor)
    4: in Form 1 und 3 mittels Databinding (aktualisierung „OnPropertyChanged“) die Eigenschaften des Objekts an die passende Eigenschaft der Controls binden.
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    mir ist immer noch unklar, ob und wofür du sowas brauchst.
    Ist ja auf jeden Fall aufwändig und verhältnismässig unübersichtlich - egal, wie's nu implementiert ist.

    Daher wäre vorrangig danach zu gucken, ob du deine Anforderungen nicht auch mit einer einfacheren Architektur erüllen kannst.

    (Wobei "Fingerübung" natürlich auch eine Anforderung ist - also wenn du einfach mal sauber umgesetzt haben möchtest - hätte auch seine Berechtigung.)

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ErfinderDesRades“ ()

    @ErfinderDesRades Jou
    @roepke Was ist zu tun, um Effekte zu produzieren, was genau soll passieren?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hallo,ich will nochmal auf das Thema zurückkommen.
    @ErfinderDesRades und RodFromGermany: Für mich sind das Fingerübungen und ich möchte mir damit ein kleines, überschaubares Projekt basteln, dass mich
    1. immer wieder daran erinnert wie die Sache funktioniert und
    2. mir die Möglichkeit gib es ggf. auch anderen Mal zu erklären.

    Aber zurück zum Thema:
    Ich habe das Ganze jetzt soweit verinnerlicht und auch schon (einigermaßen) erfolgreich angewendet.
    Jetzt stehe ich vor folgendem Problem:
    Form 1 soll nicht nur eine Instanz von Form 2 öffnen, sondern beliebig viele. D.h. jedes Mal wenn ein Button auf Form 1 gedrückt wird, öffnet sich eine weitere Instanz von Form 2.
    Alle dieses Form 2 Instanzen sollen in der Lage sein in Form 1 ein und das selbe Event auszulösen.
    Mit

    VB.NET-Quellcode

    1. Dim WithEvents Form2Events As New Form2()
    2. 'Öffne Form1, erzeuge Form2
    3. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    4. Form2Events = CType(OpenForm2(), Form2)
    5. End Sub
    6. Private Sub Form2DoEvents(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Form2Events.evtForm2
    7. 'Tu was
    8. End Sub


    in Form 1 komme ich da nicht weiter, da WithEvents keine Arrays zulässt. Wie mache ich sowas richtig?
    Ist jetzt das AddHandler zwingend?

    roepke schrieb:

    Ist jetzt das AddHandler zwingend?
    Ja.
    Spannend wird die Geschichte nun auch dadurch, dass wenn du ein Form schliesst, dass du seine Events dann auch wieder entfernen solltest, mit RemoveHandler.

    ansonsten sehe ich kl. Fehler in zeile #1 und eine andere Fragwürdigkeit in #5.
    Ich täte wirklich empfehlen, dein Test-Dingens zu zippen und anhängen - grade wenn das eine Vorlage werden soll, an der du dich in Zukunft womöglich orientierst.

    Dann kann man deinen Prototypen mal gradeziehen.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    @roepke So bekommst Du zunächst beliebig viele Instanzen Deiner Form:
    Dialoge: Instanziierung von Forms und Aufruf von Dialogen
    Probier mal dies: Form1, Form2 mit je einem Button:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    3. Dim dlg As New Form2
    4. AddHandler dlg.FormClick, AddressOf Form2_Click
    5. dlg.Show(Me)
    6. End Sub
    7. Private Sub Form2_Click(sender As Object, e As EventArgs)
    8. MessageBox.Show("da")
    9. Dim dlg As Form2 = CType(sender, Form2)
    10. dlg.DoIt()
    11. End Sub
    12. End Class

    VB.NET-Quellcode

    1. Public Class Form2
    2. Public Event FormClick(sender As Object, e As EventArgs)
    3. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    4. RaiseEvent FormClick(Me, EventArgs.Empty)
    5. End Sub
    6. Public Sub DoIt()
    7. Me.BackColor = Color.Green
    8. End Sub
    9. End Class

    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    AddHandler wird dann nötig, wenn Du zur Design-/Programmierzeit nicht festlegst, wer ein Event feuern soll. Da Du die Event-"Feuerstellen" hier erst zur Laufzeit generieren willst, kannst Du deren Events nur per AddHandler mit dem EventHandler (also der auf das Event reagierenden Prozedur) verbinden.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    So ihr lieben, nach einem halben Tag Arbeit hier jetzt mein aktueller Stand meines Fingerübungsprojektes zum Thema Events.

    WindowsApplication2 - Updates.zip

    Ich denke, ich habe so ziemlich alle Fälle berücksichtigt.
    Die Sache mit den mehreren Instanzen eines Forms habe ich jetzt mit einem Array gelöst.
    Wird ein neues Form4 geöffnet wird das Array entsprechend mit zugehörigen Infos gefüllt und die Handler eingerichtet. Wird das Form geschlossen, wird der zugehörige Eintrag im Array geleert und die Handler werden entfernt. Öffne ich nun ein neues Form4 wird geschaut, ob es Platz mit Array gibt, weil bereits ein anderes Form zuvor daraus entfernt wurde. Wenn ja, kommt das neue Form an die freie Stelle. Wenn nein, wird es hinten angehängt.
    Bin offen für jede Art von Kritik und Vorschlägen.

    roepke schrieb:

    Öffne ich nun ein neues Form4 wird geschaut, ob es Platz mit Array gibt
    Ohne jetzt dein Sample downgeloaded zu haben finde ich dieses Vorgehen eigentümlich.
    Hinzufügen/Entfernen - dafür nimmt man normalerweise kein Array, sondern eine List(Of T).
    Ich weiss jetzt nicht, inwieweit du mit List(Of T) bekannt bist, wenn nicht, ist das ganz wichtig, zu recherchieren, weil das ist eine der wichtigsten Klassen im Framework.
    Danke für die Antwort.
    Da werde ich wohl mal recherchieren.
    List(Of T) habe ich ehrlich gesagt noch nicht verwendet.
    Gehört habe ich aber schon davon, bzw. es mal wo gelesen.
    Aber das ist vielleicht auch die Lücke die ich hier habe.
    Mit dem Array, das die Forms vorhält funktionert es, aber es geht bestimmt immer noch etwas eleganter und besser.
    Daher bin ich ja hier im Forum, um besser zu werden. :)
    Es gibt sicherlich viel zu tun. Ich fang mal mit der Verbesserung Deiner Shared-Procedures in Class1 an. Da lässt sich dank LINQ einiges eindampfen:

    VB.NET-Quellcode

    1. 'Zeigt dieses Steuerelement am Anfang der z-Reihenfolge an.
    2. Friend Shared Sub BringFormToFront(ByVal FormName As String)
    3. My.Application.OpenForms.Cast(Of Form).FirstOrDefault(Function(x) x.Name = FormName)?.BringToFront()
    4. End Sub
    5. 'testet ob das Form mit dem Namen "sFormName" geöffnet ist
    6. Friend Shared Function FormIsOpened(ByVal sFormName As String) As Boolean
    7. Return My.Application.OpenForms.Cast(Of Form).Any(Function(x) x.Name = sFormName)
    8. End Function
    9. 'selektiert das Form von sFormName
    10. Friend Shared Function FormSelect(sFormName As String) As Form
    11. Return My.Application.OpenForms.Cast(Of Form).FirstOrDefault(Function(x) x.Name = sFormName)
    12. End Function

    1. Zwischenfrage: Was hat es mit dem Parameter-Präfix s in sFormName auf sich?
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    jo, tut man nicht in .Net. Da weiss man, dass ein Name ein String ist - derlei Prefixe nur in extrem seltenen Ausnahmefällen. ZB wenn man eine Zahl unanständigerweise als String vorliegen hat, könnte man sie sNumber nennen.
    Aber sowas (zahl als String vorhalten) tut man natürlich erst recht nicht.

    Ansonsten wird man glaub mit FormName nicht glücklich. Du öffnest mw. 3 mal dein Form4 - welches willste dann nehmen?

    Bzw Frage: Wozu brauchst du sowas - das geht doch sicherlich besser.
    Anners gefragt: Zeig mal Beispiel, wo diese komischen Methoden aufgerufen werden (du siehst, ich habs Sample immer noch nicht gezogen)
    Aber ist eh notwendig, dass du solche Fragen beantworten kannst.
    Die konkrete Anwendung sieht wie folgt aus:

    Ich habe einen Hardware-Hub, an den lassen sich Verschiedene Module mit Aktor- oder/und Sensorfunktionen anschließen. Also m Sinne einer Sternverteilung. Der Hub ist immer der Gleiche, nur die Module drum herum sind änderbar. Diese Module müssen parametriert werden. Ergo, ich benötige eine Software, die dem Benutzer die Möglichkeit bietet diese Parametrierung für jedes Modul vorzunehmen und dann über den Hub (welcher per USB am PC hängt) direkt in die Module zu senden. Die angeschlossenen Module können auch von gleicher Natur sein. Es ist also möglich, dass Modul A z.B. 3x vor kommt, Modul B 2x usw. Kurz, die Darstellung auf dem Bildschirm soll genau dem Hardware Aufbau entsprechen, damit der Benutzer genau weiß, für welches der angeschlossenen Module er gerade die Parametrierung vornimmt.

    Die Sache mit der Fingerübung dient einzig dazu, dass ich die Mechanismen in VB besser verstehe und Optimierungsmöglichkeiten sehe. Wie schon geschrieben bin ich eher der VHDL und Embedded Typ. Zu VB kam ich durch einen Kollegen, der ein diesbezügliches Programm (wie oben) „verbrochen“ hat, weil auch er absolut keine Ahnung von .Net zu haben scheint. Ich kämpfe mich schon seit Monaten immer mal wieder durch seinen Spaghetti-Code und versuche da Struktur reinzubringen, aufzuräumen und das Ganze leserlich zu machen.

    Da sind die Namen im Moment das Letzte um das ich mich kümmere. Erst mal will ich dafür sorgen, dass das Ganze einigermaßen sicher läuft und ich vor allem verstehe warum es das tut oder aber eben nicht.