web-dev-qa-db-fra.com

Bon moyen d'actualiser la liaison de données sur toutes les propriétés d'un ViewModel lorsque le modèle change

Version courte

Si je mets à jour l'objet Model que mon ViewModel encapsule, quelle est bonne façon de déclencher des notifications de changement de propriété pour toutes les propriétés du modèle que mon ViewModel expose?

Version détaillée

Je développe un client WPF suivant le modèle MVVM et j'essaie de gérer les mises à jour entrantes, d'un service, aux données affichées dans mes vues. Lorsque le client reçoit une mise à jour, la mise à jour apparaît sous la forme d'un DTO que j'utilise comme modèle.

Si ce modèle est une mise à jour d'un modèle existant affiché dans la vue, je souhaite que le ViewModel associé mette à jour ses propriétés de databound afin que la vue reflète les modifications.

Permettez-moi d'illustrer avec un exemple. Considérez mon modèle:

class FooModel
{
  public int FooModelProperty { get; set; }
}

enveloppé dans un ViewModel:

class FooViewModel
{
  private FooModel _model;

  public FooModel Model 
  { 
    get { return _model; }
    set 
    { 
      _model = value; 
      OnPropertyChanged("Model"); 
    }
  }

  public int FooViewModelProperty
  {
    get { return Model.FooModelProperty; }
    set 
    {
      Model.FooModelProperty = value;
      OnPropertyChanged("FooViewModelProperty");
    }    
}

Le problème:

Lorsqu'un modèle mis à jour arrive, j'ai défini la propriété Model de ViewModel, comme suit:

instanceOfFooVM.Model = newModel;

Cela provoque le déclenchement de OnPropertyChanged("Model"), mais pas de OnPropertyChanged("FooViewModelProperty"), sauf si j'appelle ce dernier explicitement depuis le setter de Model. Ainsi, une vue liée à FooViewModelProperty ne sera pas mise à jour pour afficher la nouvelle valeur de cette propriété lorsque je changerai le modèle.

Appeler explicitement OnPropertyChanged pour chaque propriété Model exposée n'est évidemment pas une solution souhaitable, et ni ne prend newModel et itère à travers ses propriétés pour mettre à jour les propriétés de ViewModel une par une.

Quelle est la meilleure approche pour résoudre ce problème de mise à jour d'un modèle entier et de nécessité de déclencher des notifications de modification pour toutes ses propriétés exposées?

31
Dan J

Selon les docs :

L'événement PropertyChanged peut indiquer que toutes les propriétés de l'objet ont changé en utilisant null ou String.Empty comme nom de propriété dans PropertyChangedEventArgs.

59
Joe White

Une option consiste à écouter vos propres événements et à créer une routine d'aide pour déclencher les autres notifications selon les besoins.

Cela peut être aussi simple que d'ajouter, dans votre constructeur:

public FooViewModel()
{
    this.PropertyChanged += (o,e) =>
      {
          if (e.PropertyName == "Model")
          {
               OnPropertyChanged("FooViewModelProperty");
               // Add other properties "dependent" on Model here...
          }
      };
}
7
Reed Copsey

Chaque fois que votre propriété Model est définie, abonnez-vous à son propre événement PropertyChanged. Lorsque votre gestionnaire est appelé, déclenchez votre propre événement PropertyChanged. Lorsque Model est défini sur autre chose, supprimez votre gestionnaire de l'ancien Model.

Exemple:

class FooViewModel
{
    private FooModel _model;

    public FooModel Model 
    { 
        get { return _model; }
        set 
        { 
            if (_model != null)
            {
                _model.PropertyChanged -= ModelPropertyChanged;
            }

            if (value != null)
            {
                value.PropertyChanged += ModelPropertyChanged;
            }

            _model = value; 
            OnPropertyChanged("Model"); 
        }
    }

    public int FooViewModelProperty
    {
        get { return Model.FooModelProperty; }
        set 
        {
            Model.FooModelProperty = value;
            OnPropertyChanged("FooViewModelProperty");
        } 
    }

    private void ModelPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        // Here you will need to translate the property names from those
        // present on your Model to those present on your ViewModel.
        // For example:
        OnPropertyChanged(e.PropertyName.Replace("FooModel", "FooViewModel"));
    }
}
1
Jon
    Implements INotifyPropertyChanged
    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged 

    RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(String.Empty))

Pour VB.net si quelqu'un d'autre en a besoin. Si vous avez déjà implémenté "INotifyPropertyChanged", la dernière ligne est tout ce dont vous avez besoin.

0
VampireMonkey