Solution Explorer

MVVM Tutorial – Part 4 (ViewModel)

Solution Explorer

Solution Explorer

In diesem Teil wollen wir nun ein konkretes ViewModel bauen. Dafür benutzen wir zum einen unser Model (die Personen aus Part 2) und unsere Basisimplementation des ViewModels (aus Part 3).

Wir erstellen ein neues Projekt vom Typ Class Library in unserer Solution und nennen es ViewModel. Wir benötigen Referenzen auf die Projekte Model und ViewModelBase und außerdem noch eine Referenz auf PresentationCore. In dem Projekt erstellen wir eine Klasse PersonViewModel, welche von unserem generischen ViewModel erbt und als generischen Typ unsere Person hat.

namespace ViewModel
{
    using ViewModelBase;
    using Model;

    /// <summary>
    /// A ViewModel for a Person.
    /// </summary>
    public class PersonViewModel : ViewModel<Person>
    {
        public PersonViewModel(Person model)
            : base(model)
        {
        }
    }
}

Jetzt müssen wir für alle Properties unseres Models, welche von außen bearbeitet werden dürfen, Properties in unserem ViewModel definieren. Wichtig ist dabei, dass im Setter die OnPropertyChanged Methode aufgerufen wird.

        public string Name
        {
            get
            {
                return Model.Name;
            }
            set
            {
                if (Name != value)
                {
                    Model.Name = value;
                    this.OnPropertyChanged("Name");
                }
            }
        }

Um GetHashCode und Equals müssen wir uns nicht weiter kümmern, da wir dies bereits in unserem generischen ViewModel erledigt haben.

Jetzt möchten wir natürlich noch die PersonList in ein ViewModel kapseln. Hierzu erstellen wir ein PersonListViewModel. Dieses erbt von unserem nicht-generischen ViewModel. Als einzige Property bekommt es eine ObservableCollection mit dem generischen Typ PersonViewModel. Also eine Auflistung von PersonViewModels (nicht Persons!). Wir nutzen hier eine ObservableCollection, da diese INotifyCollectionChanged und INotifyPropertyChanged implementiert, welches von einer Collection, welche an die GUI gebunden werden kann, gefordert wird.

    public class PersonListViewModel : ViewModel
    {

        private ObservableCollection<PersonViewModel> persons;

        public ObservableCollection<PersonViewModel> Persons
        {
            get
            {
                return persons;
            }
            set
            {
                if (Persons != value)
                {
                    persons = value;
                    this.OnPropertyChanged("Persons");
                }
            }
        }

        public PersonListViewModel()
        {
            persons = new ObservableCollection<PersonViewModel>(PersonList.Persons.Select(p => new PersonViewModel(p)));
            persons.CollectionChanged += Persons_CollectionChanged;
        }

Wir benutzen im Konstruktor das private Field persons und nicht die public Property Persons, da OnPropertyChanged sonst einen Fehler wirft.
Außerdem nutzen wir das CollectionChanged Event, um Änderungen an der Collection (Personen hinzugefügt, oder gelöscht) an unser Model weiter zu geben.

        void Persons_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                foreach (PersonViewModel vm in e.NewItems)
                {
                    PersonList.Persons.Add(vm.Model);
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                foreach (PersonViewModel vm in e.OldItems)
                {
                    PersonList.Persons.Remove(vm.Model);
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Reset)
            {
                PersonList.Persons.Clear();
            }
        }

Außerdem erstellen wir noch ein Command, welches uns eine Person zur Liste hinzufügt:

        private ICommand addPersonCommand;

        public ICommand AddPersonCommand
        {
            get
            {
                if (addPersonCommand == null)
                {
                    addPersonCommand = new RelayCommand(p => ExecuteAddPersonCommand());
                }
                return addPersonCommand;
            }
        }

        private void ExecuteAddPersonCommand()
        {
            Persons.Add(new PersonViewModel(new Person()));
        }

Diese Art der Implementation ist ein Standardpattern für Commands. Da es keine Vorbedingung für das Command gibt, bekommt der Konstruktor vom RelayCommand nur den Execute Parameter übergeben. Außerdem interessiert uns der dem Command übergebene Parameter nicht, wesshalb wir ihn in ExecuteAddPersonCommand nicht weiter verwenden.

Wie immer am Ende die Solution: MVVMTutorial Solution (Model, ViewModelBase und ViewModel)

Im nächsten Part erstellen wir dann die WPF GUI mit XAML (yeah, Bullshit Buzzword Bingo)

3 Gedanken zu „MVVM Tutorial – Part 4 (ViewModel)

  1. Sifiker

    ich hab da ein problem, bei einem Rezeptbuch:

    mZutaten = new ObservableCollection(Rezept.Zutaten.Select( p => new ZutatViewModel(p)));

    da kommt der error:
    Cannot implicitly convert type ‚System.Collections.ObjectModel.ObservableCollection‘ to ‚System.Collections.ObjectModel.ObservableCollection‘

    suche schon ewig den fehler. jemand eine ahnung, was sein könnte?

  2. Philipp Kühn Beitragsautor

    Es sieht so aus, als sei der generische Typ von mZutaten ein anderes, als der, welcher durch deine Select Abfrage zustande kommt.
    Kontrollier das mal.

  3. Alex Fleschhut

    Hallo,
    ich habe eine ähnliche Anwendung und möchte meine Liste an Prozessen (hier Personen) persistieren.
    Dazu möchte ich den SharpSerializer verwenden. Für mich macht Sinn, das ProzessModel zu serialisieren, aber dann bekomme ich Probleme, die Prozesse an das ProzessViewModel zu übergeben.
    Meine Frage ist, wie ich die Speicherung von Daten mit MVVM am Besten anpacke?
    Viele Grüße, Alex

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.