wake-up-neo.net

So schreiben Sie eine ViewModelBase in MVVM

Ich bin ziemlich neu in der WPF-Programmierumgebung. Ich versuche, ein Programm mit MVVM-Entwurfsmuster zu schreiben.

Ich habe ein paar Studien gemacht und einige Artikel dazu gelesen, und oft bin ich auf dieses Ding namens gestoßen

ViewModelBase

Ich weiß, was es ist. Aber darf ich genau wissen, wo ich anfangen soll um meine eigene ViewModelBase ausschreiben zu können? Wie ... Wirklich verstehen, was passiert, ohne zu kompliziert zu werden. Vielen Dank :)

19
DriLLFreAK100

Es ist nichts wert, MVVM-Frameworks zu verwenden, wenn Sie nicht wissen, was in Ihnen vorgeht.

Gehen wir also Schritt für Schritt vor und erstellen Sie Ihre eigene ViewModelBase-Klasse.

  1. ViewModelBase ist für alle Ihre Viewmodels üblich. Verschieben wir die gesamte Logik in diese Klasse. 

  2. Ihre ViewModels sollten INotifyPropertyChanged implementieren (verstehst du warum?)

    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    das [CallerMemberName]-Attribut ist nicht erforderlich, aber Sie können Folgendes schreiben: OnPropertyChanged(); anstelle von OnPropertyChanged("SomeProperty");, sodass Sie die Zeichenfolgenkonstante in Ihrem Code vermeiden. Beispiel:

    public string FirstName
    {
        set
        {
            _firtName = value;
            OnPropertyChanged(); //instead of OnPropertyChanged("FirstName") or OnPropertyChanged(nameof(FirstName))
        }
        get{ return _firstName;}
    }
    

    Bitte beachten Sie, dass OnPropertyChanged(() => SomeProperty) nicht mehr empfohlen wird, da wir den Operator nameof in C # 6 haben.

  3. Es ist üblich, Eigenschaften zu implementieren, die PropertyChanged wie folgt aufrufen:

    public string FirstName
    {
        get { return _firstName; }
        set { SetProperty(ref _firstName, value); }
    }
    

    Definieren wir SetProperty in Ihrer Viewmodelbase:

    protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = "")
    {
        if (EqualityComparer<T>.Default.Equals(storage, value))
            return false;
        storage = value;
        this.OnPropertyChanged(propertyName);
        return true;
    }
    

    Das PropertyChanged-Ereignis wird einfach ausgelöst, wenn sich der Wert der Eigenschaft ändert und true zurückgibt. Das Ereignis wird nicht ausgelöst, wenn sich der Wert nicht geändert hat und false zurückgegeben wird. Die Grundidee ist, dass die SetProperty-Methode virtuell ist und Sie sie in einer konkreteren Klasse erweitern können, z. B. um die Validierung auszulösen, oder indem Sie das PropertyChanging-Ereignis aufrufen.

Das ist hübsch dabei. Dies ist alles, was Ihre ViewModelBase zu diesem Zeitpunkt enthalten sollte. Der Rest hängt von Ihrem Projekt ab. Beispielsweise verwendet Ihre App die Seitennavigation, und Sie haben einen eigenen NavigationService für die Navigation in ViewModel geschrieben. Sie können also der ViewModelBase-Klasse die Eigenschaft NavigationSerivce hinzufügen, sodass Sie von allen Ihren Ansichtsmodellen aus darauf zugreifen können, wenn Sie möchten.

um mehr Wiederverwendbarkeit zu erreichen und SRP beizubehalten, habe ich die Klasse BindableBase benannt. Dies ist so ziemlich die Implementierung von INotifyPropertyChanged, wie wir es hier gemacht haben. Ich verwende diese Klasse in jeder WPF/UWP/Silverligt/WindowsPhone-Lösung wieder, da sie universell ist.

Dann erstelle ich in jedem Projekt eine benutzerdefinierte ViewModelBase-Klasse, die von BindableBase abgeleitet ist:

public abstract ViewModelBase : BindableBase
{
    //project specific logic for all viewmodels. 
    //E.g in this project I want to use EventAggregator heavily:
    public virtual IEventAggregator () => ServiceLocator.GetInstance<IEventAggregator>()   
}

wenn ich eine App habe, die seitenbasierte Navigation verwendet, gebe ich auch eine Basisklasse für Seitendarstellungsmodelle an.

