Construction de Modèles de vue basés sur des entités modèles imbriquées dans le modèle WPF et MVVM

j'ai un problème de compréhension comment construire des modèles de vue basés sur les modèles suivants

(j'ai simplifié les modèles pour être plus clair)

public class Hit
{
   public bool On { get; set;}
   public Track Track { get; set; }
}
public class Track
{
   public ObservableCollection<Hit> Hits { get; set; }
   public LinearGradientBrush Color { get; set; }
   public Pattern Pattern { get; set; }
}
public class Pattern
{
   public string Name { get; set; }
   public ObservableCollection<Tracks> Tracks { get; set; }
}

Maintenant, mon problème est, comment construire le Viewmodel..

j'ai besoin de garder les relations originales à travers les models, beacaus j'ai une méthode Serialize() sur le Pattern qui le sérialise dans un fichier XML.. (avec les Pistes et Hits)

Pour pouvoir lier le pattern aux contrôles de l'utilisateur et à ses gabarits imbriqués, je devrais aussi avoir un PatternViewModel avec une collecte observable , la même chose pour le TrackViewModel et le HitViewModel.. et je dois avoir des propriétés de présentation sur mesure sur les modèles de vue qui ne font pas partie de l'objet d'affaires (couleurs et plus..)

Il a juste ne semble pas une bonne chose pour moi de dupliquer toutes les relations des modèles sur les modèles de vue... et garder une trace de toutes ces relations tout en codant les modèles de vue est également beaucoup plus susceptible d'erreur..

quelqu'un a une meilleure approche/solution?

12
demandé sur BFil 2010-11-22 13:02:01

4 réponses

une chose que j'ai faite, avec un certain succès, est de déplacer la collecte observable hors du modèle. Voici mon schéma général:

  • dans les objets modèles, exposer une propriété de type IEnumerable<TModel> qui donne accès en lecture seule à la collection. Utilisez un simple Vieux List<TModel> , pas une collecte observable, comme la collection de soutien.
  • pour le code qui doit muter les collections des modèles (Ajouter, Supprimer, etc.), ajouter des méthodes pour le modèle objet. Ne pas avoir de code extérieur manipulant directement la collection; encapsuler que l'intérieur des méthodes sur le modèle.
  • ajoutez des événements au modèle pour chaque type de changement que vous autorisez. Par exemple, si votre modèle supporte uniquement l'ajout d'éléments à la fin de la collection, et la suppression d'éléments, alors vous aurez besoin D'un événement ItemAdded et D'un événement ItemDeleted. Créez un descendant EventArgs qui donne des informations sur l'élément qui a été ajouté. Tirer ces événements de la mutation méthode.
  • dans votre modèle, avoir un ObservableCollection<TNestedViewModel> .
  • ont le ViewModel hook les événements sur le modèle. Chaque fois que le modèle indique qu'un élément a été ajouté, instanciez un ViewModel et ajoutez-le à la collection observable du ViewModel. Chaque fois que le modèle indique qu'un élément a été supprimé, itérez la collection ObservableCollection, trouvez le modèle de vue correspondant, et supprimez-le.
  • en dehors des gestionnaires d'événements, assurez-vous que toute la collection-mutation le code est fait via le modèle -- traitez la collecte ObservableCollection de ViewModel comme strictement quelque chose pour la consommation de la vue, pas quelque chose que vous utilisez dans le code.

Cela fait beaucoup de code dupliqué pour chaque modèle de vue différent, mais c'est le meilleur que j'ai pu trouver. Il fait au moins échelle en fonction de la complexité dont vous avez besoin -- si vous avez une collection qui est add-only, vous ne devez pas écrire beaucoup de code; si vous avez une collection qui prend en charge arbitraire réordonner, insérer, trier, etc. il reste encore beaucoup de travail.

3
répondu Joe White 2010-11-22 15:52:03

j'ai fini par utiliser une partie de la solution que Joe White a suggéré, d'une manière légèrement différente

la solution était de simplement laisser les modèles tels qu'ils étaient au début, et d'attacher aux collections un eventhandler pour la collecte changée des collections internes, par exemple, le nouveau modèle Patternview serait:

public class PatternViewModel : ISerializable
{
    public Pattern Pattern { get; set; }
    public ObservableCollection<TrackViewModel> Tracks { get; set; }

    public PatternViewModel(string name)
    {
        Pattern = new Pattern(name);
        Tracks = new ObservableCollection<TrackViewModel>();
        Pattern.Tracks.CollectionChanged += new NotifyCollectionChangedEventHandler(Tracks_CollectionChanged);
    }

    void Tracks_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
                foreach (Track track in e.NewItems)
                {
                    var position = Pattern.Tracks.IndexOf((Track) e.NewItems[0]);
                    Tracks.Insert(position,new TrackViewModel(track, this));
                }
                break;
            case NotifyCollectionChangedAction.Remove:
                foreach (Track track in e.OldItems)
                    Tracks.Remove(Tracks.First(t => t.Track == track));
                break;
            case NotifyCollectionChangedAction.Move:
                for (int k = 0; k < e.NewItems.Count; k++)
                {
                    var oldPosition = Tracks.IndexOf(Tracks.First(t => t.Track == e.OldItems[k]));
                    var newPosition = Pattern.Tracks.IndexOf((Track) e.NewItems[k]);
                    Tracks.Move(oldPosition, newPosition);
                }
                break;
        }
    }
}

pour que je puisse attacher la nouvelle couleur/Style / commande sur les modèles de vue pour garder mes modèles de base propres

et chaque fois que j'ajoute/supprime / déplace des éléments dans la collection de modèles de base, les collections de modèles de vue restent en phase les uns avec les autres

heureusement, je n'ai pas à gérer beaucoup d'objets dans mon application, donc les données dupliquées et la performance ne sera pas un problème

Je ne l'aime pas trop, mais il fonctionne bien, et ce n'est pas une énorme quantité de travail, juste un gestionnaire d'événements pour le modèle de vue qui contient d'autres collections de modèles de vue (dans mon cas, un pour PatternViewModel pour synchroniser TrackViewModels et un autre sur TrackViewModel pour gérer HitViewModels)

Toujours intéressé à votre thoughs ou de meilleures idées =)

2
répondu BFil 2010-11-30 12:15:52

je pense que j'ai eu le même problème et si vous le faites comme" PatternViewModel with an ObservableCollection " vous obtenez également un impact massif sur vos performances parce que vous commencez à dupliquer des données.

mon approche a été de construire - pour votre échantillon - un nouveau modèle Patternview avec une collecte observable. Ce n'est pas contradictoire avec MVVM car la vue est liée à la collection.

de cette façon vous pouvez éviter la duplication des relation.

1
répondu Jan 2010-11-22 10:30:16

une solution que j'envisageais, même si Je ne suis pas sûr qu'elle fonctionnerait parfaitement dans la pratique, est d'utiliser des convertisseurs pour créer un modèle de vue autour de votre modèle.

ainsi, dans votre cas, vous pouvez lier Tracks directement (par exemple) à une liste de diffusion, avec un convertisseur qui crée un nouveau TrackViewModel à partir de la piste. Tout ce que votre contrôle verrait serait un objet TrackViewModel , et tous vos modèles verraient jamais d'autres modèles.

je suis pas sûr de la mise à jour dynamique de cette idée cependant, je ne l'ai pas encore essayé.

0
répondu pete the pagan-gerbil 2010-12-30 13:27:50