尝试通过微软示例理解 MVVM
Trying to understand MVVM through microsoft example
看起来 ViewModel 包含一组设置为视图的数据上下文的模型。
在 MVVM 中,ViewModel 不应该是视图的数据上下文,模型将在其中修改 ViewModel,然后影响 View 吗?
TL; DR - 这个例子根本不是 MVVM。
是的,这是一个糟糕的 MVVM 示例。视图在运行时绑定到在 "viewmodel" 中查询 属性 的 LINQ 结果,而不是绑定到视图模型本身。 Accomplishments
没有在其他任何地方使用。
换句话说,在 OP 指向的示例中,视图根本没有绑定到视图模型,而是错误地直接绑定到模型集合。 我不认为 ObservableCollection<>
构成合理的 ViewModel。 (或 IEnumerable<>
;IList<>
或类似)
这是视图代码隐藏中的违规行:
LevelViewOnPage.DataContext = from Accomplishment in vm.Accomplishments
where Accomplishment.Type == "Level"
select Accomplishment;
通常您会将视图的 DataContext
绑定到视图模型。
其他问题
在代码审查方面,一些奇怪的设计和命名约定实际上非常糟糕。
视图通过以下方式调用视图模型:
vm.GetAccomplishments();
...没有 "get" 任何东西,因为它是 void
。该方法填充了 Accomplishments
属性,如果顺序错误,您无论如何都必须阅读,太糟糕了。
如果您遵循 SOLID principles of object oriented programming,那将给 ViewModel 提供不止一个更改理由。模型很简单——一个模型,一个智力为零的愚蠢对象。
ViewModel 的职责只是保持可视化的状态,例如显示 Person 的当前值,并将任何更改转发到适当的底层服务 class。
无论如何,ViewModel 是动作发生的地方。
Order/OrderLines 的永无止境的示例,在该示例中,您构建了一个包含您想要进一步显示的所有属性的 ViewModel,这表明 ViewModel 完全是关于编排要在视图中显示的对象并促进与您的其余代码进行通信
ViewModel 通常还会扩展视图的属性,例如您可以在 LoginViewModel 中拥有一个 属性,如下所示:
public class LoginViewModel : INotifyPropertyChanged
{
public string Username{get;set;}
public string Password{get;set;}
public bool CanLogin {
get{
return Username.Length > 0 && Password.Length > 0;
}
}
}
在上面的 class 中,您现在可以将“CanLogin”属性 数据绑定到您的 "Enabled" 属性视图,以便只有在用户名和密码具有值时才能按下按钮,即使 "CanLogin" 不是模型的一部分。
然而,最大的争论是 ViewModel 是否已经有太多的责任,因为它既提供了对模型所有属性的访问(责任 1),也促进了一些代码逻辑(即调用 "UpdatePerson" 来自某些 PersonService class),这违反了单一职责原则。
出于这个原因,为了保持关注点的更清晰分离,我经常创建两个不同级别的 ViewModel - 级别 1 是到模型的纯映射,它基本上只是为我的模型在所有模型上提供 INotifyPropertyChanged public 属性,在第二层,我创建了与底层服务 class 进行一些交互的 ViewModel(例如 "VerifyPassword()"、"UpdatePerson()" 等
看起来 ViewModel 包含一组设置为视图的数据上下文的模型。
在 MVVM 中,ViewModel 不应该是视图的数据上下文,模型将在其中修改 ViewModel,然后影响 View 吗?
TL; DR - 这个例子根本不是 MVVM。
是的,这是一个糟糕的 MVVM 示例。视图在运行时绑定到在 "viewmodel" 中查询 属性 的 LINQ 结果,而不是绑定到视图模型本身。 Accomplishments
没有在其他任何地方使用。
换句话说,在 OP 指向的示例中,视图根本没有绑定到视图模型,而是错误地直接绑定到模型集合。 我不认为 ObservableCollection<>
构成合理的 ViewModel。 (或 IEnumerable<>
;IList<>
或类似)
这是视图代码隐藏中的违规行:
LevelViewOnPage.DataContext = from Accomplishment in vm.Accomplishments
where Accomplishment.Type == "Level"
select Accomplishment;
通常您会将视图的 DataContext
绑定到视图模型。
其他问题
在代码审查方面,一些奇怪的设计和命名约定实际上非常糟糕。
视图通过以下方式调用视图模型:
vm.GetAccomplishments();
...没有 "get" 任何东西,因为它是 void
。该方法填充了 Accomplishments
属性,如果顺序错误,您无论如何都必须阅读,太糟糕了。
如果您遵循 SOLID principles of object oriented programming,那将给 ViewModel 提供不止一个更改理由。模型很简单——一个模型,一个智力为零的愚蠢对象。
ViewModel 的职责只是保持可视化的状态,例如显示 Person 的当前值,并将任何更改转发到适当的底层服务 class。
无论如何,ViewModel 是动作发生的地方。
Order/OrderLines 的永无止境的示例,在该示例中,您构建了一个包含您想要进一步显示的所有属性的 ViewModel,这表明 ViewModel 完全是关于编排要在视图中显示的对象并促进与您的其余代码进行通信
ViewModel 通常还会扩展视图的属性,例如您可以在 LoginViewModel 中拥有一个 属性,如下所示:
public class LoginViewModel : INotifyPropertyChanged
{
public string Username{get;set;}
public string Password{get;set;}
public bool CanLogin {
get{
return Username.Length > 0 && Password.Length > 0;
}
}
}
在上面的 class 中,您现在可以将“CanLogin”属性 数据绑定到您的 "Enabled" 属性视图,以便只有在用户名和密码具有值时才能按下按钮,即使 "CanLogin" 不是模型的一部分。
然而,最大的争论是 ViewModel 是否已经有太多的责任,因为它既提供了对模型所有属性的访问(责任 1),也促进了一些代码逻辑(即调用 "UpdatePerson" 来自某些 PersonService class),这违反了单一职责原则。
出于这个原因,为了保持关注点的更清晰分离,我经常创建两个不同级别的 ViewModel - 级别 1 是到模型的纯映射,它基本上只是为我的模型在所有模型上提供 INotifyPropertyChanged public 属性,在第二层,我创建了与底层服务 class 进行一些交互的 ViewModel(例如 "VerifyPassword()"、"UpdatePerson()" 等