public abstract PageViewModelBase : ViewModelBase
{
    //for example all my pages has title:
    public string Title {get; private set;}
}

Ich könnte eine andere Klasse für Dialoge haben:

public abstract DialogViewModelBase : ViewModelBase
{
    private bool? _dialogResult;

    public event EventHandler Closing;

    public string Title {get; private set;}
    public ObservableCollection<DialogButton> DialogButtons { get; }

    public bool? DialogResult
    {
        get { return _dialogResult; }
        set { SetProperty(ref _dialogResult, value); }
    }

    public void Close()
    {
        Closing?.Invoke(this, EventArgs.Empty);
    }
}
56
Liero

Sie haben ein Nuget-Paket, um MVVM zu implementieren

  1. MVVM Licht
  2. MVVM Cross
  3. Prisma

Für mich ist das MVVM light für Anfänger leichter, da es einige Codebeispiele gibt.

Besser ist es also, dieses Nuget-Paket zu installieren, sich den generierten Code anzusehen und uns weitere Erklärungen zukommen zu lassen, falls dies erforderlich ist.

4
OrcusZ

Ich mag dieses BaseVewModel es verleiht Ihren Ansichtsmodellen einen schönen sauberen Stil. Schauen Sie sich die verschiedenen Vorher-Nachher-Vergleiche an. Natürlich ist nichts obligatorisch - wenn Sie eine von BaseViewModel bereitgestellte Funktion nicht mögen, verwenden Sie sie nicht. Oder ändern Sie es, weil Sie den Quellcode haben. Beachten Sie insbesondere, dass es drei verschiedene Möglichkeiten gibt, Eigenschaften mit Änderungsbenachrichtigung zu implementieren. Wählen Sie den Grad an Raffinesse aus, den Sie verstehen/mit dem Sie sich wohl fühlen.

2
Paul Smith

In den meisten MVVM-Frameworks enthalten die ViewModel-Basisklassen tatsächlich sehr wenig Code - normalerweise nur eine Implementierung von INotifyPropertyChanged und einige Hilfsfunktionen.

Schauen Sie sich den Quellcode für die ViewModelBase und ObservableObject - Klassen von MVVM Light an. ObservableObject ist in der Regel die INotifyPropertyChanged-Implementierung - es wird ein Lambda-Ausdruck anstelle von "magischen Zeichenfolgen" für den Eigenschaftennamen verwendet. ViewModelBase erweitert ObservableObject und ist hauptsächlich eine Dienstprogrammmethode, mit der Sie feststellen können, ob Sie im Visual Studio-Designer ausgeführt werden

0
Peregrine

Die folgende Klasse kann als Basis für das Ansichtsmodell in WPF-Projekten verwendet werden:

public abstract class ViewModelBase : INotifyPropertyChanged
{
    // uncomment the line below for Log4Net Logging
    //private static readonly log4net.ILog Log = Logging.For<ViewModelBase>();

    /// <summary>
    /// Multicast event for property change notifications.
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Checks if a property already matches a desired value.  Sets the property and
    /// notifies listeners only when necessary.
    /// </summary>
    /// <typeparam name="T">Type of the property.</typeparam>
    /// <param name="storage">Reference to a property with both getter and setter.</param>
    /// <param name="value">Desired value for the property.</param>
    /// <param name="propertyName">Name of the property used to notify listeners.This
    /// value is optional and can be provided automatically when invoked from compilers that
    /// support CallerMemberName.</param>
    /// <returns>True if the value was changed, false if the existing value matched the
    /// desired value.</returns>
    protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        if (object.Equals(storage, value)) return false;
        storage = value;
        // Log.DebugFormat("{0}.{1} = {2}", this.GetType().Name, propertyName, storage);
        this.OnPropertyChanged(propertyName);
        return true;
    }

    /// <summary>
    /// Notifies listeners that a property value has changed.
    /// </summary>
    /// <param name="propertyName">Name of the property used to notify listeners.  This
    /// value is optional and can be provided automatically when invoked from compilers
    /// that support <see cref="CallerMemberNameAttribute"/>.</param>
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var eventHandler = this.PropertyChanged;
        if (eventHandler != null)
            eventHandler(this, new PropertyChangedEventArgs(propertyName));
    }
}

Und ein Beispiel von ViewModel class ist:

public class MyViewModel : ViewModelBase
{
    private int myProperty;
    public int MyProperty
    {
        get { return myProperty; }
        set { SetProperty(ref myProperty, value);
    }
}