ViewModel im View nicht verfügbar!?

  • WPF

Es gibt 33 Antworten in diesem Thema. Der letzte Beitrag () ist von Akanel.

    ViewModel im View nicht verfügbar!?

    Hallo zusammen.

    Ich versuche mich gerade an einem Adressbuch in WPF, welches ich möglichst schon in MVVM umsetzen möchte um das ganze zu verstehen.
    Der Sinn eines Adressbuches sei dahingestellt, es dient mir allein zu Übungszwecken da ich dort alles mögliche ausprobieren kann.

    Nun ich habe mir folgende Struktur angelegt:


    Das Model sieht wie folgt aus:

    VB.NET-Quellcode

    1. Public Class Person
    2. Public Sub New()
    3. End Sub
    4. Public Sub New(vorname As String, nachname As String, strasse As String, plz As Integer, ort As String)
    5. Me.Vorname = vorname
    6. Me.Nachname = nachname
    7. Me.Strasse = strasse
    8. Me.PLZ = plz
    9. Me.Ort = ort
    10. End Sub
    11. Private _vorname As String
    12. Public Property Vorname() As String
    13. Get
    14. Return _vorname
    15. End Get
    16. Set(ByVal value As String)
    17. _vorname = value
    18. End Set
    19. End Property
    20. Private _nachname As String
    21. Public Property Nachname() As String
    22. Get
    23. Return _nachname
    24. End Get
    25. Set(ByVal value As String)
    26. _nachname = value
    27. End Set
    28. End Property
    29. Private _strasse As String
    30. Public Property Strasse() As String
    31. Get
    32. Return _strasse
    33. End Get
    34. Set(ByVal value As String)
    35. _strasse = value
    36. End Set
    37. End Property
    38. Private _plz As Integer
    39. Public Property PLZ() As Integer
    40. Get
    41. Return _plz
    42. End Get
    43. Set(ByVal value As Integer)
    44. _plz = value
    45. End Set
    46. End Property
    47. Private _ort As String
    48. Public Property Ort() As String
    49. Get
    50. Return _ort
    51. End Get
    52. Set(ByVal value As String)
    53. _ort = value
    54. End Set
    55. End Property
    56. Public Overrides Function ToString() As String
    57. Return $"{Vorname} , {Nachname}"
    58. End Function
    59. End Class


    Das ViewModel:

    VB.NET-Quellcode

    1. Imports System.Collections.ObjectModel
    2. Imports Model
    3. Public Class MainViewVm
    4. Public Sub New()
    5. AllePersonen = New ObservableCollection(Of Person) From {New Person With {.Vorname = "Max", .Nachname = "Mustermann", .Strasse = "Musterweg 21", .PLZ = 12345, .Ort = "Musterdorf"},
    6. {New Person With {.Vorname = "Maxi", .Nachname = "Musterfrau", .Strasse = "Musterstrasse 21", .PLZ = 12354, .Ort = "Musterstadt"}}}
    7. End Sub
    8. Private _allepersonen As ObservableCollection(Of Person)
    9. Public Property AllePersonen() As ObservableCollection(Of Person)
    10. Get
    11. Return _allepersonen
    12. End Get
    13. Set(ByVal value As ObservableCollection(Of Person))
    14. _allepersonen = value
    15. End Set
    16. End Property
    17. End Class


    Nun kann ich aber das ViewModel nicht im View ansprechen, also Intellisense bietet es mir nicht an wenn ich eine Listbox an AllePersonen binden möchte.
    Somit kann ich später dann auch nicht auf die einzelnen Propertys der Personen zugreifen.
    Habe ich etwas vergessen, oder ist mein ViewModel falsch. Fehlen da noch Propertys?

    Zusätzlich hänge ich mal das Projekt mit an.
    Dateien
    • Adressbuch00.zip

      (35,97 kB, 98 mal heruntergeladen, zuletzt: )
    Rechtschreibfehler betonen den künstlerischen Charakter des Autors.
    Ein ViewModel muss INotifyPropertyChanged implementieren. Das solltest Du noch über eine ViewModel-Basisklasse machen und davon erben. Und dann musst Du eben Deinen DataContext auf das ViewModel setzen. Sonst kennt die View das ViewModel nicht.

    Grüße
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:
    Ich nehme mal an du meinst folgendes

    XML-Quellcode

    1. <UserControl.DataContext>
    2. <vm:MainViewVm/>
    3. </UserControl.DataContext>


    hat aber leider nichts gebracht.

    Das komplette UserControl:

    XML-Quellcode

    1. ​<UserControl x:Class="uclMainView"
    2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    4. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    5. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    6. xmlns:local="clr-namespace:View"
    7. xmlns:vm="clr-namespace:ViewModel;assembly=ViewModel"
    8. mc:Ignorable="d" d:DataContext="{d:DesignInstance IsDesignTimeCreatable=True , Type={x:Type vm:MainViewVm}}"
    9. d:DesignHeight="450" d:DesignWidth="800">
    10. <UserControl.DataContext>
    11. <vm:MainViewVm/>
    12. </UserControl.DataContext>
    13. <UserControl.Resources>
    14. <Style TargetType="{x:Type ListBoxItem}">
    15. <Setter Property="BorderBrush" Value="AntiqueWhite"/>
    16. <Setter Property="BorderThickness" Value="1"/>
    17. </Style>
    18. </UserControl.Resources>
    19. <ListBox ItemsSource="{Binding AllePersonen}">
    20. <ListBox.ItemTemplate>
    21. <DataTemplate>
    22. <Grid>
    23. <Grid.RowDefinitions>
    24. <RowDefinition Height="Auto"/>
    25. <RowDefinition Height="Auto"/>
    26. </Grid.RowDefinitions>
    27. <TextBlock VerticalAlignment="Center" FontWeight="Bold" FontSize="14" Grid.Row="0">
    28. <Run Text="Max"/><!--<Run Text="{Binding AllePersonen.Vorname}"/>-->
    29. <Run Text=" , "/>
    30. <Run Text="Mustermann"/><!--<Run Text="{Binding AllePersonen.Nachname}"/>-->
    31. </TextBlock>
    32. <TextBlock VerticalAlignment="Center" Grid.Row="1">
    33. <Run Text="Musterweg 21"/>
    34. <Run Text=", "/>
    35. <Run Text="12345"/>
    36. <Run Text=", "/>
    37. <Run Text="Musterdorf"/>
    38. </TextBlock>
    39. </Grid>
    40. </DataTemplate>
    41. </ListBox.ItemTemplate>
    42. </ListBox>
    43. </UserControl>
    Rechtschreibfehler betonen den künstlerischen Charakter des Autors.
    ja genau. Und das kompiliert nicht? Oder ist das eher einfach ein IntelliSense-Problem? Was ja bei WPF nicht unüblich wär. ^^

    Grüße
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:
    Es kompiliert schon, es funktioniert ja auch. Nur Intellisense bietet es mir nicht an.
    In @Nofear23m Tutorials ging das auch ohne den DataContext anzugeben. Der Namespace ist angegeben und der Verweis gesetzt.
    Deswegen dachte ich das ich etwas falsch gemacht habe.
    Im ersten Post ist ja das Projekt angehangen. Könntest es dir ja mal ansehen.
    Rechtschreibfehler betonen den künstlerischen Charakter des Autors.
    Auf IntelliSense gebe ich da eh immer nie was, weil die meistens da bei mir auch nicht funktioniert. Wenn es also klappt, passt ja alles. ^^
    Nur Dein PropertyChanged muss halt noch rein, sonst kriegt die View nichts von Änderungen mit.

    Grüße
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:
    Hallo

    Packe deine ViewModel Klasse in einen eigenen Namespace und importiere diesen in deinem View. Dann klappt es mit dem DesignTime-Support.

    INotifyPropertieChanged benötigst du zwar, ist aber für den die IntelliSense zur Desigtzeit irrelevant.

    Wenns nicht klappt kann ich die gerne noch weiter Helfen.

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

    Akanel schrieb:

    Nun kann ich aber das ViewModel nicht im View ansprechen, also Intellisense bietet es mir nicht an wenn ich eine Listbox an AllePersonen binden möchte.
    Tja - wie's aussieht, funzt das mit dem d:DataContext nur, wenn das MainViewmodel im selben Projekt angelegt ist wie das View.

    Ich hab also dein MainViewmodel in den View gezogen, und dann im Xaml den xmlns:vm2 erfunden:

    XML-Quellcode

    1. ...
    2. xmlns:vm="clr-namespace:ViewModel;assembly=ViewModel"
    3. xmlns:vm2="clr-namespace:View"
    4. mc:Ignorable="d" d:DataContext="{d:DesignInstance IsDesignTimeCreatable=True , Type={x:Type vm2:MainViewVm}}"
    5. d:DesignHeight="450" d:DesignWidth="800">
    6. ...
    7. <ListBox ItemsSource="{Binding AllePersonen}">
    8. ...
    Dann gehts.

    ErfinderDesRades schrieb:

    Tja - wie's aussieht, funzt das mit dem d:DataContext nur, wenn das MainViewmodel im selben Projekt angelegt ist wie das View.

    Ne, das auf keinen Fall. Bei korrekter Verwendung von MVVM sind es ja auch definitiv verschiedene Projekte.

    Was nur wirklich auch der Fall ist ist folgendes:

    Habe ich in meinem ViewModel eine Eigenschaft z.b. PersonenListe as ObservableCollection(of Person) und Person ist im Model definiert benötigt die View auch einen Verweis auf das Model!
    Ist dies nicht der Fall fehlt die Intellisense durch Entwurfszeit. Es funzt dann auch zur Laufzeit weil die dll des Models durch die Abhängigkeit vom ViewModel mit in das Ausgabeverzeichnis kopiert wird und somit zur Verfügung steht.
    Zur Designzeit ist dies jedoch nicht der Fall.

    Das ist auch mitunter der Grund warum man sein VM so weit Designen sollte das man hier nicht Klassen des Models nach aussen reicht sondern hier in diesem Fall ein PersonVm erstellen würde. Denn das Model implementiert ja auch kein INotifyPropertyChanged.

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

    Ich werde mich gleich Mal dransetzen und eure Vorschläge umsetzen.
    Edit:
    @Nofear23m
    Um das richtig zu verstehen:
    Ich habe ja die Person im Model definiert. Das bedeutet ja das ich dann einen Verweis vom Model auf die View setzen müsste. Das wäre dann aber nicht MVVM konform wenn ich das richtig verstanden habe. Ist das Korrekt?

    Nofear23m schrieb:

    Das ist auch mitunter der Grund warum man sein VM so weit Designen sollte das man hier nicht Klassen des Models nach aussen reicht sondern hier in diesem Fall ein PersonVm erstellen würde. Denn das Model implementiert ja auch kein INotifyPropertyChanged.


    Wie muss ich das nun verstehen? Das wird ja geau das sein was ich umsetzen muss/sollte.
    Bedeutet ich lege mir noch ein PersonVm an mit den Propertys von Person? Da fehlt mir gerade das Verständnis. Könntest Du mir da auf die Sprünge helfen?
    Dateien
    • Adressbuch01.zip

      (37,28 kB, 92 mal heruntergeladen, zuletzt: )
    Rechtschreibfehler betonen den künstlerischen Charakter des Autors.

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

    Akanel schrieb:

    Das bedeutet ja das ich dann einen Verweis vom Model auf die View setzen müsste

    Achtung, genau anders herum. Die View benötigt einen Verweis auf das Model.

    Akanel schrieb:

    Das wäre dann aber nicht MVVM konform wenn ich das richtig verstanden habe. Ist das Korrekt?

    In meinen Augen nicht. Ausserdem müsstest du dann im Model auf jeden Fall INotifyPropertieChanged implementieren. Sonst nbekommt die View keine änderungen mit.

    Akanel schrieb:

    Wie muss ich das nun verstehen? Das wird ja geau das sein was ich umsetzen muss/sollte.


    Mein Lösungsvorschlag wäre wie folgt:

    Das Model (Hier benötigt es gar keine Properties mit Getter und Setter):

    VB.NET-Quellcode

    1. Public Class Person
    2. Public Property Vorname As String
    3. Public PRopery Nachname as string
    4. ....
    5. End Class


    Das ViewModel (PersonVm):

    VB.NET-Quellcode

    1. Public Class PersonVm
    2. Private _personModel as Model.Person
    3. Public sub New()
    4. _personModel = new Model.Person
    5. End Sub
    6. Friend Sub New(pers as Model.Person)
    7. 'Der Konstrukltor ist Friend damit das Model nicht nach aussen gereicht wird
    8. _personModel = pers
    9. End Sub
    10. Public PRoperty Vorname as String
    11. Get
    12. Return _personModel.Vorname
    13. End Get
    14. Set
    15. _personModel.Vorname = value
    16. RaiseEvent OnPropertyChanged..........
    17. End Set
    18. End Property
    19. End Class


    So könnte z.b. dann ein ViewModel aussehen welches eine Liste von Personen hält:

    VB.NET-Quellcode

    1. Public Class PersonListVm
    2. Public Sub New()
    3. Dim AllPersons as List(Of Model.Person) = LoadPersons 'Irgendeine lademethode die X Personen läd
    4. Persons = new ObservableCollection(Of PersonVm)
    5. For each p in AllPersons
    6. Persons.Add(new PersonVm(p)
    7. Next
    8. End Sub
    9. Private _persons as ObservableCollection(Of PersonVm)
    10. Public Property Persons as ObservableCollection(Of PersonVm)
    11. Get
    12. Return _persons
    13. End Get
    14. Set
    15. _persons = value
    16. RaiseEvent OnPropertyChanged..........
    17. End Set
    18. End Property
    19. End Class



    Wie man gut sehen kann habe ich im PersonVm auch das Model allerdings als private Variable und gebe im Getter eines Properties lediglich den Wert zurück bzw. setze diesem im setter.
    Das hat zum einen den Vorteil das ich kein Model-Objekt ins View reiche und zum anderen das ich hier auch flexibel bin was die Daten angeht. Beispielsweise kann ich hier im Setter den neuen Wert prüfen und evtl. sogar aufbereiten damit immer korrekte Werte ins Model gelangen.

    Hoffe das war verständlich.

    PS: Bitte kleine Syntaxfehler zu verzeihen, habe das hier nur im Editor runtergetippt.

    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

    Brauchst du noch Hilfe oder ist das erledigt?

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

    Sorry das ich hier noch nichts geschrieben habe. Ich probiere noch Rum, komme aber immer wieder an ein Problem. Ich Versuche mich später, nach der Arbeit, noch mit Details zu melden.
    Danke das du nachfragst.
    Rechtschreibfehler betonen den künstlerischen Charakter des Autors.
    @Nofear23m
    Hat es einen bestimmten Grund warum du noch mal eine

    VB.NET-Quellcode

    1. AllPersons as List(Of Model.Person)
    machst?

    Oder kann ich auch direkt über die

    VB.NET-Quellcode

    1. ObservableCollection(Of PersonVm)
    initialisieren?

    Es geht um den Code:

    VB.NET-Quellcode

    1. Public Class PersonListVm
    2. Public Sub New()
    3. Dim AllPersons as List(Of Model.Person) = LoadPersons 'Irgendeine lademethode die X Personen läd
    4. Persons = new ObservableCollection(Of PersonVm)
    5. For each p in AllPersons
    6. Persons.Add(new PersonVm(p)
    7. Next
    8. End Sub
    9. Private _persons as ObservableCollection(Of PersonVm)
    10. Public Property Persons as ObservableCollection(Of PersonVm)
    11. Get
    12. Return _persons
    13. End Get
    14. Set
    15. _persons = value
    16. RaiseEvent OnPropertyChanged..........
    17. End Set
    18. End Property
    19. End Class

    Gruß Murdoc

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

    Hallo @Murdoc

    Äpfel und Birnen!
    Wenn du genau hinsiehst ist die List(Of T) mit instanzen den Model-Typs befüllt (Person)
    Die Observable(Of T) wurde allerdings mit instanzen des ViewModel-Typs befüllt (PersonVM)

    Kann man aber leicht übersehen.

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

    Aber das haben wir doch in Zeile 8 mit folgendem: Persons.Add(new PersonVm(p))

    Würdest du jetzt noch ein ViewModel drumrum machen so ala PersonListVm könntest du dieser in einem Konstruktor natürlich auch die List(Of T) mitgeben und innerhalb des Konstruktors die Befüllung machen. Aber WO der Code für das befüllen steht ist im Grunde ja irrelevant.

    Oder meinst du was anderes?

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