【问题标题】:MVVM with aggregated model classes - how to wrap in ViewModels?具有聚合模型类的 MVVM - 如何包装在 ViewModels 中?
【发布时间】:2010-08-31 18:26:52
【问题描述】:

我目前正在尝试使用 MVVM 模式创建一个小型应用程序。但是我真的不知道如何在我的 ViewModel 中正确包装聚合模型类。根据我对 MVVM 的了解,您不应该将 ViewModel 中的模型作为属性公开,否则您可以直接从视图绑定到模型。所以看来我必须将嵌套模型包装在另一个 ViewModel 中,但这会在稍后同步 Model 和 ViewModel 时带来一些问题。

那么你如何有效地做到这一点?

我将举一个简短的例子。假设我有以下模型类:

public class Bar
{
    public string Name { get; set; }
}

public class Foo
{
    public Bar NestedBar { get; set; }
}

现在我相应地创建了两个 ViewModel 类,包装了模型,但是遇到了 FooViewModel 的问题:

public class BarViewModel
{
    private Bar _bar;
    public string Name 
    { 
        get { return _bar.Name; }
        set { _bar.Name = value; }
    }
}

public class FooViewModel
{
    private Foo _foo;
    public BarViewModel Bar
    {
        get { return ???; }
        set { ??? = value; }
    }
}

现在我该如何处理 FooViewModel 的 Bar 属性?为了让“get”工作,我需要返回一个 BarViewModel 实例。我是否在 FooViewModel 中创建该类型的新字段并将 _foo.NestedBar 对象包装在其中?对该字段属性的更改应该向下传播到底层 Bar 实例,对吗?

如果我需要为该属性分配另一个 BarViewModel 实例,如下所示:

foo.Bar = new BarViewModel();

现在它不会向下传播到模型,它仍然保存着 Bar 类型的旧实例。我需要基于新的 BarViewModel 创建一个新的 Bar 对象并将其分配给 _foo,但是如何优雅地做到这一点?在这个示例中它非常简单,但如果 Bar 更复杂,有很多属性,那将是很多输入......更不用说它很容易出错,如果你忘记设置其中一个属性。

【问题讨论】:

  • 很遗憾您没有将此标记为解决方案或未发表评论!这样人们肯定不会再帮助你了!

标签: wpf mvvm


【解决方案1】:

@地精

您的代码存在一些缺陷:例如如果我从数据库中获取 Foo 对象的列表并且我想将它们中的每一个包装在 ObservableCollection 中怎么办?

那么你的 FooViewModel 构造器应该接受 Foo 模型作为参数,而不是在构造器内部创建它!

通常您这样做是为了将模型包装到视图模型中,同时将其放入可绑定的集合中:

IEnumerable<Foo> foos = fooRepository.GetFoos();
foos.Select( m => viewmodelCollection.Add(new ViewModel(m,e.g.Service)));

模型属性不会复制到 ViewModel 地狱不! ViewModel 确实将其属性委托给模型属性,例如:

public class FooViewModel
{
   private Foo _foo;

   public FooViewModel(Foo foo,IService service)
   {
      _foo = foo;

   }

   public string FoosName
   { 
      get{return _foo.Name };
      set
      {
         if(_foo.Name == value)
            return;

         _foo.Name = value;
         this.NotifyPropertyChanged("FoosName");
      }
   }

}

就像 Goblin 所说的所有 UI-Specific 界面一样:

IDataErrorInfo
INotifyPropertyChanged
IEditableObject

等等……

仅由 ViewModel 实现。

【讨论】:

  • 但是这个问题的邮件问题怎么解决:public class FooViewModel { private Foo _foo;公共 BarViewModel 酒吧 { 得到 { 返回 ???; } 放 { ??? =价值; } } }
【解决方案2】:

我上面的答案只有在你做 DDD 时才有意义 - 如果你不是 - 你可以像这样解决你的问题 - 简单地“扁平化”模型:

public class FooViewModel
{
    private Foo _foo;
    public string Name
    {
        get { return _foo.Name; }
        set { _foo.Name = value; }
    }
    public string BarProperty
    {
        get { return _foo.Bar.Property; }
        set { _foo.Bar.Property = value; }
    }
}

或者你可以像我在前面的例子中展示的那样 - 只是忽略关于聚合的所有内容......应该仍然有效。

【讨论】:

    【解决方案3】:

    好的 - 首先要做的事情 - 使用“聚合”一词意味着您遵守 DDD?如果你是 - 你正在做一个封装 no-no :-)。绝不应允许一个聚合编辑另一个聚合。如果您所拥有的是两者确实是聚合的,那么它们将变得关联(这在 DDD 意义上是完全“合法的”-但是您在 FooViewModel 上的属性将不是 BarViewModel 类型,而是 Bar 类型。这样 Bar将(应该)负责更新自身 - 我们只维护 FooViewModel 中的链接。

    但是,如果您正在做的是带有 ValueType 子级的 AggregateRoot - 那么在稍微修改域模型的情况下,您可以这样做:

    public class Foo
    {
        public string SomeProperty { get; set; }
        public Bar Bar { get; set; }
        public void Save()
        {
            //Magically saves to persistent storage...
        }
    }
    public class Bar
    {
        public Bar(string someOtherProperty)
        {
            SomeOtherProperty = someOtherProperty;
        }
        public string SomeOtherProperty { get; private set; }
    }
    

    然后是 ViewModel:

    public class FooViewModel
    {
        private Foo _foo;
        public FooViewModel()
        {
            Bar = new BarViewModel();
        }
        public BarViewModel Bar { get; private set; }
        public void SetFoo(Foo foo)
        {
            _foo = foo;
            SomeProperty = foo.SomeProperty;
            Bar.SetBar(foo.Bar);
        }
        public string SomeProperty { get; set; }
        public void SaveChanges()
        {
            _foo.SomeProperty = SomeProperty;
            _foo.Bar = Bar.CreateUpdatedBar();
            _foo.Save();
        }
    }
    public class BarViewModel
    {
        public string SomeOtherProperty { get; set; }
        public void SetBar(Bar bar)
        {
            SomeOtherProperty = bar.SomeOtherProperty;
        }
        public Bar CreateUpdatedBar()
        {
            return new Bar(SomeOtherProperty);
        }
    }
    

    这样 - FooViewModel 现在能够控制 BarViewModel(除了接受一个 valuetype 之外什么都不做 - 并在被要求时创建一个新的)。这也解决了一个常见的 UI 问题('我们如何编辑没有设置器的对象?' - 回答:'我们没有 - 我们创建一个新对象')。缺少很多充实内容(INotifyPropertyChanged、脏跟踪等,但如果您通过这种思维飞跃,这些都很容易:-)。

    我希望这有点道理 :-) 否则,我很乐意详细说明。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-18
      相关资源
      最近更新 更多