C# WPF - Ein oder mehrere Viewmodel in diesem Fall?

Departet

Lieutenant
Registriert
Sep. 2008
Beiträge
717
Moin ihr schlauen Leute,

ich brauche einmal Hilfe bei der Aufteilung von einem Viewmodel.
Ich habe ein Inventar mit etlichen Viewmodels (Geräte, Modelle, Typen, Benutzer, Standorte, Lieferanten, etc.). Dadurch, dass ich viele verschiedene Viewmodel habe, werden die Modelle untereinander nicht durch das INotify Interface über Änderungen informiert. Einzig innerhalb eines Modells kann dies verwendet werden.
Macht es hier Sinn ein allgemeines InventarViewmodel zu erstellen? Aktuell muss ich bei Änderungen in einem Viewmodel alle anderen neu laden um den aktuellen Stand zu haben.
Ich habe das Gefühl, unnötig viele Viewmodel erstellt zu haben.

Mfg,
Departet
 
Das ViewModel ist dazu da der View zu sagen, was sie mit den Models tun soll. Ich frag mich gerade, ob du Begriffe durcheinander wirfst oder ViewModel bisher nicht ganz verstanden hast (no offense). Zumindest ergibt es für mich als Außenstehender, der deinen Code nicht kennt, keinen Sinn, warum ein Gerät ein ViewModel haben sollte, wenn es keine Geräte-View gibt, die Sachen davon anzeigt.

Aus dem Stehgreif würde ich sagen Geräte, Modelle, Typen, Benutzer usw. sind alles Models mit ihren Properties und INotifyPropertyChanged. Dazu gesellen sich für deine Oberfläche ein oder mehrere ViewModels (jenachdem wie viele UserControls du benutzt), die dann entsprechend mit den Models jonglieren und Datenstände aktualisieren usw. (z. B. eine DetailView.xaml mit DetailViewModel.cs für Standorte mit weiteren Standortdetails wie Koordinaten, Name etc.)
Ohne da aber die Abhängigkeiten zu kennen kann man dir nicht weiterhelfen, wir wissen ja nicht was du eigentlich beabsichtigst geschweige denn wie dein Programm aussieht.
 
  • Gefällt mir
Reaktionen: Departet und SomeDifferent
Keine Sorge, ich würde dir gerne selber beantworten ob ich es komplett begriffen habe haha
Aktuell sieht es wie folgt aus:
1641807212352.png
1641807305207.png

Die verschiedenen Tabs sind in etwas alle gleich aufgebaut. Wenn ich jetzt ein Modell (im ModelleVM) erstelle muss das GeräteVM das natürlich wissen, deshalb aktualisiere ich zur Zeit alle ViewModel. Jedes ViewModel hat verschiedene Funktionen/Methoden und eine oder mehrere Views.
Die Idee ist jetzt ob ich alles zusammenfasse um ein "InventarVM" zu erstellen. Dann muss ich nach dem Ändern von Daten keine anderen VM mehr neu laden weil alles durch das INotifyPropertyChanged informiert wird.
 
Departet schrieb:
Wenn ich jetzt ein Modell (im ModelleVM) erstelle muss das GeräteVM das natürlich wissen
Muss es eigentlich nicht. Es muss nur wissen, wo es die aktuell verfügbaren Modelle herbekommt. Ich denke hier ist strukturell lediglich der falsche Ansatz dahinter.
An sich kannst du es mit deiner aktuellen Struktur nicht anders machen, daher "alles richtig gemacht", nur ist die Code-Effizienz halt blöd, wie du schon selber gemerkt hast.

Ab hier hast du eigentlich mehrere mögliche Wege.
  1. Du baust dir einen Helperservice, der deine aktuell verfügbaren Sachen enthält. Also sowas wie "AvailableModels", "AvailableHosts" usw. Wenn du dann ein Gerät neu anlegst fügst du es hinzu (z. B. Helper.AddModel(Model model)) und nutzt dann später bei der Modellauswahl sowas wie "Helper.AvailableModels" als Liste für die Auswahl. Damit entkoppelst du die VMs von deiner Datenhaltung, was ganz wichtig ist, gerade bei größeren Projekten! Die sollen ja nicht die (View-unabhängige) Datenhaltung machen sondern lediglich die Schnittstelle für deren Bearbeitung und Anzeige sein.
  2. Du packst nicht jeden Tab in ein eigenes ViewModel sondern nimmst ein großes für alles. Das beinhaltet dann Properties, wie "Devices", "Users" usw. Im Gerätetab gibst du der Tabelle dann einfach sowas mit wie ItemsSource = {Binding Devices} und dann werden da halt nur die Geräte angezeigt. Fügst du per Butten ein neues Modell hinzu hast du automatisch Zugriff im Gerätetab, weil alle den gleichen DataContext haben, nämlich das große MainViewModel.
Punkt 2 hat natürlich den Nachteil, dass das eine, große ViewModel irgendwann ein Moloch wird, je mehr Tabs dazu kommen. Hier muss man etwas abwägen was sich anbietet und wo es mit dem Programm ggf. noch hingeht. Beide Wege sind okay.

Ich hoffe ich hab dir ein paar Denkanstöße geben können wie du weitermachen kannst :) Wenn du noch Fragen hast gerne her damit.
 
  • Gefällt mir
Reaktionen: Departet und KitKat::new()
Noch ein Tip :
Benutze anstatt einer List<T> eine ObservableCollecition<T> auf die du an allen Stellen bindest. Diese implementiert das ICollectionChanged-Event welches automatisch Bindings bzw. die UI aktualisiert.

Ich persönlich würde mir eine Container-Klasse erstellen, die sämtliche Datenbestände beinhaltet. Bestenfalls als Schnittstelle um diese austauschbar zu halten. Den Container injizierst du in jedes ViewModel wo du Daten abgreifen musst.

