ObservableCollection mit Properties in MVVM

  • WPF

Es gibt 14 Antworten in diesem Thema. Der letzte Beitrag () ist von Sandro2020.

    ObservableCollection mit Properties in MVVM

    Moin,

    ich habe ein Problem, dass wenn ich die Property meiner WrapperClasse ändere, sie in meiner ListView nicht aktualisiert wird. Hier die erklärung/ aufbau

    das sind meine Klassen:

    public class CardViewModel : ViewModelBase
    public class CollectionViewModel : ObservableCollection<CardCollectionViewModel>
    public class CardCollectionViewModel : ObservableCollection<CardViewModel> (um diese geht es)

    C#-Quellcode

    1. [/b]public string theme
    2. {
    3. get
    4. {
    5. return CardCollection.theme;
    6. }
    7. set
    8. {
    9. CardCollection.theme = value;
    10. }
    11. }


    Wenn ich hier in meiner View die Property ändere, wird sie nicht aktualisiert aber in dem Object ist diese aktualisiert. Wie kriege ich es hin, dass ich hier eine OnPropertyChanged auslöse, welches dann die Gebindete View aktualisiert. Die Klasse ist im Anhang.

    Danke schonmal :D

    Gruß Sandro
    Dateien

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

    Hallo

    Das Problem ist simpler als du denkst. Dazu musst du wissen wie Binding in der WPF überhaupt funktioniert.
    Ich kann hierzu nur meiner Tutorialreihe (siehe Signatur empfehlen) Schaut dir das Kapitel über Binding einfach mal durch.

    Kurz und knapp. Diese ViewModel Klasse muss die Schnittsteller INotifyPropertyChanged implementieren. Oder du machst dir eine Basisklasse welche diese Implementiert und deine ViewModel Klassen erben dann von dieser. Das wäre der "sauberste" Weg.

    Um die WPF (die Oberfläche) dann davon in kenntnis zu setzen das sich der Wert einer Eigenschaft nun geändert hat musst du im Setter der Eigenschaft das Event RaisePropertyChanged werfen, welchem du den Namen des Properties übergibst. In der Regel hat jede Eigenschaft einer ViewModel Klasse im Setter genau diesen Code.

    Schau dir am besten das Video dazu an. Solltest du noch Fragen haben kannst du dich gerne nochmals melden.

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

    Das Problem ist, dass die Klasse schon ObservableCollection Klasse ist die die aber zusätzlich noch properties hat. Die Properties von <CardViewModel> Klasse sind alle mit einem PropertyChangeEvent ausgestattet und das funktioniert. Das hinzufügen und entfernen von elementen klappt auch nur das ändern der eigenen Properties nicht.
    Hallo

    Du hast im ersten Post ein Property gepostet welches kein OnNotifyPropertyChanged wirft. Auch deine Datei haben ich mir angesehen, und auch hier sind einige Properties drinnen welche kein INotifyPropertyChanged werfen.
    Allgemein sieht mir das ganze eher umständlich aus, ich denke das kann man vereinfachen - vorallem denke ich das deine Klasse nicht von ObservableCollection<CardViewModel> erben müsste, aber ich kenne dein Projekt nicht. Fakt ist das wenn eine änderung in der View nicht nachgeführt wird liegt es daran das die View eben nicht über diese änderung informiert wird (solange das BindingMode im XAML nicht auf "OneWayToSource" oder "Manual" festgelegt ist.

    Ansonsten mach mal ein kleines Minibeispiel welches dieses Reproduziert und Poste das Projekt mal. (ohne Bin Ordner)

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

    Moin ,

    danke schonmal für die bemühungen. Ich glaube nicht das ich daraus ein kleineres Projekt machen kann. Meine Hauptfrage sollte eigentlich sein, wie schmeiße ich das PropertyChange event bei einer Klasse die von der Standard Klasse Observable Collection Erbt. In dem Projekt geht es um eine Lernkarten app wo ich dann eine Sammlung von KartenSammlungen habe in denen Karten sind. Durch einen ViewModel Locator übergebe ich meinen ViewModels diese Sammlung . In der kann ich neue erstellen oder Löschen. Bei den Karten kann ich diese auch ändern und die View übernimmt das. Nur wie du schon gemerkt hast, rufe ich das ProperyChange Event nicht bei den Properties der observablecollection Klasse auf. Wie kann ich dies machen. Und eine INotifyPropertyChanged Basis Klasse habe ich auch die die VM alle erben aber nicht diese Wrapper Klassen. Ich hoffe das kann dir mein Problem besser beschreiben.
    Ok, und diese besagten Wrapper Klassen welche NICHT von der Basisklasse erben, hast du auf diese Zugriff? Sind die von dir oder von wo anders?

    Es geht ja um ein Property aus einer dieser Wrapperklassen welches kein PropertyChanged wirft richtig? Also entweder INotifyPropertyChanged implementieren und das Event werfen oder eben eine ViewModel Klasse drumrum erstellen.

    Zeig doch mal die Klasse und ein Property welches NICHT Nachgeführt wird. Das im ersten Post? Dort ist es klar wenn du kein Event wirfst.

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

    Ja genau das sin die aus dem ersten Post. Hier sonst mal die drei Wrapper Klassen oben im Post erneuert. Die Model Klassen sind einfache klassen

    C#-Quellcode

    1. public class Card
    2. {
    3. public string Name { get; set; }
    4. public string Content { get; set; }
    5. public string Solution { get; set; }
    6. public int Number { get; set; }
    7. public bool isActive { get; set; }
    8. // public List<bool> successRate = new List<bool>();
    9. }


    C#-Quellcode

    1. ​public class CardCollection : ObservableCollection<Card>
    2. {
    3. //Attribute
    4. public DateTime deadline;
    5. public String theme;
    6. public double successGoal = 0.7; //Die soll % Zahl von der Collection
    7. public double successRateAll; //Wie viel du der Collection schon richtig hast
    8. }


    C#-Quellcode

    1. public class Collections : ObservableCollection<CardCollection>
    2. {
    3. }
    Hallo

    Nochmal in ruhe.

    ALLE Properties welche im View per Binding aktualisiert werden sollen MÜSSEN OnPropertyChanged() im Setter haben.
    In deiner angehängten CardViewModel Klasse ist das überall auskommentiert und im CardCollectionViewModel haben Properties wie theme oder successGoal auch nichts der gleichen im Setter.

    Im ersten Post sprichst du das theme Property an welches NICHT im View aktualisiert wird. JA, weil es der View eben auch nicht mitteilt das es nun einen neuen Wert besitzt.

    Mach:

    C#-Quellcode

    1. public string theme
    2. {
    3. get
    4. {
    5. return CardCollection.theme;
    6. }
    7. set
    8. {
    9. CardCollection.theme = value;
    10. this.OnPropertyChanged("theme");
    11. }
    12. }


    und alles sollte passen.

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

    Hey,

    leider geht das ja nicht da kommt der Fehler: Severity Code Description Project File Line Suppression State
    Error CS1503 Argument 1: cannot convert from 'string' to 'System.ComponentModel.PropertyChangedEventArgs' Logic.Ui C:...\CardCollectionViewModel.cs 32 N/A

    C#-Quellcode

    1. ​public string theme
    2. {
    3. get
    4. {
    5. return CardCollection.theme;
    6. }
    7. set
    8. {
    9. CardCollection.theme = value;
    10. [color=#FF8C00]this.OnPropertyChanged("theme");[/color]
    11. }
    12. }


    Daher die Frage wie rufe ich die Funktion in einer Klasse auf die die ObservableCollection<T> erbt

    C#-Quellcode

    1. ​[cs]​
    [/cs]
    Ok, wir kommen da auf keinen grünen zweig.

    Erstelle ein kleines Projekt das diesen Fehler reproduziert und lade es hoch.

    Mit Teilinformationen kommen wir an dieser stelle nicht weiter. Du verwendest ein MVVM Framework und jann diesen Fehler beim besten willen nicht repsoduzieren.

    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

    Ich habe mir dein Beispiel mal angesehen. Testen kann ich es nicht, weil in dem Beispiel ja nichts passiert und ich habe gerade leider nicht die muse hier logik zu implementieren.
    Aber... Was das "Deadline" Property betrifft funktioniert folgendes: this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("deadline"));
    Warum? Weil in der Basisklasse ObservableCollection<CardVM> die Signatur der Methode eine andere ist wie in deiner ViewModelBase-Basisklasse. Das ist wichtig mit der Intellisense zu arbeiten.
    Hier sieht man genau das ein PropertyChangedEventArg erwartet wird.

    Aber sowieso schaut mir das ein wenig dubios aus, kann aber natürlich sein das es so gewollt ist.

    Im MainWindowVm hast du eine Eigenschaft "scc" vom Typ ObservableCollection<SetOfCardCollection>
    SetOfCardCollection wiederum erbt von ObservableCollection<CardCollection>
    CardCollection erbt dann von ObservableCollection<Card>

    Wenn überhaupt sollte ja CardCollection von ObservableCollection<CardVm> erben oder.

    Ich weis nicht ob es so gewollt ist das du eine Auflistung in einer Auflistung in einer Auflistung. Also ganze drei Listen ineinander. ?(

    Mal ganz abgesehen davon das du das Model bis zum View durchreichst was schlecht ist da es das Pattern verletzt und so in diesem Fall im View auch keinerlei Benachrichtigungen zur verfügugn hast, geschweige denn von Intellisense im XAML Editor.

    Ich habe schon mitbekommen das es eine Lernkerten App werden soll. Aber ich glaube nicht das du soo viele Listen verschachteln willst. Im Grunde musst du für dieses Vorhaben nicht mal von ObservableCollection erben. Kann man, muss man aber 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. ##

    Hey,

    Im MainWindowVm hast du eine Eigenschaft "scc" vom Typ

    C-Quellcode

    1. ObservableCollection<SetOfCardCollection>
    sollte eigentlich

    C-Quellcode

    1. ObservableCollection<CardCollection>
    sein. Das mit dem Durchreichen habe ich gemacht um die Synchronisation vom Model und ViewModel zu gewährleisten falls jemand auch im Model direkt was hinzufügt.

    Wie würdest du das denn machen, wenn du eine Liste von mehreren Listen mit Carten haben möchtest. Hierbei soll die Liste mit den Karten aber auch zusätzliche Eigenschaften haben. Also welche Klasse (Model und View) würde welche erben. ObservableCollections sollen wir laut unseren Prof schon benutzen.

    Und OMG Danke endlich

    C-Quellcode

    1. this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("deadline"));
    das funktioniert :D. Habe es mitlerweile auch hinbekommen habe aber die CardCollection jetzt nur noch von ViewModelBase erben lassen und eine weitere property als observablecollection hinzugefügt. Hier stellt sich die Frage darf man das? Verletzt das das Pattern?

    Danke und Gruß

    Sandro

    Sandro2020 schrieb:

    Das mit dem Durchreichen habe ich gemacht um die Synchronisation vom Model und ViewModel zu gewährleisten falls jemand auch im Model direkt was hinzufügt.

    Ist schon logisch. So wie du es in der Klasse CardVM hast passt es auch. Das Model Objekt ist Private und dann hast du die Properties inkl. OnPropertyChanged. So ist es korrekt.
    Aber in CardCollectionVM hast du public CardCollection CardCollection;. Ist jetzt nicht so schlimm solange du es im View nicht verwendest aber dennoch sollte man sich daran halten weil vieleicht jemand anderes auf die Idee kommen könnte diese Variable von aussen zu verwenden. Wie gesagt, nicht so schlimm aber du wolltest du konform bleiben.

    Sandro2020 schrieb:

    ie würdest du das denn machen, wenn du eine Liste von mehreren Listen mit Carten haben möchtest.

    Ich hätte eine CardsVM Klasse in der ich einfach ein Property ObservableCollection(Of CardVM) hätte. Auch hier kann ich weitere Eigenschaften definieren. Das ist Geschmacksache aber ich denke diese drei Listen ineinander sind so nicht gewollt gewesen. Oder?

    Sandro2020 schrieb:

    das funktioniert :D.

    Super. hätte aber seit Post 2 so sein können. ;) Fehlende benachrichtigung liegt in der WPF so gut wie immer genau an diesem Fehler.

    Sandro2020 schrieb:

    habe aber die CardCollection jetzt nur noch von ViewModelBase erben lassen

    Glaube ich dir nicht! Da es eine Model-Klasse ist würde ein Verweis auf "Logic.Ui" benötigt werden und das würde einen Ringverweis bedeuten, was nicht klappt.

    Sandro2020 schrieb:

    Verletzt das das Pattern?

    Nein, das verletzt das Pattern nicht. Das Pattern besagt das die Layer Model - View - ViewModel getrennt sein müssen. Und das sind sie ja. Bedeutet, solange du z.b. im View keinen Verweis auf das Model benötigst ist alles gut.
    Aber bedenke, auch wenn du im View keinen Verweis auf das Model hast würde das Binding auf ein Model-Objekt funktionieren, ohne Intellisense im XAML Designer, aber es funktioniert. Auch das würde das Pattern verletzen wenn man ganz genau ist, aber da wäre man schon sehr genau (wie ich oben eben, aber du wolltest es genau wissen).

    Kleine Anmerkung noch du deinem Code:
    Du musst übrigens nicht immer den Namen des Property im OnPropertyChanged("Name"); angeben. Du kannst auch OnPropertyChanged(nameof(Name));, so hast du schon mal die Compilerprüfung und wenn du ein Property umbenennst wird es an dieser Stelle auch umbenannt. Und da in deiner ViewModel-Basisklasse [CallerMemberName] verwendet wurde kannst du auch völlig simple OnPropertyChanged(); verwenden und das CallerMeberName Attribut kümmert sich um den Rest. Funktioniert hald nur direkt im Property welches du übergeben willst. Willst du im Setter vom Property "Test1" der WPF vieleicht noch mitteilen das sich das ReadOnly Property "Test2" damit auch geändert hast musst du das dann mit OnPropertyChanged(nameof(Test2)); angeben.

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