基于WPF和MVVM模式中的嵌套模型实体构建ViewModel

时间:2010-11-22 10:02:02

标签: wpf mvvm nested viewmodel

我在理解如何基于以下模型构建视图模型时遇到问题

(我将模型简化为更清晰)

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

现在,我的问题是,如何构建ViewModels ..

我需要通过模型保持原始关系,beacaus我在Pattern上有一个Serialize()方法,将它序列化为XML文件..(带有相关的曲目和命中)

为了能够将模式绑定到用户控件及其嵌套模板,我还应该有一个带有ObservableCollection&lt; TrackViewModel&gt;的PatternViewModel。在它中,对于TrackViewModel和HitViewModel来说也是如此..我需要在不属于业务对象的视图模型上具有自定义表示属性(颜色和更多......)

在视图模型上复制模型的所有关系对我来说似乎不是一件好事...... 并且在编写视图模型时跟踪所有这些关系也更容易出错..

任何人都有更好的方法/解决方案吗?

4 个答案:

答案 0 :(得分:3)

我做了一件事,取得了一些成功,就是将ObservableCollection移出模型。这是我的一般模式:

  • 在模型对象中,公开类型为IEnumerable<TModel>的属性,该属性提供对集合的只读访问权限。使用普通的旧List<TModel>而不是ObservableCollection作为支持集合。
  • 对于需要改变模型集合(添加,删除等)的代码,请向模型对象添加方法。没有外部代码直接操作集合;封装模型内部的方法。
  • 为您允许的每种更改类型向模型添加事件。例如,如果模型仅支持将项添加到集合的末尾并删除项,那么您将需要ItemAdded事件和ItemDeleted事件。创建一个EventArgs后代,它提供有关已添加项目的信息。从变异方法中解雇这些事件。
  • 在您的ViewModel中,有ObservableCollection<TNestedViewModel>
  • 让ViewModel挂钩模型上的事件。每当模型说明添加了一个项目时,实例化一个ViewModel并将其添加到ViewModel的ObservableCollection中。每当模型说项目被删除时,迭代ObservableCollection,找到相应的ViewModel,然后将其删除。
  • 除了事件处理程序之外,确保所有的集合变异代码都是通过模型完成的 - 将ViewModel的ObservableCollection视为严格意义上的视图消费,而不是代码中使用的东西。

这为每个不同的ViewModel提供了大量重复的代码,但它是我能够想到的最好的代码。它至少根据您需要的复杂程度进行扩展 - 如果您有一个仅添加的集合,则不必编写太多代码;如果你有一个支持任意重新排序,插入,排序等的集合,那就更有用了。

答案 1 :(得分:2)

我最终使用Joe White提出的解决方案的一部分,方式略有不同

解决方案是将模型保留在开头,并将集合附加到内部集合的CollectionChanged的事件处理程序,例如,PatternViewModel将是:

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

所以我可以在视图模型上附加新的颜色/样式/命令以保持我的基本模型清洁

每当我在基本模型集合中添加/删除/移动项目时,视图模型集合将保持彼此同步

幸运的是,我不需要在我的应用程序中管理大量对象,因此重复的数据和性能不会成为问题

我不太喜欢它,但它运行良好,并且它不是大量工作,只是视图模型的事件处理程序,包含其他视图模型集合(在我的情况下,一个用于PatternViewModel同步TrackViewModels和TrackViewModel上的另一个管理HitViewModel)

仍然对你的想法或更好的想法感兴趣=)

答案 2 :(得分:1)

我认为我遇到了同样的问题,如果你这样做,就像“PatternViewModel with ObservableCollection&lt; TrackViewModel&gt;”您也会因为开始复制数据而对性能产生巨大影响。

我的方法是为您的示例构建一个带有ObservableCollection&lt; Track&gt;的PatternViewModel。这与MVVM没有矛盾,因为视图绑定到集合。

这样可以避免重复关系。

答案 3 :(得分:0)

我一直在考虑的一个解决方案,虽然我不确定它在实践中是否能完美运行,但是使用转换器来创建模型周围的视图模型。

因此,在您的情况下,您可以将Tracks直接绑定到(作为示例)列表框,并使用从Track创建新TrackViewModel的转换器。你所看到的所有控件都是TrackViewModel对象,所有模型都会看到其他模型。

我不确定这个想法的动态更新,但我还没试过。