Autor Beitrag
Glowhollow
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 77



BeitragVerfasst: Mi 17.04.19 09:12 
Guten Morgen !

Es ist soweit, die ersten Tests mit der Konvertierung des VB-Projekts zu C# sind abgeschlossen, bis auf die eine Schnittstelle die nicht registriert ist, läuft alles einwandfrei.

Jetzt gehts ans MVC Pattern, und hier könnte ich etwas Inspiration brauchen von euch.

Habe hier mehrere Klassen (mysqlcon, pwencode, useraccess), die wiederrum in mehreren Forms Daten aktualiseren müßten.

Gemäß MVC, würde ich in einer Model-Klasse (ist das richtig), die Daten aufbereiten, um sie dann per Event, an die Forms zu triggern, die dann wiederrum, die daten aus dem Model ziehen.

Was ich hier noch nicht so ganz umreisse, ist die sache mit den Buttons in den einzelnen Forms, die Listener dafür definiere ich i.d.R. in der Form Klasse an sich, oder soll ich die in Control packen. Aber dann müßte ja Control, sowohl fürs Event werfen, als auch für die Listener zuständig sein.

Ist das richtig so ?

Moderiert von user profile iconTh69: Beiträge zusammengefasst

Habe diesbezüglich

www.codeproject.com/...-Pattern-with-Csharp

gefunden, werde mich jetzt daran großteils halten, wenn aber jemand noch ideen hat, gerne :)
Glowhollow Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 77



BeitragVerfasst: Mi 17.04.19 13:04 
Ich hab zu dem Codebeispiel eine Frage.

Er hat das ganze in 4 Projekte unterteilt.

MVC-Controller
MVC-Model
MVC-View
und USEMVCApplication.

Jetzt habe ich versucht das zu adaptieren, stelle jedoch fest, das sowohl meine MVC-Model, als auch MVC-Controller keine statische Main Methode haben.

Das hat der Entwickler (soweit ich das nachvollziehen konnte), bei seinen Unterprojekten auch nicht gemacht, wieso kann ich dort kompilieren, und bei mir meckert er rum. Was habe ich übersehen ?

Vielen Dank !
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Mi 17.04.19 14:20 
Warum sollten sich auch in den anderen Klassen eine statische Main-Methode befinden? Es sollte nur in genau einer Klasse (meist Program oder so wie das Projekt heißt) diese Main-Methode geben.

Was ist denn die genaue Fehlermeldung bei dir?

Edit: Wenn du verschiedenen Projekte (innerhalb der Solution) anlegst, dann sollte es nur ein Hauptprojekt (WinForms) geben, die anderen wären dann reine Bibliotheken (Libraries) und müssen dann referenziert werden. M.E. solltest du aber hierfür nicht verschiedene Projekte anlegen, sondern arbeite mit passenden Unterverzeichnissen (wenn das Projekt größer wird, dann kannst du immer noch Teile davon in Libraries auslagern).


Und zum eigentlichen Thema:
Viel wichtiger ist ersteinmal eine generelle Architektur, s. z.B. [Artikel] Drei-Schichten-Architektur.
Zum Thema MVC solltest du dir besser den Wiki-Artikel durchlesen: Model View Controller.

Der Autor vom Codeproject-Artikel hat m.E. es nicht richtig umgesetzt, denn die View sollte keinen direkten Aufruf von Controller-Methoden haben (d.h. keine Referenz auf den Controller besitzen), sondern über Ereignisse benachrichtigen.
Bei WinForms ist es aber so, daß eine Form-Klasse selbst schon Controller-Funktionalität enthält (also z.B. die Eingaben entgegennimmt), so daß es m.E. nicht sinnvoll ist, eine eigene Controller-Klasse anzulegen.
Wichtig ist hauptsächlich nur die Trennung von Logik und UI (View).
Glowhollow Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 77



BeitragVerfasst: Mi 17.04.19 16:32 
Ok, hab mich verguckt, habe beim anlegen, nicht darauf geachtet, das es Klassenbibliotheken sind, sondern hab ne klasse erstellt, und dann auf klassenbibliothek als Ausgabetyp angelegt. Das habe ich jetzt behoben. Jetzt gehts.

