web-dev-qa-db-fra.com

Comment écrire un ViewModelBase dans MVVM

Je suis assez nouveau dans l'environnement de programmation WPF. J'essaie d'écrire un programme en utilisant un modèle de conception MVVM.

J'ai fait des études et lu des articles qui s'y rapportent et je suis souvent tombé sur cette chose appelée 

ViewModelBase

Je sais ce que c'est .. Mais puis-je savoir précisément par où commencer pour pouvoir écrire ma propre ViewModelBase? Comme ... vraiment comprendre ce qui se passe sans devenir trop compliqué. Je vous remercie :)

19
DriLLFreAK100

Utiliser des frameworks MVVM ne vaut rien si vous ne savez pas ce qui se passe à l'intérieur.

Alors, allons étape par étape et construisez votre propre classe ViewModelBase.

  1. ViewModelBase est une classe commune à tous vos modèles de vue. Passons toute la logique commune à cette classe. 

  2. Vos modèles de vue doivent implémenter INotifyPropertyChanged (comprenez-vous pourquoi?)

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

    l'attribut [CallerMemberName] n'est pas obligatoire, mais il vous permettra d'écrire: OnPropertyChanged(); au lieu de OnPropertyChanged("SomeProperty");, vous éviterez ainsi que la chaîne reste constante dans votre code. Exemple:

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

    Veuillez noter que OnPropertyChanged(() => SomeProperty) n'est plus recommandé, car nous avons l'opérateur nameof en C # 6.

  3. Il est courant d'implémenter des propriétés qui appellent PropertyChanged comme ceci:

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

    Définissons SetProperty dans votre 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;
    }
    

    Il déclenche simplement l'événement PropertyChanged lorsque la valeur de la propriété change et renvoie true. il ne déclenche pas l'événement lorsque la valeur n'a pas changé et renvoie false. L'idée de base est que la méthode SetProperty est virtuelle et que vous pouvez l'étendre dans une classe plus concrète, par exemple pour déclencher la validation, ou en appelant l'événement PropertyChanging.

C'est joli ça. C’est tout ce que votre ViewModelBase doit contenir à ce stade. Le reste dépend de votre projet. Par exemple, votre application utilise la navigation de base de pages et vous avez écrit votre propre NavigationService pour gérer la navigation à partir de ViewModel. Vous pouvez donc ajouter la propriété NavigationSerivce à votre classe ViewModelBase afin d’y avoir accès à partir de tous vos modèles de vue, si vous le souhaitez.

afin de gagner en réutilisabilité et de conserver la SRP, j'ai la classe appelée BindableBase qui est à peu près l'implémentation de INotifyPropertyChanged comme nous l'avons fait ici. Je réutilise cette classe dans toutes les solutions WPF/UWP/Silverligt/WindowsPhone, car elle est universelle.

Ensuite, dans chaque projet, je crée une classe ViewModelBase personnalisée dérivée de BindableBase:

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>()   
}

si j'ai une application qui utilise la navigation par page, je spécifie également la classe de base pour les modèles de vue de page.

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

Je pourrais avoir une autre classe pour les dialogues:

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

Vous avez un paquet de nuget pour implémenter MVVM

  1. MVVM light
  2. Croix MVVM
  3. Prisme

Pour moi, le plus simple pour un débutant est MVVM light car il fournit des exemples de code.

Le mieux est donc d'installer ce paquet de nugets, de jeter un coup d'œil sur le code généré et de nous revenir pour plus d'explications si vous avez besoin.

4
OrcusZ

J'aime cette BaseVewModel elle donne un style bien net à vos modèles de vue. Découvrez les différentes comparaisons «avant» et «après». Bien sûr, rien n'est obligatoire - si vous n'aimez pas une fonctionnalité fournie par BaseViewModel, ne l'utilisez pas. Ou modifiez-le parce que vous avez le code source. Notez en particulier qu'il existe trois manières différentes d'implémenter des propriétés avec notification de modification: choisissez le niveau de sophistication que vous comprenez/avec lequel vous vous sentez à l'aise.

2
Paul Smith

Dans la plupart des frameworks MVVM, les classes ViewModel de base contiennent en réalité très peu de code, généralement une simple implémentation de INotifyPropertyChanged et certaines fonctions d'assistance.

Jetez un coup d'œil au code source des classes ViewModelBase et ObservableObject de MVVM Light. ObservableObject est principalement l'implémentation INotifyPropertyChanged - en utilisant une expression lambda plutôt que des "chaînes magiques" pour le nom de la propriété. ViewModelBase étend ObservableObject et est principalement une méthode utilitaire permettant de déterminer si vous utilisez le concepteur Visual Studio.

0
Peregrine

La classe ci-dessous peut être utilisée comme base de modèle d'affichage dans les projets WPF:

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));
    }
}

Et un exemple de ViewModel class est:

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