WPF Einstieg - Fragen BInding + MVVM

  • WPF

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

    WPF Einstieg - Fragen BInding + MVVM

    Hallo Leute,

    ich versuche mich in WPF + MVVM einzuarbeiten und bastle gerade ein simples Beispiel zusammen.
    Datengrundlage ist eine Datatable, diese wird eigentlich aus einer Datenbank befüllt, zum Testen für Euch habe ich das auf
    eine Methode geändert, die die Datatable mit Testdaten befüllt.

    Auf dem Fenster gibts 2 Listboxen, eine soll Kopf- die andere Detaildaten darstellen. Zum Testen binden beide aktuell nur ein Property.

    Ich habe versucht, die Bindung über DataContext herzustellen, das hat aber nicht geklappt. Mit ItemsSource klappt es, auch für die 2. Listbox, aber
    beim Ändern der Selektion der 1. Listbox ändert sich der Eintrag nicht automatisch mit.

    Zum Anderen ist das sicher noch keine richtige MVVM-Umsetzung - vielleicht kann mir jemand sagen was ich noch wie anders machen muss.
    An eigenen Beispielen lernt man ja am besten.

    Vielleicht hat ja jemand die Muse sich mein Gewurschtelt anzuschauen und mir Tipps zu geben 8o

    Zusammengefasst habe ich Anliegen:
    1. Datenbindung über DataContext wie das Sascha immer zeigt
    2. generelle Anmerkungen bzgl. WPF + MVVM (was mache ich falsch oder müsste anders umgesetzt werden)
    Im Anhang findet ihr das Projekt.
    Dateien
    • WpfApp1.7z

      (32,95 kB, 105 mal heruntergeladen, zuletzt: )
    Gruß Murdoc
    Hallo @Murdoc

    Ich sehe mir dein Beispiel gerne an und versuche schritt für schritt zu erklären was ich wie ändern würde. Anschießend stelle ich das Projekt wieder Online.
    Falls du dann noch weitere Frage hast kannst du diese ja dann gerne hier stellen. So, ich gehe jetzt mal was essen und dann mach ich mich an dein Projekt.

    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. ##

    Hallo @Murdoc

    OK, habe mir das Projekt angesehen und umgeändert. Da du gefragt hast ob das Projekt MVVM gerecht wird gebe ich hier meinen Senf auch gleich dazu ^^

    Projektstruktur
    Die Projektstruktur ist eher suboptimal. Bei MVVM sind die einzelnen Layer voneinander zu trennen. Man kann auch alles in einem Projekt haben, bekommt so aber nicht mit sollte man den Fehler begehen doch mal das View ins ViewModel zu holen. Sind die Layer getrennt kann man das erst gar nicht. Sieh dir hierzu die Projektmappenstruktur aus dem Beispiel von @Akanel an. Er hat dies bereits korrekt umgesetzt und das war glaube ich auch C#.

    Namenskonvention
    Bei ViewModel sollte man den Namen immer so wählen das man auch noch weis für was die Klasse ist wenn man vieleicht 100 Klassen oder mehr hat. In deinem Beispiel z.b. ArtikelViewModel und ArtikelListeViewModel ODER man geht an die kürzere schreibweise was ich gerne mache und schreibt ArtikelVm und ArtikelListeVm.

    Namespaces
    Erstelle dir Namespaces. Das habe ich jetzt zwar an deinem Beispiel nicht gemacht aber sollte man immer machen. Alle ViewModels sind dann zumindest in einem ViewModel-Namespace. Ich unterteile diese dann sogar noch in verschiedene Unter-Namespaces wie z.b. ViewModel.Artikel, ViewModel.Settings, ViewModel.Customer usw.

    ViewModel-Basisklasse
    Damit du nicht immer wieder INotifyPropertyChanged implementieren und die Hilfsmethode schreiben (oder Copy&Paste) musst lege eine Basisklasse an von welches alle ViewModelklasse erben.
    Das habe ich dir gleich so gemacht

    CallerMemberName
    Ein tolles - relativ - neues Feature ist CalleMemberName aus dem Namespace System.Runtime.CompilerService. Hierdurch vereinfacht sich der Aufruf von PropertieChanged ungemein und Tippfehler können ausgeschlossen werden.
    Du kannst also im Nachhinein auch Properties umbenennen ohne alle Strings per Hand ausbessern zu müssen.

    Deine ListKlasse (jetzt umbenannt in ArtikelListeViewModel)
    ModelSelect (heißt nun SelectedArtikel - da weis man immer WAS selektiert ist) war bei dir eine ObservableCollection(Of ArtikelViewModel). Warum?
    Du hast nur eine Dimension. Wenn ich viele Artikel im Regal habe und einen herausnehme habe ich EINEN Artikel in meiner Hand. Nicht mehrere.
    Insofern ist das View ja auch falsch. Warum im DetailView eine Listbox. Ich habe ja nur EINES in der Hand. Außer das war so gewollt das man mehrere Selektieren können soll und die sollen unten angezeigt werden. Aber das wäre dann kein DetailView.

    UI
    Versuche immer Dynamisch zu arbeiten. Ich habe deine View so weit ausgebessert das sich diese mit der Größe des Fensters und den Gegebenheiten mit verändert. Das sind eine der stärken der WPF. Versuche diese zu nutzen.
    Guck mal von meinem Tutorial nochmals Kapitel 2.1.1 und 2.1.1.1.

    Bitte verstehe das alles nicht als Kritik, ich will dir nur ein paar Tipps auf den Weg geben, je eher du die WPF so verwendest wie SIE das will bekommst du deine Erfolgserlebnisse die du die wünscht.

    Anbei nun auch das Projekt.

    Grüße
    Sascha
    Dateien
    • WpfApp1.zip

      (756,03 kB, 214 mal heruntergeladen, zuletzt: )
    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. ##

    Hallo @Nofear23m

    Vielen Dank das du dir mein Projekt angesehen hast und für deine Anmerkungen.

    Ich muss gestehen dass einige Dinge die du angesprochen hast, aufgrund von Bequemlichkeit da es sich um ein Testprojekt handelt,
    von mir nicht entsprechend umgesetzt worden (betrifft Namenskonvention, UI)

    Mit Projektstruktur meinst du die Zuordnung in Ordner oder? Das fällt dann unter den obigen Punkt - das hatte ich aus deinem Tut schon gelernt.

    Da hätte ich besser mal etwas mehr Mühe reinstecken sollen - sorry dafür.

    Ja - es ist ja möglich das mehrere Einträge gewählt werden könnten. Ich hab das halt als Detailview bezeichnet, da es zusätzliche Details zu dem/den gewählten Einträgen anzeigen soll. Vielleicht war meine Benamsung da ungeschickt.

    Was bringt mir die Aufteilung in Namespaces für Vorteile?

    Ich schau mir deine Änderungen gleich an.

    Nochmals vielen Dank!
    Gruß Murdoc
    Hallo

    OK, der Reihe nach.

    Murdoc schrieb:

    Mit Projektstruktur meinst du die Zuordnung in Ordner oder? Das fällt dann unter den obigen Punkt - das hatte ich aus deinem Tut schon gelernt.

    Nein, schau dir mal das verlinkte Projekt an. Nicht nur(!) dir Ordner, ich meine wirklich verschiedene Projekte!! Die Applkation als WPF Projekt, das Model als Klassenbibliothek, Das ViewModel als Klassenbibliothek und die View als "WPF Benutzersteuerelementbibliothek".

    Murdoc schrieb:

    Da hätte ich besser mal etwas mehr Mühe reinstecken sollen - sorry dafür.

    Musst dich nicht entschuldigen, wichtig ist das du es weist.

    Murdoc schrieb:

    Ja - es ist ja möglich das mehrere Einträge gewählt werden könnten. Ich hab das halt als Detailview bezeichnet, da es zusätzliche Details zu dem/den gewählten Einträgen anzeigen soll. Vielleicht war meine Benamsung da ungeschickt.

    Achso, dann ist das wieder eine andere Sachen.
    Problem hierbei. Die Listbox besitzt kein DependencyProperty SelectedItems (man beachte das s am schluss).

    Also muss man "Tricksen". Da wir nicht wissen welche Items selektiert sind - zumindest nicht von der Listbox selbst müssen wir auf ein ListboxItem gehen.
    Dieses besitzt ein Property IsSelected. Also fügen wir unserer ArtikelViewModel-Klasse eine Eigenschaft hinzu. Die Eigenschaft IsSelected und Binden mitteln Style-Setter daran.

    XML-Quellcode

    1. <Style TargetType="{x:Type ListBoxItem}">
    2. <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
    3. </Style>


    Im ViewModel bekommen wir jetzt nicht wirklich mit wenn sich das Propertie eines Artikel ändert. OK, man könnte nun Events reinmachen und diese von der ArtikelListViewModel-Klasse aus abonnieren, ist aber alles nicht schön.
    Es gibt ein schönes NuGet-Paket "Microsoft.Expression.Interactions". Dieses eingebunden und schon haben wir die Möglichkeit ein Event an einen Command "umzuleiten".

    XML-Quellcode

    1. <i:Interaction.Triggers>
    2. <i:EventTrigger EventName="SelectionChanged">
    3. <i:InvokeCommandAction Command="{Binding ArtikelListeSelectionChangedCommand}"/>
    4. </i:EventTrigger>
    5. </i:Interaction.Triggers>


    Klinkt jetzt kompliziert, ist es aber nicht. Aber es ist eben schon eine spezielle Konstellation was du da vorliegen hast.

    Murdoc schrieb:

    Was bringt mir die Aufteilung in Namespaces für Vorteile?

    Viele, Ordnung(!!) und das du die Übersicht behaltest. Ausserdem gehört es zum guten Stil. Hast du mal mehrere Dutzend ViewModels will ich sehen wie du noch mit der Intellisens zurechtkommst. Ich will die immer so sauber wie möglich halten.

    Ich hänge dir das aktuelisiert Projekt wieder an.

    Grüße
    Sascha
    Dateien
    • WpfApp1.zip

      (939,89 kB, 115 mal heruntergeladen, zuletzt: )
    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. ##

    Da muss ich doch grad mal eine Frage aus Interesse einwefen:

    Nofear23m schrieb:

    Die Applkation als WPF Projekt, das Model als Klassenbibliothek, Das ViewModel als Klassenbibliothek und die View als "WPF Benutzersteuerelementbibliothek".
    Warum ist es so wichtig das alles in verschiedene Bibliotheken zu packen, für ein und dieselbe Anwendung? Hat man daraus irgendwelche Vorteile, oder dient es nur der strikten Trennung?
    Hallo @EaranMaleasi

    EaranMaleasi schrieb:

    Warum ist es so wichtig das alles in verschiedene Bibliotheken zu packen, für ein und dieselbe Anwendung? Hat man daraus irgendwelche Vorteile, oder dient es nur der strikten Trennung?

    so kann man es sehen, es geht aber (später) ja noch weiter. Das MVVM Patterns erfordert es eben. Eines der Vorteile von MVVM ist die Testbarkeit. Das erreiche ich nur durch die Trennung der Layer.
    In weiterer Folge sind die einzelenen Layer später austauschbar(er). In einer "echten" Applikation hat man evtl. sogar noch einen DataAccessLayer und eine Busineslogik zwischen Model und ViewModel.

    Auch wenn man dies alles nicht benötigt empfehle ich es immer gerne da Anfänger dann nicht erst in Versuchung kommen das Model in die View zu reichen oder sogar gar die View ins ViewModel. Da wäre nämlich fatal.

    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. ##

    Dassis offsichtlich Ansichtssache, weil ich bin anderer Ansicht :)
    Ich versuche prinzipiell, Architektur nicht wesentlich komplizierter zu machen, als wie man es absehbar benötigt.
    ich stelle da auch eine Verbindung zum Yagni-Prinzip her - kann man googeln, das fokussiert recht gut auf die Problematik.
    Ich weis das du anderer ansicht bist. Und wie würdest du UnitTests implementieren??
    Es verstößt gegen das Pattern.

    Ich habs schon gesagt, lass dir einen Namen für dein "Pattern" einfallen. 8-) MVVM besagt das die Layer getrennt werden müssen. Ist ja nicht auf meinem Mist gewachsen.
    Einfach halten - ja. Aber nicht wenn man im nachhinein probleme bekommt. Und gerade Anfänge sollten dies auch tun um nicht vieleicht unabsichtlich das Pattern zu verletzen. Ich weis wie das ist, das geht schnell und man bekommt es oft nicht mit. Wenn man einen Verweis auf ein Projekt hinzufügen muss denkt man darüber nach - warum brauche ich jetzt diesen Verweis....

    Und früher oder später fängt jeder Programmierer mit Unittests an.
    Aber das hatten wir schon sooo oft. Aber ich lasse mich gerne eines besseren belehren wenn du weist wie man es anders besser machen kann. Gerne

    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. ##

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