WinForms Projektentwicklung bzgl. der Rolle des Formulars

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

    Es gibt 11 Antworten in diesem Thema. Der letzte Beitrag () ist von Amro.

      WinForms Projektentwicklung bzgl. der Rolle des Formulars

      Hallo zusammen,

      die von @Yanbel angestoßene Diskussion bzgl. MVVM@WinForms hat mich zum Schreiben dieses Threads geführt. Ich möchte explizit seinem geplanten Thread nicht vorgreifen. Da ich allerdings keine Ahnung habe, was kommt, ich aber auch nicht weiß, wann es kommt, folgt hier eine kleine WinForms-Architekturdesignentwicklung, die der ein oder andere, der vielleicht genauso fachfremd wie ich ist, als Anleitung sehen könnte, etwas besser sein eigenes Projekt zu strukturieren oder neue Möglichkeiten gezeigt zu bekommen. Dazu habe ich erstmal fünf Entwicklungsstufen eines Projekts erzeugt. Für Pros und alte Hasen dürfte dieser Thread aber wahrscheinlich wenig interessant sein.
      Das eigentliche Projekt, welches in seinen 5 Stufen hier beschrieben wird, befindet sich als Gesamtpaket im Anhang und ist an sich lächerlich. Es ist ein einfaches Pseudowürfelspiel gegen den Computer und bedarf keiner Erklärung. Auch die verwendete Lade-/Speichertechnik für die Statistik ist ein Witz. Aber genau dabei soll es auch bleiben, um den Fokus auf den Umgang mit dem Formular (im Weiteren kurz das(!) Form, denn das ist auch der englische Begriff für Formular, daher WinForms) an sich zu behalten.


      Stufe 1 - Einsteigerart

      So würde es wohl ein Programmiereinsteiger machen.
      Das Form weiß alles. Alle Variablen sind formweit bekannt. Keine Klasse außer Form. Die ganzen Abläufe sind in den EventHandlern der Buttons untergebracht.

      Vorteile
      • Man kann direkt alle auf dem Form anzuzeigenden Texte und andere GUI-Veränderungen vornehmen.
      • Der Code ist sehr kompakt (im Vergleich zu den anderen Ausbaustufen).
      • KISS - zumindest für den Anfang

      Nachteile
      • Verletzung von SoC, SRP, DRY und zweifellos anderen Prinzipien auch.
      • Testbarkeit? So ziemlich unmöglich.
      • Der Anfang vom Spaghetticodechaos, in den man sich irgendwann als Codeanfänger verfängt, nie mehr rausfindet und deshalb früher oder später frustriert aufgibt.


      Stufe 2 - Klumpenform

      Immer noch ist das Form der Oberboss. Es hat zumindest mal eine weitere Klasse in den Code geschafft, auch wenn es nur minimal besser als Stufe 1 ist.

      Vorteil
      • Zumindest gehören die Spielvariablen nicht mehr zum Form direkt, sondern in eine Klasse, die dafür auch verantwortlich ist.

      Nachteile
      • siehe Stufe 1


      Stufe 3 - Klassennutzung

      Die erste Klasse wurde stark ausgebaut, die Spielmechanik (alias Business Logic?) steckt nun in ihr. Auch das Speichern und Laden ist in eine eigene Klasse gewandert, wurde also vom Rest abgespaltet.
      Ich habe auch absichtlich Aufrufe der Art EventHandler->Integration->Operation drin. Im BtnThrowDice.Click-EventHandler wird erst ThrowDice aufgerufen, was wiederum Game.ThrowDice() aufruft.
      Was soll der Mumpitz?

      Ich teile gern meine Forms in mehrere Partial files auf: EventHandler, Integration, Operation und andere
      Dadurch kann ich mir schneller eine Geschichte erzählen lassen: Was kann der User u.a. machen (EventHandler), was passiert dann (Integration), und wenn ich an den Details interessiert bin, also wie dann etwas passiert, dann schau ich mir die Operation-Datei an.
      Während Integration bei mir nur formeigene Funktionsaufrufe enthält, werden in Operation andere Klasseninstanzen zur Mitarbeit aufgefordert und man verlässt quasi das formeigene Gebiet.
      Zur Übersicht habe ich allerdings keine eigenen partial files in den Projekten erzeugt.


      Vorteile
      • Plötzlich sieht man, was wo passiert und welche Klasse wofür konkret verantwortlich ist. Das Speichern und Laden soll geändert werden? Dafür ist die Klasse GameFileContentManager nun verantwortlich. Es sollen weitere Würfel hinzukommen? Man ändert die Game-Klasse.
      • für CCD-Fans: IOSP ist an dieser Stelle viel einfacher umzusetzen
      • Man kann mit Partial files nun viel besser trennen: geht es um EventHandler? Will man nur Prozeduren anschauen, die das GUI manipulieren? Oder ist man nur an Validierungsfunktionen interessiert? Da kann man sehr gut eigene Aufteilungen der Klassen angehen, wo vorher nur Mischmasch-in-Prozeduren war. Dies ist zwar nicht erst durch die hier eingeführte Klassennutzung möglich, aber es geht einfacher, da nun eine Monolithenklasse selber aufgeteilt wird.

      Nachteile
      • Der Code wird plötzlich gefühlt 3x so groß, bedingt durch gegenseitige Aufrufe und viele Prozedurrümpfe.


      Stufe 4.1 – MVP Oberbossform

      Keine Ahnung, ob die Stufe 4 das ist, was man als eine Art von Model-View-Presenter bezeichnet, aber es klingt ein wenig nach dem (nicht vollständig! das Model weiß bei mir nix von ViewModel und Form!), was man Martin Fowler unter der Bezeichnung Supervising Controller zuschreibt, glaubt man dem Wikipedia-Eintrag.
      Das Form bleibt weiterhin Oberboss, erzeugt aber eine ViewModel-Instanz, welches sich um den Kontakt zur Spielmechanik kümmert. Das Form selber sendet Befehle ans ViewModel, welches diese dann umsetzt. Werden GUI-Updates benötigt, teilt das ViewModel dies über Property-Änderungen und dem EventRaising von PropertyChanged (als Implementierung der INotifyPropertyChanged-Schnittstelle) mit, wodurch eine BindingSource auf dem Form dies an die einzelnen GUI-Elemente weitergibt. Für jede GUI-Property-Änderung wird also quasi eine Klassenproperty im ViewModel benötigt. Will man auch z.B. Texteingaben aus einer TextBox ans ViewModel weiterleiten, muss man natürlich die Private Setters in Public Setters umwandeln.

      Vorteile
      • Das Form macht fast nichts mehr, nur noch das Weiterleiten der Eingaben an das ViewModel. ViewModel-Properties sind über eine BindingSource mit dem GUI verbunden.
      • Das ViewModel bleibt bei einer Aufgabe: Sich um die Koordinierung des Datenmodell zu kümmern. Indem es selbst seine Daten ändert und eventuelle Änderungsevents feuert, ist es ihm egal, wer damit was anfängt. Das ViewModel weiß damit nichts über das Form.

      Nachteile
      • Für jede Property, die man auf dem GUI ändern will, braucht es eine eigene ViewModel-Property. Und zwar allein wegen des Event-Feuerns komplett ausformuliert – zumindest bis Microsoft sich da was einfacheres überlegt. Zum Glück kann man die in ne Partial-Class-File auslagern oder notfalls in einer Region verstecken.

      Stufe 4.2 – MVP Passivform

      In dieser Ausbaustufe wird das Form komplett zur Anzeige degradiert. Das ViewModel übernimmt die Änderungen am GUI. Dies wird dadurch erreicht, dass man in den Projekteigenschaften das Anwendungsframework deaktiviert, Sub Main als Startprozedur einstellt, ein Modul erstellt und dort eine Sub Main definiert, die das ViewModel als Oberklasse bestimmt, welches wiederum das Form aufruft.

      Vorteile
      • Das Form ist nur noch für eine Sache zuständig: GUI-Design. Keine BindingSource, keine Events.
      • Das ViewModel kann das Form direkt manipulieren, ohne dafür eigene Properties hernehmen zu müssen.

      Nachteile
      • Man muss jedes Event des Forms, an dem man interessiert ist, per AddHandler abonnieren und beim Beenden am besten wieder abbestellen. Eine Verwendung der Handles-Klausel ist nicht möglich.
      • Das ViewModel hat eine weitere Aufgabe bekommen (für die SRP-Puristen)

      Wie geht’s weiter? Ich habe keine Ahnung, da ich Stufe 4 noch nie praktisch außerhalb des hiesigen Demoprojektes angewendet habe. Jetzt sind Eure Erfahrungen und Tipps gefragt. Zweifellos kommen zahlreiche Anmerkungen, Einsprüche, Bedenken, whatever. Ich stehe bezüglich des Umgangs mit der Rolle des Formulars vor neuen Unbekannten. Dies soll auch weniger ein abgeschlossenes Tutorial von mir als ein erster Schritt in eine eventuell gemeinsame Weiterentwicklung prinzipeller Art v.a. für Programmieranfänger sein. Das Projekt an sich soll natürlich nicht weiterentwickelt werden, das kann jeder selber machen. Es geht mir um die Architektur bzw. um die Rolle des Formulars an sich.

      ##########

      Update 18.06.: Im Projekt MVP Oberbossform hatte sich noch ein wenig an Testcode befunden, der da nicht reingehörte.
      Dateien
      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.

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

      Ich habe vor kurzem ein neues Programm entwickelt. Sagen wir mal vereinfacht, es ging darum Dateien in eine Datenbank zu speichern und später anzuzeigen/aufzulisten. Vorgabe war, das es externen Entwicklern ermöglicht werden soll teile des Programms zu „Steuern“. Zudem sollen Teile des Programms über einen Windows Dienst gesteuert werden. Zu guter letzt, gibt es eine WinForms GUI die der Mensch nutzen kann um Dinge im Programm tun zu können. Zudem werden die Daten in einer Datenbank gespeichert.

      Im Prinzip 3 unterschiedliche Wege (GUIs) teile des Programms zu steuern.

      Daher habe ich die komplette Businesslogik, sowie alle „ViewModels“ in eine .dll gepackt und somit nahezu 100% der Logik dort implementiert. Die WinForms Anwendungen hat nur Logik um Daten anzuzeigen und Eingaben zu empfangen und an die dll weiter zu geben. Bzw. Empfängt die Events die aus der dll gefeuert werden. Die validierung und weiter verarbeitung findet dann in der dll statt.

      Das war mein erstes Projekt das so extrem Logik und GUI getrennt hat und sehr komplex es zu konzipieren. Jetzt allerdings ist es sehr komfortabel Änderungen zu machen und super wartbar. Der weg dahin ist allerdings deutlich schwieriger als einfach alles in die Form zu packen. Es lohnt sich aber :)
      "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
      Stufe 4.3 - MVP PluginForm

      Dies ist eine Variante, die dem Programmstart bei 4.2 ähnelt, aber die Beziehung von Form und ViewModel nahezu aufhebt. Das Form macht sich hierbei quasi komplett austauschbar, wohl ähnlich dem, was @mrMo in Post#2 beschreibt.
      Während bei den anderen 4er-Stufen entweder das ViewModel Besitz des Forms oder das Form Besitz des ViewModels ist, existieren hier beide Komponenten auf gleicher Ebene. Der Aufbau in dieser Stufe gibt einen ersten rudimentären Einblick in die Prinzipien "Dependency Injection" und "Code against Interface" und Beachtung von new is glue. Beide werden vom Startmodul erzeugt und in Wechselwirkung gebracht.

      Wie bei 4.2 kennt das ViewModel das Form, allerdings nicht direkt, sondern das ViewModel weiß nur, dass ihm da ein Ding untergeschoben wird, welches bestimmte Kriterien erfüllt. Darauf kann sich das ViewModel verlassen. Was das Form zur Erfüllung der Kriterien macht, ist dabei dem ViewModel egal.

      Beim ViewModel selbst hat sich vom Code im Vergleich zu Stufe 4.2 fast nichts getan. Statt das Form direkt zu manipulieren, werden nun die Interface-Methoden aufgerufen. Die grundsätzliche Code-Struktur ist aber die gleiche.
      Das Form hat ein paar mehr Aufgaben als bei 4.1. Schließlich muss das Interface implementiert werden.
      Für Einsteiger: Das Interface ist in der Datei IForm.vb: Kein Code, der was ändert, sondern nur eine Auflistung von Dingen, die das Form können muss. Wie das Form diese Punkte umsetzt, steht dann in der Form-Klassendatei.

      Hätte ich konsequent weiter umgebaut, wären natürlich auch die anderen Klassen durch Einführung weiterer Interfaces vom ViewModel entkoppelt worden. Da ich aber hier den Blick auf die Rolle des Forms bzw. dessen Zusammenspiel mit dem ViewModel richten will, habe ich die anderen Klassen so belassen. Der diesbezügliche Umbau dürfte auch Einsteigern nicht so schwer fallen, schließlich ist das Beispiel mit dem Form gegeben.

      Vorteile
      • Form und ViewModel sind effektiv unabhängig voneinander, können also auch unabhängig voneinander entwickelt werden; Solange sie sich beide an den Vertrag (nämlich das Interface) halten, ist alles gut.
      • Das Form kann auch durch ein anderes (G)UI ausgetauscht werden. Solange das Interface implementiert wird, läuft alles. Es ist dann egal, ob sich dann ein WPF-GUI dranhängt, ein Konsole oder eine internetfähige Kaffeemaschine - sofern sie mit einer .NET-Assembly umgehen können ;)
      Nachteile
      • Man muss sich (endlich) mal Gedanken machen, was das UI wirklich können muss. Das kann eine lange Liste werden. Dann ggf. auch über ISP nachdenken bzw. es anwenden, vor allem, wenn man mehr als eine Klasse hat, die man auf diese Weise einbauen will.

      Ich habe bei Stufe 1 gesehen, dass ich ein Thema nur minimal angeschnitten habe, ohne es weiter auszuführen: Testbarkeit. Dazu werde ich wohl noch später was schreiben.
      Dateien
      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.
      Mir scheint, punkt 4 wäre statt MVP besser benannt mit "MVVM" (Model-View-Viewmodel): Model ist dein Game, Viewmodel ist ja als Klasse bereits so bezeichnet, und View ist natürlich das Form.

      Den sog. "MVP-Pattern" hab ich nie wirklich verstanden, du scheinst dir da auch unsicher zu sein, und was auf Wiki steht zum "MVP-Supervising Controller" scheint mir bisserl wirr, und nicht wirklich klärend:
      Da "kümmert sich die Ansicht um die Synchronisation von Präsentierer<->Ansicht" - vorzugsweise via Databinding.
      Dann "kümmert sich der Präsentierer um alle anderen Abläufe zwischen Modell und Ansicht" - welche Abläufe sollen das denn sein?
      Und er sorgt für die "Übertragung der Datenobjekte vom Modell zur Ansicht" - ja was soll die Ansicht denn noch mit Datenobjekten? - ich denke, die Ansicht synchronisiert sich selbst, via Databinding?



      Ich hab mal ein "VVM" - Beispiel gecodet (also MVVM ohne Model), wo ich mir die Ausdifferenzierung von Viewmodel und Model schenke - meine Game- und GameStats-Klassen unterstützen selber Databinding - haben also die Viewmodel-Funktionalität mit drinne.
      In meim Sample gibts sogar etwas, was in WPF einem ValueConverter entspräche - sowas ist bei mir (ähnlich WPF) Obliegenheit des GUIs.

      Was mir an deinen Architektur-Darlegungen bisserl fehlt, ist ein Hinweis auf Utilities/Helper-Code.
      Damit ist ganz allgemein verwendbare Logik gemeint, die auch in anneren Anwendungen genutzt wird.
      Etwa einen GameFileContentManager benötige ich nicht - das Abspeichern wird via Serialisierung abgefrühstückt. Hat auch den Vorteil, dass die zu speichernden Klassen geändert werden können, und werden ohne Nachbesserung ebenso serialisiert.
      Oder - weil ich mehrere datenbindable Klassen habe - habich eine NotifyPropertyChanged-Basisklasse, von der die Datenbinde-Fähigkeit einfach geerbt wird.
      Utilities bewirken eine starke Entlastung der eigentlichen Anwendung von kleinteiliger Logik. Der dortige Aufwand schrumpft zusammen auf Einzeiler, und die aufgerufene Logik funktioniert auch zuverlässiger als jedesmal eine Neu-Entwicklung pro Anwendung.



      "MVP-Passiv View" gefällt mir nicht besonders.
      Da kann ich keine Trennung von View und Viewmodel mehr erkennen, wenn alles alles nur noch im Viewmodel stattfindet, und sogar das Form selbst ins Viewmodel geholt ist. Quasi der Klumpen wandert komplett aus dem Form ins Viewmodel - scheint mir eher eine Verschlimmbesserung.
      Ich finde es ganz in Ordnung, wenn im View was zu tun bleibt: ButtonClickse empfangen/zuordnen, oder - wie im "ValueConverter" gezeigt - besonders aufwendige View-Erfordernisse zu erfüllen - gehört ins View - mach ich ins Form.
      Dateien
      • VvmKlassen00.zip

        (16,82 kB, 134 mal heruntergeladen, zuletzt: )

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

      Tja, ob jetzt MVP oder MVVM - das hab ich eben selber noch nicht raus. Ich habe MVVM immer aus Unwissenheit mit WPF assoziiert und daher eine entsprechende Benennung hier vermieden. Stellt sich dann natürlich die Frage, wie groß der Aufwand von MVVM in WinForms tatsächlich ist und ob das dann wirklich eine so inakzeptable Hürde wäre, es auch anzuwenden, wie manchmal zu lesen ist. Klar, nicht bei jeder Pieps-Kleinkramanwendung muss man gleich mit MVVM u.ä. kommen. Aber auch dafür ist dieser Thread gedacht. Das Projekt ist klein. Und trotzdem sind wir beim Thema Umsetzung von MVP/MVVM angekommen. Wir könnten ggf. also zeigen/ermitteln, wieviel tatsächlicher Aufwand dafür betrieben werden muss, um es einzubauen/umzusetzen.
      Ich hab leider auch mitbekommen, dass ich zwar MVP schreibe, aber nicht von Presenter rede, sondern von ViewModel. Ändern werde ich es aber nicht mehr. Allein schon wegen des hiesigen 1. Abschnitts MVP oder doch eher MVVM?
      Bzgl. Supervising Control sehe ich schon viele Sachen in 4.1 umgesetzt und als zutreffend an. Dass der Presenter eben die Daten vom Model über eigene Strukturen an die Ansicht weiterleitet, indem es eben seine PropertyValues verändert und dies per PropertyChanged-Event an die BindingSources meldet, passt doch. Aber wurscht. Wir wollen ja keinen Wikipedia-Artikel diskutieren. Vielleicht ist dort auch die Beschreibung schwammig.

      Zu Deinem Beispielprojekt: Aha, vielen Dank dafür. Du arbeitest also nicht nur mit einem ViewModel, welches an das Form per BindingSources in Verbindung steht, sondern mit mehreren, die Model und ViewModel in einem sind, also quasi MVM. Auch ein interessanter Weg. Das Form ist weiterhin Oberboss/Supervising Control. Wenn Du die Kategorisierung erlaubst.

      ErfinderDesRades schrieb:

      Was mir an deinen Architektur-Darlegungen bisserl fehlt, ist ein Hinweis auf Utilities/Helper-Code.
      Nun, mein Hauptaugenmerk liegt ja bei der Rolle des Forms. Dass es einen Haufen Aufgaben übernehmen kann, ist mir bewusst. Aber davon wollte ich ja auch ein stückweit weg. Dass man diese Aufgaben durch Extensions und notfalls Bibliotheksaufrufe kürzen kann: zweifellos.

      btw: Dass Du inzwischen bei Property-Settern statt z.B. Set(value As Integer) nur noch Set schreiben musst, weißt Du?

      Stufe 4.2 - Passivform ist eine krasse Form-Rolle. Ich wollte dabei nur zeigen, wie ich verstehe, wie man diese MVP-Variante in WinForms umsetzen könnte. Dass dabei dann die ganzen Aufgaben dem ViewModel/Presenter angelastet werden, ist mir bewusst und das habe ich ja auch bei den Nachteilen geschrieben. Mir ging es dabei auch darum, Einsteigern zu zeigen: das Form muss nicht alles können, es geht auch ganz anders - in einer sehr extremen Weise.
      Ich persönlich werde zukünftig wohl mein Hauptaugenmerk auf 4.3 und auch etwas auf 4.1 legen. Wie weit ich damit komme, kann ich - wie geschrieben - nicht abschätzen. Aber es sind eben auch Varianten oder Ausbaustufen wie Deine, die Vielfalt die in die Gedankenwelt der (nicht-nur-)Einsteigerprogrammierer bringen (können), um vom Chaos wegzukommen, den die ersten Ausbaustufen früher oder später verursachen.
      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.

      VaporiZed schrieb:

      Stellt sich dann natürlich die Frage, wie groß der Aufwand von MVVM in WinForms tatsächlich ist und ob das dann wirklich eine so inakzeptable Hürde wäre, es auch anzuwenden, wie manchmal zu lesen ist.
      Hmm - nanu?
      Das ist doch mit diesem Tutorial im praktischen Beispiel geklärt - Variante 4.1 folgt doch dem MVVM-Pattern.
      Eine Hürde kann ich da garnet erkennen, sondern der Pattern zeigt sich bereits bei dieser Mini-Anforderung als praktikabel und wohltuend strukturierend.
      Also natürlich ist 1. - die EinsteigerLösung - objektiv die beste: eine Anwendung, deren Code auf eine Bildschirmseite geht, ist an Qualität einfach nicht zu überbieten.
      Aber als Beispiel für eine plausible Anwendungs-Strukturierung durch Trennung von Zuständigkeiten macht sich imo der MVVM hier ganz ausgezeichnet.

      VaporiZed schrieb:

      Ich persönlich werde zukünftig wohl mein Hauptaugenmerk auf 4.3 und auch etwas auf 4.1 legen.
      Ach - zu 4.3 - Plugin-GUI - habich mich ja noch garnet ausgelassen.
      Jo - ich hab sowas noch nie gebraucht, und auch noch nie ein überzeugendes Beispiel gefunden.
      Wieso zum Kuckuck sollte man, wenn man eine Anwendung mit einer Oberfläche entwickelt hat, für dieselbe Anwendung eine zweite Oberfläche entwickeln - was soll das?
      Wenn die erste Oberfläche nicht ok ist, dann soll man sie eben verbessern. (Und dabei wird man zu 99% auch die BL anfassen, und dann ist eh Essig mit die ganzen dollen Plugin-Interfaces.)
      Wenn man aber keine zweite Oberfläche baut, dann ist der ganze PlugIn-Kram auch Humbug, und ist extremes Beispiel für Verstoss gegen Yagni.
      Yagni-Verstösse sind nicht "KavaliersDelikte", wo man sich ein "Nice to have", bastelt und freut, sondern genau diese unnötigen "Nice to haves" stehen sinnvolleren Weiterentwicklungen später ja massiv im Wege.
      Auch was @mrMo in Post#2 anführt beruht ja nicht auf PlugIn, sondern scheint mir eine Server-Client-Architektur: Verschiedene Anwendungen nutzen dieselbe BusinessLogik - nicht: Verschiedene GUIs für dieselbe Anwendung.

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

      ErfinderDesRades schrieb:

      Ach - zu 4.3 - Plugin-GUI - habich mich ja noch garnet ausgelassen.
      Jo - ich hab sowas noch nie gebraucht, und auch noch nie ein überzeugendes Beispiel gefunden.
      Wieso zum Kuckuck sollte man, wenn man eine Anwendung mit einer Oberfläche entwickelt hat, für dieselbe Anwendung eine zweite Oberfläche entwickeln - was soll das?
      Plugins werden dann verwendet, wenn man eine Veränderung der Funktionalität schaffen will, die nicht vom Entwickler selbst, sondern vom Anwender oder einem anderen Programmierer implementiert werden kann.
      Als Beispiel hierfür könnte man eine Winamp-Skin nennen.

      Vielleicht möchte jemand ein eigenes GUI-Plugin schreiben, weil ihm die bestehende Oberfläche zu überladen ist für die eine Funktion, die er braucht und damit die nicht benötigten Funktionen ausblenden.

      Gebraucht habe ich so was auch noch nicht.
      --
      If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
      --
      Nun zum Thema »Nutzung mehrerer Formulare«

      Bei den gezeigten Bildern habe ich versucht, die Rechteckgröße proportional zur "Relevanz" einer Sache darzustellen. Relevanz kann aber viel bedeuten:
      • Codeumfang
      • Zeit, wie häufig das Programm in diesem Teil unterwegs ist
      • Änderungsaufwand

      Allerdings ist mir die Box beim Sub Main ein gutes Stück zu groß geraten :rolleyes:



      Bei den Kandidaten Einsteigerart und Klumpenform ist es ziemlich einfach. Das Form instanziiert ein weiteres und übergibt die Kontrolle an ein anderes. Wie man vorgeht, hat RodFromGermany ausführlich in Dialoge richtig instanziieren beschrieben.
      Wie dabei Verlauf der Kontrolle ist, habe ich mal versucht, schematisch darzustellen:



      Bei Klassennutzung und Oberbossform ist fast genauso, nur dass der Datenfluss auch mal in den Klassen bzw. konkret in den ViewModels ist. Trotzdem: Die Erzeugung weiterer Forms und die Übergabe der Kontrolle erfolgt immer noch durch die Forms.



      Beim Passivform sieht es schon ein stückweit anders aus. Das Startmodul bzw. die Sub Main übergibt die Kontrolle an das erste ViewModel, welches dann sein Form1 erzeugt. Werden weitere Forms benötigt, erzeugt z.B. ViewModel1 eine ViewModel2-Instanz und übergibt dem die Kontrolle. ViewModel2 erstellt dann wiederum ein Form2, mit dem dann gearbeitet wird usw.



      Beim PluginForm ist es komplizierter, aber nahe am Passivformschema dran. Auch hier fließt die Kontrolle von einem ViewModel zum nächsten, indem sie sich instanziieren. Da die ViewModels das UI, mit dem sie zusammenarbeiten, nicht kennen, wird in der Sub Main eine (auch über ein Interface verlinkte) Formfabrik erzeugt und dem 1. ViewModel übergeben. Dieses kann dann die Fabrik anweisen, ein UI zur Verfügung zu stellen, welches dann kompatibel zu den Anforderungen des ViewModels ist. Mit dem wird dann über das Interface kommuniziert (im Schema gelb dargestellt)



      das Ganze weitergesponnen:
      So kann man mehrere Formfabriken (besserer Name wäre eigentlich UI-Fabriken) erstellen, die eben Konsolen-UIs, WinForms-UIs, … erstellen. Was es dann wirklich wird, wird in der Sub Main entschieden. Und wenn man das dann z.B. noch abhängig davon macht, was in einer beigefügten Textdatei steht, kann man sogar, wenn man das fertige Programm hat, durch Änderung dieser Textdatei entscheiden, was man für UIs bekommt. Vielleicht sind wir dann beim Pattern Abstrakte Fabrik angekommen, aber das geht jetzt zu weit.



      Im Anhang habe ich auch noch für die letzten beiden Schemata je ein Testprojekt angehangen, was das bisherige Projekt nur erweitert, damit man nicht gedanklich wieder von vorne anfangen muss, wenn man sich durch die Beispielprojekte dieses Threads durchhangelt.
      Dateien
      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.

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

      kann übersprungen werden, das war eine zu-Früh-Version

      Stufe 5 - MVVM
      (zumindest so, wie ich es interpretiere :rolleyes: )

      Aufgrund der Weiterentwicklung von WinForms unter .NET gibt es nun die Möglichkeit einer verbesserten Form des DataBindings, zu lesen hier: MVVM mit WinForms - kleiner Einstieg für Anfänger

      Das Form ist bleibt natürlich voll funktionsfähig, die Verwendung von altem und neuen DataBinding wurde jetzt aber eingesetzt/intensiviert.
      In der FrmMain.vb gibt es fast keinen Code mehr. Geblieben sind: Datenzuordnung an drei BindingSources, Anzeige des Forms, Schließen des Forms - allerdings ohne den Einsatz von EventHandlern.

      Das Form ist mittels Interface an eine ViewModel-Klasse angeschlossen, die quasi alles koordiniert, inklusive einer neu hinzugekommenen Klasse RoundSummary. Damit ähnelt diese Ausbaustufe dem MVP Passivform.
      Sich verändernde Daten in den Klassen Game und RoundSummary werden mithilfe der INotifyPropertyChanged-Schnittstellenimplementierung an das GUI weitergereicht, sodass dies auf dem aktuellen Stand bleibt. Button-Befehle werden über RelayCommands an das ViewModel weitergereicht, welches dann die eigentlichen Befehle ausführt bzw. diese an die zuständigen Klassen weiterleitet.

      Das Form hat somit quasi eine pure Anzeigerolle erhalten, ähnlich wie beim MVP Passivform, aber mit m.E. besseren Methoden.

      Ich bezweifle zwar aufgrund meiner erst frisch gewonnenen Erkenntnisse, dass das sauberes MVVM ist, aber ich freue mich über Verbesserungsvorschläge.
      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.

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

      Stufe 5 - MVVM (As I meant it to be :whistling: )

      Hatte im Vorpost den Code vergessen. Macht nix, war eh eher ein Mischmasch aus MVP und MVVM - Stichwort Kommunikation zwischen VM und View mittels Interface und VM instanziiert das View. Daher nochmal Rolle rückwärts und von vorn:

      Das MainForm wird nun in der Sub Main erzeugt und ihm wird als DataContext ein ViewModel mit an die Hand gegeben. In der FrmMain.vb selbst gibt es fast keinen Code mehr. Geblieben ist im CodeBehind nur noch die ViewModel-Zuordnung an eine BindingSource.

      Die ViewModel-Klasse koordiniert quasi alles, inklusive einer neu hinzugekommenen Klasse RoundSummary.
      Sich verändernde Daten in den Klassen Game und RoundSummary werden mithilfe der INotifyPropertyChanged-Schnittstellenimplementierung an das GUI weitergereicht, sodass dies auf dem aktuellen Stand bleibt. Button-Clicks des UI werden über RelayCommands an das ViewModel weitergereicht, welches dann die eigentlichen Befehle ausführt bzw. diese an die zuständigen Klassen weiterleitet.

      Das Form hat somit quasi eine pure Anzeigerolle erhalten, ähnlich wie bei 4.2 MVP Passivform, aber mit m.E. besseren Methoden. Gleichzeitig ist das Form Eigentümer des ViewModels, das ViewModel kennt das Form nicht. Damit ähnelt diese Stufe wiederum 4.1 MVP Oberbossform/Supervising Controller

      Einen BourbonWermutstropfen gibt es aber: Das Schließen des MainForms. Das scheint mir auch in WPF-MVVM laut ersten Recherchen ein noch nicht endgültig gelöstes Problem. Mein Ansatz:
      • Ich binde in der FrmMain.Designer.vb die DialogResult-Property des Forms an die DialogResult-Property des ViewModels.
      • Ich starte das MainForm in der Sub Main via ShowDialog.
      • Ich verwende einen RelayCommand, um die DialogResult-Property des ViewModels und mittels INotifyPropertyChanged auch die gleich benannte des MainForms mit einem anderen Wert zu belegen, was zum Schließen des MainForms und somit zum Programmende führt.
      Das Ganze hat aber 3 Nachteile:
      1. In der Sub Main wird das MainForm nicht klassisch per Application.Run(New FrmMain) gestartet/angezeigt, da ich sonst keinen Dialog erhalte, den ich mittels Setzen des DialogResults automatisch schließen lassen kann.
      2. Das DataBinding für die Form-Property DialogResult muss manuell in der Form.Designer.vb erfolgen, da diese nicht im Designer selbst beim DataBinding-Dialog zur Verfügung steht bzw. angeboten wird.
      3. Das ViewModel muss den Datentyp DialogResult kennen, was dazu führt, dass man das ViewModel nicht für eine WPF-MVVM-App nutzen kann, weil dort die Window-Property DialogResult vom Typ Boolean? ist.
      Für Gegenvorschläge bin ich offen.
      Das Einfachste (Stichwort KISS) wäre natürlich im MainForm-CodeBehind ein EventHandler für den ButtonClick und Me.Close. Aber ist das MVVM-gerecht? Schließlich macht das Form jetzt was selbst und das VM macht es nicht.

      Schön wäre, wenn andere Vorschläge keinen Code im CodeBehind des Forms benötigen und das ViewModel nicht das MainForm als Parameter nutzt.
      Dateien
      • MVVM.zip

        (10,95 kB, 184 mal heruntergeladen, zuletzt: )
      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.
      Nur als Vorschlag , ich würde das umgekehrt machen. Erstell das ganze erst in WPF und MVVM.
      Wenn das funktioniert dann würde ich versuchen das ViewModel projekt in WindowsForms umzusetzten.
      Wenn du wirklich nur die Oberfläche austauschen Kannst ohne den Code im ViewModel-(Projekt) anzurühren dann hast du es richtig gemacht.

      Binde direkt an die Eigenschaften der Game Property in dein (Main).ViewModel.