ViewModel应该如何引用其Models属性?

M46*_*463 5 c# data-binding wpf mvvm

由于ViewModel的工作是“准备”模型的属性以在View中显示,因此从ViewModel引用基础Models属性的最佳方法是什么?

我现在可以考虑两种解决方案:


选项1-在ViewModel中复制模型的属性(包装方法)

建筑

class Model
{
    public string p1 { get; set; }
    public int p2 { get; set; }
}

class ViewModel : INotifyPropertyChanged
{
    // Model-instance for this ViewModel
    private Model M;

    public string p1 
    {
        get { return M.p1; }
        set 
        { 
            M.p1 = value; 
            // assuming View controls are bound to the ViewModel's properties 
            RaisePropertyChanged("p1");  
        }
    }

    // let's say, I only want to check a Checkbox in the View,
    // if the value of p2 exceeds 10.
    // Raising the property changed notification would get handled
    // in the modifying code instead of the missing setter of this property.
    public bool p2
    {
        get 
        { 
            if (M.p2 > 10)
            { return true; }
            else
            { return false; }
        }
    }

    // Initialize the Model of the ViewModel instance in its c'tor
    public ViewModel()
    { M = new Model(); }
}
Run Code Online (Sandbox Code Playgroud)

捆绑

<Textbox Text="{Binding p1}"/>
<Checkbox IsEnabled="False" IsChecked="{Binding p2, Mode=OneWay}"/>    
Run Code Online (Sandbox Code Playgroud)

好处

  • 完全控制模型的属性如何在视图上显示,如p2所示:int根据需要转换为bool
  • 可以单独提出ViewModel属性的更改,与选项2相比,可能会稍微提高性能。

缺点

  • 违反DRY
  • 更多代码来编写/维护。
  • 对Model / ViewModel的修改很容易成为shot弹枪手术

选项2-将整个模型视为ViewModel的属性

建筑

class Model
{
    public string p1 { get; set; }
    public int p2 { get; set; }
}

class ViewModel : INotifyPropertyChanged
{
    // Model instance for this ViewModel (private field with public property)
    private Model _M;
    public Model M 
    { 
       get { return _M; }
       set 
       {
           _M = value;
           // Raising the changing notification for the WHOLE Model-instance.
           // This should cause ALL bound View-controls to update their values,
           // even if only a single property actually got changed
           RaisePropertyChanged("M");
       } 
    }

    // Initialize the Model of the ViewModel instance in its ctor
    public ViewModel()
    { M = new Model(); }
}
Run Code Online (Sandbox Code Playgroud)

捆绑

<Textbox Text="{Binding M.p1}"/>
<Checkbox IsEnabled="False" IsChecked="{Binding M.p2, Mode=OneWay, Converter={StaticResource InverseBooleanConverter}"/>
Run Code Online (Sandbox Code Playgroud)

好处

  • 可以节省很多代码。
  • 降低复杂性。
  • 提高可维护性。

缺点

  • 在这种方法中,除了Model的某些可能交互逻辑外,ViewModel只是Models属性的连续流热水器。
  • 无法控制模型的属性如何在视图中显示-最终导致完全不必要的ViewModel以及视图中转换逻辑的实现。

Mik*_*son 5

ViewModel的责任是将Model公开给View,不应将Model的属性公开为ViewModel中的其他属性,而是将View直接绑定到模型。

此外,在模型中包含逻辑是没有错的,实际上,与ViewModel相比,在模型中包含与模型相关的代码更有意义。

这是一个例子:

public class Movie
{
    private string _Name;

    public string Name
    {
        get { return _Name; }
        set
        {
            _Name = value;

            //Notify property changed stuff (if required)

            //This is obviously a stupid example, but the idea
            //is to contain model related logic inside the model.
            //It makes more sense inside the model.
            MyFavourite = value == "My Movie";
        }
    }

    private bool _MyFavourite;

    public bool MyFavourite
    {
        get { return _MyFavourite; }
        set
        {
            _MyFavourite = value;

            //Notify property changed stuff.
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,为了更直接地回答您的问题,您应该将模型作为属性公开在视图模型中。

public class ViewModel
{
    private Movie _Model;

    public Movie Model
    {
        get { return _Model; }
        set 
        { 
            _Model = value;

            //Property changed stuff (if required)
        }
    }

    ...
}
Run Code Online (Sandbox Code Playgroud)

因此,您的视图将绑定到该Model属性,就像您已经这样做一样。

编辑

在用于强制转换为类型的示例中,您可以在Model中实现一个只读属性,如下所示:

public bool MyBool
{
    get 
    { 
        return MyInt > 10; }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在的奇妙之处在于,INotifyPropertyChanged每当MyInt更改时,您都需要为此属性调用。因此,您的其他属性如下所示:

public int MyInt
{
   get { ... }
   set 
   {
       _MyInt = value;

       //Notify property changed for the read-only property too.
       OnPropertyChanged();
       OnPropertyChanged("MyBool");
   }
}
Run Code Online (Sandbox Code Playgroud)

  • 我很困惑,因为据我所知,这个答案违反了 MVVM 的整个前提。ViewModel 应该“隐藏”模型,对吗?!我知道模式不是法律,但问题明确是关于如何遵循该模式,所以正确的答案不应该实际上解释如何遵循该模式吗? (8认同)
  • 因此,基本上,您鼓励使用**选项#2**。但是,如果 ViewModel 的唯一目的是向视图显示模型的实例,为什么我还需要 ViewModel 呢?此外,缺少 Models 属性准备的问题(例如类型转换,请参阅选项 #1 的 *advantages* 部分中的 *M.p2* 示例)在本次尝试中仍未得到解决。 (3认同)
  • 我必须同意@LeMotJuiced 的观点。举例来说,我有一个服务从外部服务获取域对象并将数据显示为 IEnumerable&lt;MyModel&gt;。我不希望它知道它的用途,事实上在大多数情况下,它不知道,因为模型位于一个单独的库中,没有引用 WPF(或任何其他视图)。因此,在理想情况下,模型应该只包含数据。然后,ViewModel 决定将模型的哪一部分呈现给视图并包含视图特定逻辑,例如复选框的属性。 (3认同)
  • 是的,**选项2 **是可行的方法。一般用法要求将视图绑定到视图模型,在该视图模型中公开模型,这并不意味着在某些情况下(非常罕见)不需要视图模型,在这种情况下,您可以直接绑定到模型。请记住,MVVM是一种模式,而不是法律。关于类型转换,没有什么可以阻止您在模型中创建**只读属性**(仅具有“ get”的属性),这将使您能够将逻辑包含在模型中。**或**,您可以像完成操作一样使用转换器。 (2认同)