我正在为Windows应用商店应用程序应用MVVM模式(并在过程中学习它)。
现在我倾向于在View和ViewModel之间建立1:1的对应关系,其中多个ViewModel依赖于相同的底层模型。
例如,假设我有一个实体“学生”。我有两种方式来查看学生:在全屏详细信息页面中或作为教室中的学生列表。这导致以下View / ViewModel对:
目前我假设我的ViewModel将直接公开Model,而我的Xaml将绑定到ViewModel.Model.property-name(我意识到这是有争议的)。
假设我可以从任一视图对学生执行某些操作(例如,“毕业生”)。我希望在我的模型中具有研究生行为(以避免遗忘域模型),并且我希望避免在依赖于相同模型的ViewModel之间重复行为。
我的意图是拥有一个ICommand(例如,一个RelayCommand),我可以将一个Graduate按钮绑定到View中。这是我的问题:
有没有理由不让ICommand成为Model类的属性?
基本上这意味着类似以下内容(忽略了对存储库的需求):
public class Student {
public ICommand GraduateCommand { get { ... } }
void Graduate() { ... }
}
这样,StudentDetailsView和StudentListItemsView都可以拥有绑定到该命令的Xaml(其中DataContext是StudentViewModel,Model是公共属性):
<Button Command="{Binding Model.GraduateCommand}" />
显然,我可以将Student :: Graduate()公开,在两个ViewModel上创建重复的GraduateCommands,并让执行委托调用Model.Graduate()。但是,通过ICommand而不是方法暴露类的行为会有什么不利之处?
答案 0 :(得分:2)
首先,在很多情况下,如果你可以在模型上实现INotifyPropertyChanged
,那么直接从View绑定到模型就完全没问题了。它仍然是MVVM。这可以防止ViewModel混乱使用大量“relay-direct-to-Model”代码。您只在VM中包含View不能直接使用的内容(需要换行/非规范化/转换数据,或者Model属性不实现INPC,或者您需要另一个验证层......)。
那就是,命令是View和ViewModel之间通信的主要手段。
由于这些原因,命令不属于模型。
如果您担心跨VM的代码重复,可以创建一个StudentViewModel
,StudentDetailsViewModel
和StudentListItemViewModel
将继承。 StudentViewModel
将定义命令及其常见行为。
答案 1 :(得分:1)
如果在视图中使用模型的属性,则应该停止调用该MVVM。您可以将研究生命令实现移动到另一个类(比如一个Helper类),并在ViewModel之间共享它(在初始化期间)。
GraduateCommand=new RelayCommand (GraduateHelper.Graduate, CanGraduate);
错误:将Graduate()放入您的实体。
修改强>
INotifyPropertyChanged的扩展方法
public static class NotifyExtension
{
public static void OnPropertyChanged(this INotifyPropertyChanged source, PropertyChangedEventHandler h, string propertyName)
{
PropertyChangedEventHandler handler = h;
if (handler != null) handler(source, new PropertyChangedEventArgs(propertyName));
}
public static bool SetProperty<T>(this INotifyPropertyChanged source,PropertyChangedEventHandler handler, ref T field, T value, string propertyName)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
source.OnPropertyChanged(handler, propertyName);
return true;
}
}
然后:
public class Student:INotifyPropertyChanged
{
private string _name = "Name";
public string Name
{
get { return _name; }
set {
this.SetProperty<string>(PropertyChanged, ref _name, value, "Name"); }
}
public event PropertyChangedEventHandler PropertyChanged;
}
public partial class MyViewModel :INotifyPropertyChanged
{
private Student _student=new Student();
public Student Student
{
get { return _student; }
set
{
this.SetProperty<Student>(PropertyChanged, ref _student, value, "Student");
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
最后Xaml:
<TextBlock Text="{Binding Path=Student.Name}"></TextBlock>