【问题标题】:Building ViewModels based on nested Model Entities in WPF and MVVM Pattern基于 WPF 和 MVVM 模式中的嵌套模型实体构建 ViewModel
【发布时间】:2011-05-13 18:05:40
【问题描述】:

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

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

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

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

我需要通过模型保持原始关系,因为我在 Pattern 上有一个 Serialize() 方法,可以将它序列化为 XML 文件..(带有相关的 Tracks 和 Hits)

为了能够将模式绑定到用户控件及其嵌套模板,我还应该有一个带有 ObservableCollection 的 PatternViewModel,TrackViewModel 和 HitViewModel 也是如此。我需要自定义演示文稿视图模型上不属于业务对象的属性(颜色等)

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

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

【问题讨论】:

    标签: wpf mvvm nested viewmodel


    【解决方案1】:

    我已经成功完成的一件事是将 ObservableCollection 从模型中移出。这是我的一般模式:

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

    这为每个不同的 ViewModel 带来了很多重复的代码,但这是我能想到的最好的。它至少可以根据您需要的复杂性进行扩展——如果您有一个仅添加的集合,则不必编写太多代码;如果您有一个支持任意重新排序、插入、排序等的集合,那么工作量就更大了。

    【讨论】:

    • 这很有趣,真的,是一种更简洁的方法,但在编写代码方面仍然是一种矫枉过正,我希望有一个好方法,我尝试考虑对模型进行子类化,并且大量其他解决方案,但没有一个足够好..我也找不到使用具有嵌套关系的模型的 MVVM 的好例子..
    • 是的,这是一个荒谬的工作量,但这是我找到的最好的方法。我希望其中一个 MVVM 框架能够应用约定来自动将 ViewModel 包装在您的模型周围,就像它们自动发现您的 ViewModel 的视图一样。然后你只需要一个 ObservableCollection(在你的模型中),其余的会自己处理。
    【解决方案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 上管理 HitViewModels)

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

    【讨论】:

    • +1 用于分享您的解决方案。尽管如此,我仍然对 MVVM 需要多少绑定代码来应对这种“常见”场景感到惊讶:-/
    【解决方案3】:

    我认为我遇到了同样的问题,如果你像“带有 ObservableCollection 的 PatternViewModel”那样做,你也会因为你开始复制数据而对你的性能产生巨大影响。

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

    这样可以避免重复的关系。

    【讨论】:

    • 这正是我现在使用的方法,我正在绑定一个具有 ObservableCollection Tracks 的 PatternViewModel,但我想在 Tracks 和 Hits 上添加表示逻辑,例如颜色HitViewModel 上的属性返回对应的 Track Color 或 ToggleCommand,将其打开和关闭......我需要在模型上添加这个表示逻辑而不是复制关系和数据,但如果我把模型上的表示逻辑,我最终没有遵循模式..
    【解决方案4】:

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

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

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

    【讨论】:

      猜你喜欢
      • 2013-12-09
      • 2016-07-17
      • 2015-08-24
      • 1970-01-01
      • 1970-01-01
      • 2010-11-10
      • 1970-01-01
      • 2011-05-20
      • 1970-01-01
      相关资源
      最近更新 更多