C#:
using System.Collections.ObjectModel;

public class Container{
    public ObservableCollection<Lieferant> Lieferanten = new ObservableCollection<Lieferant>(); 
    public ObservableCollection<Modell> Modelle = new ObservableCollection<Modell>();
    //etc...
  
}
Wenn du einer Collection Elemente hinzufügst oder entfernst, updaten sich die Datenbindungen dann automatisch.

Das sollte dir schon ein großes Stück weiterhelfen.

Edit :

Ansonsten versuche Gemeinsamkeiten zu finden und diese zu abstrahieren. Hast du häufig eine Liste mit zwei Buttons "hinzufügen" und "entfernen" Musst du nicht zwangsläufig für jedes Model eine eigenes ViewModel entwickeln. Du kannst auch ein EditModelViewModel entwerfen, in das du jedes Mögliche Objekt packen und dessen Eigenschaften bearbeiten kannst.
 
  • Gefällt mir
Reaktionen: Nero1
Vielen Dank für die Hilfe!
Punkt 1 ist aktuell für meinen Wissensstand die schwerere Lösung. Punkt 2 würde sich da eher anbieten. Es soll nach jetzigem Stand auch keine weiteren Tabs geben. Lediglich weitere Funktionen wie ein Export für die Inventur (was dann aber auch im ViewModel landen würde) bzw. als Command Daten vom VM abgreift.

EDIT: @SomeDifferent das würde dann aber ja trotzdem nur in dem VM Änderungen bekanntgeben und nicht die Daten eines anderen VM aktualisieren oder nicht? Wenn ich in einem VM händisch z.B. ein Modell hinzufüge und mit dem EF in der DB speicher dann sehe ich die Änderungen ja nicht in dem Geräte VM bis ich hier die Daten neu lade.
 
Doch, an der Stelle musst du glaube ich etwas umdenken. Ich arbeite nicht mit dem EntityFramework, aber sofern ich mich erinnere gibt es dort schon eine Art Container-Klasse. Dort heißt sie aber meine ich Context. In diesem Context gibt es mehrere DbSets quasi für jede Business-Klasse eine.

Jetzt darfst du nicht hingehen und dir in deinem ViewModel eine neue Liste erzeugen und dort alle Items rein packen, sondern du gibst deinem ViewModel dieses Context-Objekt mit.

C#:
  public class MainViewModel
    {
        
        private MyDbContext _Context = new MyDbContext();

    }


    public class LieferantenViewModel
    {
        public MyDbContext Context { get; }

        public LieferantenViewModel(MyDbContext context)
        {
            Context = context;
        }

    }

    public class HerstellerViewModel
    {
        public MyDbContext Context { get; }

        public HerstellerViewModel(MyDbContext context)
        {
            Context = context;
        }
    }


Dann kannst du mit deinem Binding über den Context direkt auf die passende Liste binden.

XML:
<ListView ItemsSource="{Binding Context.Hersteller.Local}" />
<ListView ItemsSource="{Binding Context.Lieferanten.Local}" />

Die Local-Eigenschaft von DbSet ist schon eine Observable-Collection die die von mir beschriebene Funktionalität bereitstellen sollte.
 
  • Gefällt mir
Reaktionen: Departet
Hatte ein ähnliches Problem wie Du und hab das per Event Aggregation gelöst, damit ViewModels, die Änderungsinfos benötigen, was von den Änderungen mitbekommen.

Code ist hier falls Du Dich bedienen möchtest:
https://github.com/Drexel2k/VidUp/tree/master/VidUp.UI/EventAggregation
https://github.com/Drexel2k/VidUp/blob/master/VidUp.UI/EventAggregation/EventAggregator.cs

Hatte den Code aus einem YouTube Video nachgebaut, weiss aber nicht welches das war, gibt mit Sicherheit auch Frameworks dafür...
Ich würde auf gar keinen Fall ein RiesenViewModell für alles machen, das wird irgendwann ein unwartbares Monster.

Bei Tab Wechsel kann es sein, dass die Controls eh refresht werden, ist zumindest bei mir der Fall.
 
  • Gefällt mir
Reaktionen: Departet
Oh shit @SomeDifferent da hast du garantiert Recht. Ich erstelle tatsächlich aktuell mit jedem VM einen neuen Context. Sobald ich den selben Context überall verwende sollte das wirklich klappen. Werde ich so schnell wie möglich testen, kann aber etwas dauern, da überall refresh Methoden, auf Grund der aktuellen Lösung, eingebaut sind.
Vielen Dank nochmal an Alle. Ich werde berichten!
 
  • Gefällt mir
Reaktionen: SomeDifferent
Update und eine kleine Frage:
1641900142920.png

Beim drücken des Add Button wird ein neues Standort Objekt (EDV+rand(1,11)) erstellt und anschließend im MitarbeiterVM ein neues Objekt für jedes Standort Objekt im gemeinsamen Context Objekt erstellt. (hui so viele Objekte).
Zu der Frage - wenn ich nach dem Erstellen der ganzen Objekte kein SaveChanges ausführe dann wird in der Mitarbeiter View zwar die richtige Menge an Objekten angezeigt aber bei den neu erstellten Standorten wird kein Name angezeigt. Weiß jemand wieso? Im lokalen Speicher sollte doch alles sein?
Wenn ich jedoch ein SaveChanges am Ende anhänge klappt alles einwandfrei. Ist auch kein Problem für mein Inventar, da man seine Änderungen im Tab natürlich speichern oder verwerfen soll.

1641900462480.png
 

Ähnliche Themen

  • Geschlossen
2 3 4
Antworten
69
Aufrufe
14.009
Zurück
Oben