Andere Frage (möchte jetzt keinen neuen Thread dazu aufmachen). Ich würde gerne im controller eine Funktion auslösen, die im View, den Focus auf ein bestimmtes Objekt legt.

Leider scheitern meine Ansätze daran, das ich zwar das Objekt übergeben kann (in dem Fall ...

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
public object ReturnUserObj
{
  get { return this.TB_Splash_user; }
  set { }
}


wobei TB_Splash_user ein Textfeld ist.

Würde dann gerne im Controller, einfach _view.ReturnUserObj.Focus() machen, aber das funktioniert so nicht.

Wie fokussiere ich eine Objekt, im Controller über die View hinaus ?

Moderiert von user profile iconTh69: Code- durch C#-Tags ersetzt
Moderiert von user profile iconTh69: C#-Tags hinzugefügt
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Mi 17.04.19 16:55 
Warum object als Datentyp?
Glowhollow Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 77



BeitragVerfasst: Mi 17.04.19 17:00 
Naja, weil Object, das übergeordnete Element einer Textbox ist oder nicht ?

Ich dachte, das wenn ich Object übergebe, das ich damit gleich die Properties mit übernehme, dem ist aber nicht so.

[EDIT:] Auch das ändern des Datentyps auf TextBox, ändert nichts daran.

Müßte im Prinzip, die Textbox übergeben, so das ich den Focus im Controller auf diese Textbox setzt.

[EDIT2:] Gut habe jetzt eine Lösung dafür, auch wenn sie unsauber ist.

Im Splashscreen, wo die Textbox ist, habe ich einen getter und setter

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
public TextBox ReturnUserObj
{
    get { return this.TB_Splash_user; }
    set { this.TB_Splash_user.Focus(); }
}


Durch die Zuweisung, z.bsp. von null, wird set ausgeführt, und setzt den Focus richtig.

Ich bin mir aber ziemlich sicher, das nicht die optimalste Lösung ist, gibts dafür nen anderen Ansatz ?

Moderiert von user profile iconTh69: Code- durch C#-Tags ersetzt
Moderiert von user profile iconTh69: C#-Tags hinzugefügt
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Mi 17.04.19 17:28 
Das sind aber elementarste Grundlagen der OOP.
Um den Focus zu setzen, mußt du einen Datentyp (Klasse) verwenden, welche dies unterstützt (also Control oder eben eine davon abgeleitete Klasse wie TextBox).

Und dann sollten Getter und Setter generell symmetrisch sein:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
public TextBox ReturnUserObj
{
    get { return this.TB_Splash_user; }
    set { this.TB_Splash_user = value; }
}

(wobei hier der Setter nicht vernünftig ist, s.u. - also gelöscht werden sollte)
Und dann kannst du darauf dann Focus() aufrufen:
ausblenden C#-Quelltext
1:
form.ReturnUserObj.Focsu();					


Dies ist zwar technisch so möglich, sollte jedoch aus Design-Gründen nicht so benutzt werden (da es u.a. gegen die Regel verstößt, Interna einer Klasse nicht nach außen zu geben)!
Besser wäre wohl eine Methode SetFocus() (mit passendem Namen), welche dann nur intern auf die TextBox zugreift.
Glowhollow Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 77



BeitragVerfasst: Mi 17.04.19 17:53 
Du wirst lachen, als ich das mit dem Object gemacht habe, war ich sicher, das es so gehen würde. Aber das wollte so nicht. Mit TextBox als Datentyp, hatte ich das garnicht getestet. Sry.
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Do 18.04.19 09:27 
Zum Thema MVC stelle ich dir mal die Vorgehensweise bei meinen (meisten) eigenen Projekten vor.

Die MainForm sollte möglichst nur aus folgenden Elementen bestehen:
- Menü (inkl. Code für Menü-Aktionen)
- UserControl (Darstellung)
- StatusBar (optional)

So stellt die MainForm den Controller dar, d.h. die User-Interaktion findet hauptsächlich über diese Klasse statt, d.h. bei Aufruf eines Menüpunktes (über Button-Klick oder mittels Tastatur-Kürzel) wird die eigentliche Aufgabe weiterdelegiert (z.B. Aufruf eines Dialogs oder Aufruf einer Methode des UserControls).
Das UserControl wiederum ist dann je nach Umfang auch wieder auf mehrere (User)Control-Klassen hierarchisch unterteilt (Beispiel: beim Windows-Explorer gäbe es je nach Frame eine eigene Control-Klasse, also Verzeichnisansicht, Dateiliste, Vorschau, ...).

Als wirklich passierte Anekdote kann ich dir dieses "Parade"-Beispiel erzählen:
Für einen Editor (für ein Computerspiel) hat ein Designer in einer-Form alle Controls (ein TabControl mit ca. ein Dutzend TabPages und jeweils zig Elementen) mittels des VS-Designers erstellt gehabt.
Nun hat der Entwickler (bevor ich zum Projekt hinzugeholt wurde) den komplette UI-Code in dieser einen Form-Klasse untergebracht (>= 30.000 Zeilen)!
Ich habe dann den Code 'refactored' und ein Main-UserControl (mit der TabPage) und jeweils UserControls für die einzelnen TabPages erstellt (und noch ein paar Extra-Controls für Elemente, welche auf mehreren TabPages benutzt wurden - und jeweils separat ausprogrammiert worden sind :shock: ).
Nach dem Umbau hatte ich ca. 20 Klassen mit jeweils nur noch 500-1500 Zeilen (hat mich aber insg. ca. einen Monat beschäftigt - inkl. Weiterentwicklung und Testen).
Glowhollow Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 77



BeitragVerfasst: Do 18.04.19 09:41 
Das war bestimmt ne Menge Arbeit.

Ich weiß, ist nicht eure Aufgabe, aber so ganz habe ich das MVC-Pattern auf die Auflösung des Projekts noch nicht ganz durchdacht.

Ich habe den EntryPoint Program.cs - dieses instanziert splashscreen und mainform. Ich habe jetzt einen splashscreen controller geschrieben, das funktioniert auch soweit ganz gut. Jetzt hat aber splashscreen auch einen Button (Login-Screen), welcher eine Datenbankabfrage macht und auf Daten in der Mainform zugreifen möchte.

Hier müßte ich ja für die Mainform einen Controller schreiben, der sich da um die Steuerung von Mainform, und die aktualisierung des Views kümmert.

Jetzt stellt sich mich aber die Frage, wie ich den Focus, des aktiven Controllers switche, da im splashscreencontroller, nur funktionen drin sind, die den Splashscreen betreffen (mal ganz von deren internen abhängigkeiten).

Es soll doch immer nur 1 Controller aktiv sein - right ?
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Do 18.04.19 10:08 
Vergiss das mit den extra Controller-Klassen, das ist unnötig!

Und wieso muß ein Splash-Screen (bzw. Login-Dialog) auf Daten der MainForm zugreifen (wo diese doch noch nicht mal angezeigt wurde)?

Der Ablauf sollte doch eher so sein:
ausblenden Programmlogik
1:
2:
3:
4:
- Login-Dialog aufrufen
- zurückgegebene Daten an Modell weiterreichen (und diese reicht den eigentlichen DB-Login an den DAL weiter)
- MainForm anzeigen
  - auf Modelldaten zugreifen

Statt einer Controller-Klasse benötigst du also eine gemeinsame Modellklasse, welche von allen deinen Forms (und Dialogen) benutzt wird.
Glowhollow Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 77



BeitragVerfasst: Do 18.04.19 10:55 
Hey Th69,

nun, in der Mainform wird links unten angezeigt, ob der Server erreichbar ist. Nach dem Login, wird erst mal die datenbank überprüft,ob sie erreichbar ist, und soll, wenn das soweit in Ordnung ist, in der Mainform ein value setzen.

Was ist ein DAL ?

Im Moment hab ich die dbcon (was für die datenbankabfragen zuständig ist), schon im Bereich Model liegen, jedoch würde diese gerne Daten in den Forms beschreiben und ich hab noch kein sinniges konzept, wie ich das alles am besten kapsele.

Soweit ich das ja verstanden habe, ist das Model für die Datenbereitstellung zuständig, diese müssen ja vom controller angetriggert werden, das das view, sich die Daten holt und darstellt. Soweit die Theorie.

Nur in diesem Projekt, schreibt eine Form in die andere und umgegekehrt, und ich bin dabei, das Schritt für Schritt, auseinanderzunehmen (nachdem der Transfer von VB->C# abgeschlossen ist) und wieder zusammensetzen.

Jedoch hänge ich noch bei der "Trennung" des ganzen.

Um dir einen groben Überblick zu geben.

ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
Im Model befinden sich folgende Klassen:
- dbcon (mehrere Methoden, mit diversen Datenbankabfragen)
- pwencode (separierte klasse, das für die Verschlüsselung von passwörtern zuständig ist)
- useraccess (schreibt wild zwischen diversen forms rum)

Im View habe ich die Forms:
- debugform (eine Form, die zum debuggen benutzt wird)
- Form1 (die Hauptform)
- Fulltable (eine Form, die in der Form1 angezeigt wird)
- pwchange (eine Form zum ändern das Passworts)
- splashscreen (der startbildschirm)

und im Controller hab ich im Moment:
- splashcontroller (für die Steuerung des Splashscreens zuständig)
- mainformcontroller (für die mainform)


Da aber über den splashscreen, einen dbcon aufruf habe, das in der mainform was ändern soll, stellt sich die Frage, wie ich das am besten löse. Es sind nur ganz wenige Variablen die gesetzt bzw. geändert werden sollen, und den Splashcontroller, wird im Prinzip nur einmal aufgerufen, bzw. erneut, wenn man da wieder zurückkommt (z.bsp. nach einem relogg).

Ingesamt, muß die wilde Kommunikation, so wie sie jetzt besteht, zwischen den Forms aufhören. Die Frage ist die, wie löse ich es am besten, das Änderungen in den Forms untereinander nur über die view laufen sollen.

Soll ich hier mit "listener" arbeiten (und events schmeissen), damit sich die View die daten aus den Models holt. Wie soll die das gegenseitige ändern innerhalb der Forms untereinander separiert werden, insbesondere da ich im Moment, als kleines Beispiel folgende Situation habe.

Ich arbeite ja auch mit Interfaces, so hat der MainformController auch eine Referenz zu dem Interface, interssanterweise habe ich hier im prinzip 2x eine getter und setter methode drin.

Beispiel: die dbcon, will eine Variable in der Mainform ändern. Nennen wir diese hier mal LoggedinUserID. Diese ist über getter und setter in der Mainform gesetzt, da ich das aber über den controller erledigen soll, habe ich im controller, eine "ähnliche" variante eines getter und setters, wobei ich hier z.bsp. eine Funktion habe, wie setLoggedInUserID(int value), die dann wiederrum im view, den getter und setter anspricht.

Ich kann mir nicht vorstellen, das das die beste Lösung ist. Oder ist das wirklich so, das mir Hilfsmethoden schreiben muß, um das Hin und Herschieben der Daten zwischen der Forms stattfinden zu lassen ?
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Do 18.04.19 11:30 
Hast du dir den Link zur Architektur mal durchgelesen (da wird auch erklärt, was ein DAL ist)?

Und daraus ergibt sich auch, daß die Logik (Modell) komplett unabhängig von der UI zu sein hat (also nicht: "useraccess (schreibt wild zwischen diversen forms rum)").
Und wenn die UI sich auf Grund von Änderungen in der Logik anpassen soll, dann sollte die Logikschicht entsprechende Ereignisse (events) bereitstellen, welche dann von der UI abonniert werden (niemals umgekehrt!) - steht auch so in meinem Artikel über die "Kommunikation von 2 Forms" (bzw. zwischen Form und Logik).

Baue die Logikschicht immer so auf, daß sie auch in anderen Projekten (Konsole, Web, Multi-Platform, ...) verwendet werden könnte. Und daher wird auch der Data Access Layer (DAL) separat davon gehalten, damit man z.B. einfacher auf eine andere DB (oder ein anderes Speicher-Format) wechseln kann.

Was soll denn der Sinn des gesamten Programms sein (also die eigentliche Business-Logik)?

PS: Und der Sinn deiner Controller-Klassen erschließt sich mir (aus deiner Beschreibung) immer noch nicht. Was genau willst du denn damit "steuern"? User-Eingaben und Datenausgabe erfolgt doch schon in den Forms (bzw. UserControls) selber (dafür gibt es ja schließlich das WinForms-Framework).
Glowhollow Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 77



BeitragVerfasst: Do 18.04.19 14:12 
Der Sinn des Programms, ist die Auswertung/Darstellung von Buchungsdaten seitens eines Reisebüros. Hier sollen Daten aufbearbeitet werden, z.bsp. welche Flüge, von Flughafen A nach Flughafen B werden in Zeitraum x, gebucht, wie ist dort der Gewinn pro person, etc.. Summiert und das ganze drumherum.

Das Programm an sich ist ja auch schon funktionsfähig, nur nicht nach MVC aufgelöst und ich frage mich hier ernsthaft, wie ich alle diese hin und her schreiberei von Values zwischen den Forms, so auflösen kann, das ich diese Trennung ausführen kann.

Ich habe im moment die dbcon noch im model drin. Ja dbcon liefert daten zurück, aber dbcon schreibt auch in forms, und das versuche ich gerad zu umgehen. Delegates können hier die Lösung sein, allerdings habe ich sofern noch nie mit Delegates gearbeitet, vor allem weil ja die Trennung auch vorsieht, das dbcon - keine direkte Verbindung zur mainform hat. Da muß ich noch etwas grübeln.

Deinen Beitrag bezüglich der Kommunikations zwischen mehreren bzw. in deinem Falle 2 Forms, war sehr interessant. Habe auch noch nicht alles verinnerlicht, zeigt aber die Richtung in die es gehen soll.

Habe damals auch mit Events gearbeitet (wenn auch nicht in C#). Mir ist natürlich bewußt, das das hier auch mit events lösbar ist, dennoch löst es noch nicht ganz die Sichtbarkeit zwischen der Klasse dbcon und MainForm. Ich werd mich da von einem Spezi, ein bischen inspirieren lassen, im Moment kann ich diesen Knoten im Kopf nicht lösen. Ich weiß, wies es theoretisch aussehen soll, kann es jedoch gerade nicht umsetzen. Erfordert mehr Forschung !

Danke für deine Mühen, ich bin dieses WE nicht da, werde mich aber damit auseinandersetzen sobald ich Zeit habe, also nicht wundern, wenn ich nicht sofort reagieren.

[Nachtrag]: Ich werde jetzt erst mal alle Methoden und Abhängigkeiten grafisch aufarbeiten, das ich da mal durchblicke. Dann kann man ja immer noch gucken, wie ich das am besten auflöse.
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Do 18.04.19 17:46 
Zitat:
Das Programm an sich ist ja auch schon funktionsfähig, nur nicht nach MVC aufgelöst und ich frage mich hier ernsthaft, wie ich alle diese hin und her schreiberei von Values zwischen den Forms, so auflösen kann, das ich diese Trennung ausführen kann.


Vom Ist zum Soll zu kommen ist hier vermutlich rein evolutionär auch eher schwierig. Ich bezweifle das du so, insbesondere mit diesen Hintergedanken wie "wie ich alle diese hin und her schreiberei von Values zwischen den Forms, so auflösen kann", weiterkommst. Formen kommunizieren in einem MVC basierten UI System nicht miteinander. Formen kommunizieren nur ,je nach MVC Ausprägung, mit ihrem Controller/Presenter/Model/ViewModel. Ganz einfach gesprochen Die Kommunikation geht immer durchs Model. Form nach Model und dann wiederum Model nach anderer Form. Wenn man sehr viele MVC Klassengruppen hat also vermutlich eine größeren Workflow in seiner Anwendung braucht man andere Anwendungspatterns. MVC ist ein reines UI Steuerungpattern kein Architekturpattern.

Das du dir die Methoden/Abhängigkeiten explizit aufbereiten willst ist ein guter Anfang. Ich würde dir empfehlen das zu nutzen und damit dann nicht deine vorhandene Applikation evolutionär umzubauen sondern parallel eine neue Applikation zusammenzusetzen. Du musst ja den alten Code nicht vergessen oder ignorieren. Sondern du kannst den als Code-Steinbruch verwenden und gezielt Teile in die neue Applikation übernehmen so wie es sich dann anbietet.

Initial solltest du dann, sobald du einen Überblick hast was es alles für Operationen in der Anwendung gibt ein passendes Model daraus entwickeln. Als etwas das die Daten der Anwendung darstellt und Wege (z.b. über einen DAL) diese Model mit deiner Datenbank abzugleichen. Das ganze OHNE irgendeine UI. Die kommt erst anschließend.