MVVM - Viele Objekt-Abstufungen - Suche nach Lösungsvarianten

  • WPF

Es gibt 6 Antworten in diesem Thema. Der letzte Beitrag () ist von Nofear23m.

    MVVM - Viele Objekt-Abstufungen - Suche nach Lösungsvarianten

    Hallo an die VB-Gemeinde.

    Ich habe seit einiger Zeit MVVM für mich entdeckt und riesen Freude am Programmieren (wieder)entwickelt.
    Nun bin ich an einem Projekt dran, wo sich MVVM zunächst als "die Lösung" präsentierte, mittlerweile aber ein paar Probleme aufgeworfen hat.


    Ausgangslage:

    Ich habe eine UI (View) ein ViewModel (ViewModel) welches dynamisch Objecte erzeugt (Models) diese stellen Lasersysteme dar.
    Da die Systeme mehrere verschiedene Parameter besitzen und diese sich auch signifikant von einander unterscheiden können, habe ich hier etwas viele Klassen aufgebaut.

    Parameter
    von diesem erben die SerialParameter und die UsbParamete, da eine Anlage sowohl über Serielle wie auch USB angesprochen/angeschlossen werden kann. Die zuvor erwähnten "dynamischen Objecte" nenne ich LaserSystems und diese besitz je nach Analge eine n Anzahl von Parametern und je nach Anlage sind es SerialParameter oder UsbParameter.

    Soweit so kompliziert.

    Problem:

    Die Idee wäre ja beim Start der Applikation, dass diese nach den Anlagen sucht und bei Vorhandensein, eben jene Objecte passend zur Anlage erzeugt und in einer List aufführt. Wählt nun der Anwender eines der Anlagen aus, wird die UI entsprechend angepasst (nur einzelne Controls die weg müssen oder wieder angezeigt werden).
    Ein "runtimeListener" arbeitet in einem eigenen Thread und stellt die Kommunikation zwischen Applikation und Anlage her.
    Dieser hat eine liste von Parametern die es immer abfragt und eine taskliste, die der Benutzer durch Interaktion mit dem GUI befüllt.
    Hier noch ein Versuch es shematisch dazustellen:



    Bisher hatte ich im ViewModel Eigenschaften die eine Binding-Beziehung zum View hatten und durch aufruf der Funktionen im Model und deren Rückgabewert diese Eigenschaften gesetzte. Die Setzung neuer Werte rufte den PropertyChangedEventHandler auf den Plan und so wurde das UI auch aktuell gehalten.

    Jetzt habe ich vieles davon auf das LaserSystem aufgebaut. Dort wird festgestellt welche Parameter welchen Wert hat, dort arbeitet der RuntimeListener im Hintergrund und gibt Werte aus. Nun stehe ich davor und habe überhaupt keinen Plan wie ich die Werte einigermassen schlau zu den Eigenschaften im ViewModel bringe.

    Falls es da keine gute Möglichkeit dafür gibt, bin ich auch bereit alles umzubauen.
    Bin also wirklich für jeden Vorschlag offen.


    Danke an alle die sich die Zeit nehmen
    Bilder
    • Untitled Diagram.jpg

      81,95 kB, 786×793, 114 mal angesehen
    Hallo @Hutti

    Wennich das aus deinem Text richtig verstehe sehe ich hier ansich kein Problem dies so in einem ViewModel abzubilden.
    Die ganze Sache mit dem Lasersystem und so lass mal aussen vor.

    Ich nehme an du weist welche Art und Werte von Parametern von den Lasersystem zurückkommen oder zurückkommen können.

    Schritt 1:
    Mach ein Interfaces für die Kommunikation und die Parameter damit du hierfür "Fakes" (Mocks) erstellen kannst. So kann Beispielsweise unabhängig entwickelt werden.
    Also könntest du z.b. ein Beispiel hier hochladen wo werte in die Klassen kommen als würde eine Maschine (Lasersystem) vorhanden sein.

    Schritt 2:
    Nun kommen "Beispielwerte" in deine Klassen mit welchen du ein ViewModel befüllen und das ganze Testen kannst.

    Schritt 3:
    Nun einfach den Mock gagen die echte Implementierung austauschen und schon läuft alles. Im Idealfall.

    Das Problem welches ich nun habe, ich sehe aus dem Text nicht welche Werte/Paramter wie wo in welche Klasse kommen können und wie das ganze aussehen soll/muss/kann.

    Vieleicht kannst du ja ein Minibeispiel schnell zusammenmachen (mit Testdaten) wo man sieht wie das am Ende funktionieren soll. Dann finden wir sicher eine Idee wie man das per MVVM abbilden kann.

    Das kann eine Anwendung sein, ne Skizze, ne Animation, ein Lastenheft oder sonst was. Ich (wir) müssen uns nur auskennen. Vieleicht liegt es an mir, aber ich verstehe an dem Text nicht Wo es hakt. Auch kein Code und kein Anhang.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hi.

    Sieht gut aus, besser als mein Spaghetti-Code...

    Also zum Verständnis, ist die Presentation der Daten hier dein Anliegen?

    Wenn ja, dann... Am besten mit Master-Detail-Ansichten arbeiten, um das einzelne LaserSystem optisch Aufzuwerten, und die Seriellen-Daten (und anderes) anzuzeigen.

    Das kann mit hilfe von DataTemplates in XAML gelöst werden, und kann für "Gruppen" von LaserSystemen gut aufbereitet werden.

    Ich liebe ListBoxen und von einer ListView habe ich schonmal gehört.

    Da hier Geräte in einer Auflistung sind, kann eine DatagridView genutzt werden, aber wir sind ja nicht mehr in den 2000er Jahren,
    und mit MVVM kann ein ListenEintrag (ListBoxItem oder ListViewItem) eine eigene Datatemplate besitzen und wunderbar umgesetzt werden.

    Ist ein wenig frickelig, wenn das noch nie gemacht wurde, aber ich statte die meisten Auflistungselemente mit eigener Ui-Logik aus.
    Zum Beispiel ein Button, der nur den Eintrag betrifft (update, delete, refresh etc.), oder das Anzeigen von unterschiedlichen StatusMeldungen und mehr...

    Das Beispiel von @Nofear23m mit den Mock-Daten und dem Interfacing, kann ich persönlich empfehlen, benutze es 'selbah' aber nie. (ECHT jetzt...?)

    Falls Du auf die Idee kommst, für jeden ListBoxItem eine eigene UserControl mit eigenem ViewModel zu erstellen, lass es... Es sei denn du Produzierts für die "Börse Echtzeit-Software"...

    Ich kann aus Erfahrung sprechen, das diese Vorgehensweise eher umständlich als produktiv ist.

    Ich stelle mir das Viewmodel immer als das "Programm" vor und das View als das "BlingBling",
    und das Model ist mittlerweile nur zum XML Import/Export und als "Komplexe Variablen Definition" verkommen.

    In deinem Fall ist das verwenden von Interfacen (Schnittstellen) fast eine Notwendigkeit,
    weil API-Grenzen "immah" ("Explizit mit AH am Ende") mit einem Interface versehen sein sollten.

    Somit ist dann eine UI mit MockUp-Daten (VerspottungAuf-Daten?) eigentlich die beste Wahl.

    Wenn der Lapidar von mir zusammgeklatschte Beitrag fragen aufwirft, dann einfach nachhaken.

    Ich hoffe das der Kontext mich hier nicht getäuscht hat.... ?(

    c.u. Joshi aus HH

    Beispiel Vorgehen

    Nofear23m schrieb:


    Schritt 1:
    Mach ein Interfaces für die Kommunikation und die Parameter damit du hierfür "Fakes" (Mocks) erstellen kannst. So kann Beispielsweise unabhängig entwickelt werden.
    Also könntest du z.b. ein Beispiel hier hochladen wo werte in die Klassen kommen als würde eine Maschine (Lasersystem) vorhanden sein.


    "Interfaces" ist ein gutes Stichwort und ich bin gerade dabei mich damit auseinander zu setzen!
    Sieht nach der Lösung aus, die ich suche.

    Nofear23m schrieb:


    Vieleicht kannst du ja ein Minibeispiel schnell zusammenmachen (mit Testdaten) wo man sieht wie das am Ende funktionieren soll. Dann finden wir sicher eine Idee wie man das per MVVM abbilden kann.


    Ja das kann ich verstehen. Ich habe hier versucht nochmals etwas zu veranschaulichen, wie sich das am Ende abspielen soll.
    Die schmalen Pfeile beschreiben die Beziehung, die dicken (mit Farbe ausgefüllt) die Beispielhandlung.
    In diesem Szenario wählt der Anwender, die Anlage2 aus.
    Wie hier zu sehen, habe ich zwar eine ViewModel diese kann aber mehrere verschiedene Anlagen besitzen.(Jenachdem was vorhanden)
    Die Anlage (LaserSystem) bekommt einen command zurück. Dieser hat einen Parameterobjekt und einen Wert (string) dieser stellt den ausgelesenen Wert dar.
    Die Anlage muss jetzt natürlich je nach Parameter den ausgelesenen Wert etwas anders bearbeiten.
    Ein Wert wird: *S1PF005000# sein, was 500 kHz (Frequenz) darstellen. Also muss die Zahl daraus extrahiert werden, aber auch um 10 geteilt werden.
    Ein anderer kann hingegen so aussehen: *S1WA000002# hier zählt nur die letzte Zahl und diese kann nur 0,1,2 und 3 sein.
    Diese Parameterspezifischen bearbeitung der Rückgaben, habe ich in den jeweiligen Parametern eingebaut. Z.B hat der Parameter beim 500kHz beispiel einen Faktor von 10
    Der Parameter beim zweiten aber einen Faktor von 1 und so weiter.



    Der Weg von den LaserAnlagen zur ViewModel ist mein Problem.
    Verstehe ich Interfaces richtig, dass ich eines z.B dazwischen hängen kann? (LaserSystem: Implements BeisSpielInterface und ViewModel: Implements BeispielInterface)

    Danke Leute für euere tatkräftige Hilfe und dass ihr Verständnis für mein Unwissenheit zeigt.

    Gruss

    EDIT:
    Ach ja, wie das command zur LaserAnlage gelangt weiss ich auch noch nicht. Da das Threading ja die ganze Zeit läuft und nicht eine einzelne Funktion ist, die etwas zurückgibt.
    Bilder
    • Beispiel.jpg

      86,27 kB, 830×722, 78 mal angesehen
    Hallo

    Wenn ich das richtig sehe gibt es zwei verschiedene Lasersystem welche unterschiedlich funktionieren. Soweit richtig?
    OK, dann bau dir mal Interfaces auf (wie ein Bauplan oder ein Rezept). Für jede Art von Lasersystem (solange es nicht mehr wie 2 oder 3 sind, sonst müsste man sehen das man das Generisch hinbekommt) ein Interfaces.
    Dieses beschreibt was ein Lasersystem kann/hat/macht.

    Wenn du Zugriff auf diese Lasersysteme hast kannst du die "echte" implementierung der Interfaces gleich machen um zu sehen ob die Interfaces so stimmig sind.
    Die Services sind auf jeden Fall eine eigene Klassenbibliothek (empfehle ich zumindest). Hier gibt es jetzt auch kein ViewModel oder sonst was. Einfach das Service welches sich um alles kümmert.
    Wenn ich das richtig verstehe kommt würd eich da allerdings auch den "RuntimeListener" reinpacken. Denn so wie ich das sehe ist der auch nur im Lasersystem drinnen.

    Gut, dieses Service kann nun völlig unabhängig operieren. Du kannst eine einfach Consoleanwendung schreiben in welcher du die Werte von einem Lasersystem ausgibst, über die Console aber auch Signale senden.
    Das Service kümmert sich im idealfal auch gleich darum die Werte in "lesbare" oder wenn man so will in "vernünftige" wert umzuwandeln welche auch gleich im richtigen Datentyp sind.
    Also das aus dem String "*S1PF005000#" ein Integer für die kHz wird. Andersrum wenn du was sendest soll sich das Service auch darum kümmern.

    Du kannst aber auch wenn du kein Lasersystem zur verfügung hast einen Mock implementieren. Der gibt dann gefakte werte zurück und Simuliert sohin ein Lasersystem. So kannst du Beispielsweise auch Fehlerhafte Werte Simulieren und kannst dann darauf reagieren. sowas mit einer "echten" Maschine zu erzeugen ist oft schwer. Wie einen Fehler herbeiführen der gerade nicht vorhanden ist?

    Wenn du das hast, ist 50% getan. Nun nur noch das ganze in einem ViewModel auslesen und verarbeiten. Der User klickt wo drauf und im ViewModel sagst du das nun der Wert xy an die Maschine z gesendet werden soll und alles weitere macht das Service. Je nachdem ob nun der Fake hereingereicht wurde oder die "echte" implementierung wird der neue Wert dann an die Maschine gesendet oder an einen Mock.

    Wie genau die das Service erstellst können wir dir hier wenig helfen. Wenn du allerdings die Interface richtig aufsetzt und einen Mock implementierst können wir wieder ins Spiel kommen um dir mit deinem ViewModel zu helfen.
    Du wirst aber sehen das sich ein ViewModel dann herrlich einfach und übersichtlich erstellen lässt da du den ganze Boilerplatecode im Service hast. du musst dich um das nicht mehr innerhalb des VM kümmern.

    Und da können wir dich dann unterstützen und dir Helfen deine Anwendung komplett MVVM tauglich zu machen, aber beim Service musst du leider selbst wissen was du brauchst. Wir kennen die Maschinen und deren Kommunikation nicht.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Ein weiteres Problem

    Nofear23m schrieb:

    Hallo

    Wenn ich das richtig sehe gibt es zwei verschiedene Lasersystem welche unterschiedlich funktionieren. Soweit richtig?


    Ja, richtig!


    Nofear23m schrieb:


    OK, dann bau dir mal Interfaces auf (wie ein Bauplan oder ein Rezept). Für jede Art von Lasersystem (solange es nicht mehr wie 2 oder 3 sind, sonst müsste man sehen das man das Generisch hinbekommt) ein Interfaces.
    Dieses beschreibt was ein Lasersystem kann/hat/macht.


    ich habe das bereits mit Klassen gelöst. Hierbei eine Hauptklasse quasi, von der die verschiedenen Lasersysteme erben.
    Ist das in irgend einer Weise negativ für mich, wenn ich es mit Klassen löse?

    Weiter habe ich mir tatsächlich auf die schnelle einen Mock aufgebaut und arbeite nun damit.



    Nofear23m schrieb:


    Wenn ich das richtig verstehe kommt würd eich da allerdings auch den "RuntimeListener" reinpacken. Denn so wie ich das sehe ist der auch nur im Lasersystem drinnen.


    Danke für diesen Hinweis, ich habes nun so gelöst, dass die zuvor im RuntimeListener verarbeiteten Funktionen, sich in den jeweiligen lasersystemen befinden. So scheint es auch viel sauberer zu laufen. :thumbsup:



    An und für sich, hat sich somit das eigentliche Problem gelöst. Falls du dir dennoch etwas Zeit für mich nehmen könntest, ich hätte da noch ein anderes Problem:

    Ich arbeite mit der MahApps.Metro (mahapps.com/)Erweiterung und dort kann man sehr interessante und schöne Interaktionsformen nutzen (mahapps.com/controls/dialogs.html)

    Hier der Aufbau:


    • View

      XML-Quellcode

      1. <Controls:Splitbutton SelectionChanged="SplitButton_SelectionChanged"/>


    • Ebenfalls View, aber .vb code

      VB.NET-Quellcode

      1. Private Sub SplitButton_SelectionChanged(sender As Object, e As SelectionChangedEventArgs)
      2. _viewModel.GiveAMessageBack("MeinTitel", "MeineNachricht")
      3. _viewModel.searchForSystems()
      4. End Sub


    • ViewModel

      VB.NET-Quellcode

      1. Public Async Sub GiveAMessageBack(ByVal Title As String, ByVal Text As String)
      2. laserSystems(mySystem).Hold()
      3. Await _dialogInstance.ShowMessageAsync(Me, Title, Text)
      4. laserSystems(mySystem).Start()
      5. End Sub
      6. ' Durchsucht alle Ports nach LaserSystemen
      7. Public Sub searchForSystems()
      8. visible = Visibility.Hidden
      9. laserSystems = tmpRuntimeInstance.getAllDevices()
      10. End Sub




    Hierbei entsteht ein Problem: Das UI wird eingefroren.
    Die Message wird erst nach dem beenden der searchForSystems angezeigt. Obwohl ich diese davor eigentlich aufrufe.
    Gibt es hierfür eine elegante Lösung?



    EDIT: 10:45

    Ich habe gerade diesen schönen und verständlichen Beitrag gelesen:
    Async, Await und Task

    Damit hat sich auch dieses Problem lösen lassen.
    Vielen Dank für deine tatkräftige Hilfe!

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Hutti“ () aus folgendem Grund: Habe im Forum zum zweiten Problemstellung bereits etwas gefunden

    Hutti schrieb:

    Ist das in irgend einer Weise negativ für mich, wenn ich es mit Klassen löse?

    Ne, das nicht. Ich weis zwar nicht wie du es ohne einem Interface machst das du einen Fake injezierst aber solange es klappt ok. Wenn du ein Interface erstellst hast du ja dann auch eine Klasse. Oder besser zwei Klassen. Eine für den Fake und eine "echte". Beide implementieren das Interface.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##