Mvvm 向域模型对象添加ICommand是否合适?
我正在为Windows应用商店应用程序应用MVVM模式(并在过程中学习) 现在我倾向于在视图和ViewModel之间建立1:1的对应关系,其中多个ViewModel依赖于同一基础模型 例如,假设我有一个实体“Student”。我有两种方式来查看学生:全屏详细信息页面或教室中的学生列表。这将产生以下视图/视图模型对:Mvvm 向域模型对象添加ICommand是否合适?,mvvm,Mvvm,我正在为Windows应用商店应用程序应用MVVM模式(并在过程中学习) 现在我倾向于在视图和ViewModel之间建立1:1的对应关系,其中多个ViewModel依赖于同一基础模型 例如,假设我有一个实体“Student”。我有两种方式来查看学生:全屏详细信息页面或教室中的学生列表。这将产生以下视图/视图模型对: 学生详细信息视图/学生详细信息视图模型 StudentListItemView/StudentListItemViewModel 目前,我假设我的ViewModel将直接公开该模
- 学生详细信息视图/学生详细信息视图模型
- StudentListItemView/StudentListItemViewModel
显然,我可以将Student::Graduate()公开,在两个ViewModels上创建重复的graduateCommand,并让执行委托调用Model.Graduate()。但是,通过ICommand而不是方法公开类的行为有什么缺点呢?如果在视图中使用模型的属性,那么应该停止调用该MVVM。您可以将graduate命令实现移动到另一个类(比如Helper类)中,并在ViewModels之间共享它(在初始化期间) 错误:将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;
}
}
公共静态类扩展
{
PropertyChanged上的公共静态void(此INotifyPropertyChanged源,PropertyChangedEventHandler h,字符串propertyName)
{
PropertyChangedEventHandler处理程序=h;
if(handler!=null)处理程序(源,新PropertyChangedEventArgs(propertyName));
}
公共静态bool SetProperty(此INotifyPropertyChanged源、PropertyChangedEventHandler处理程序、引用T字段、T值、字符串propertyName)
{
if(EqualityComparer.Default.Equals(字段,值))返回false;
字段=值;
OnPropertyChanged(处理程序,propertyName);
返回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;
}
公共类学生:INotifyPropertyChanged
{
私有字符串_name=“name”;
公共字符串名
{
获取{return\u name;}
设置{
this.SetProperty(PropertyChanged,ref _name,value,“name”);}
}
公共事件属性更改事件处理程序属性更改;
}
公共部分类MyViewModel:INotifyPropertyChanged
{
私立学生=新学生();
公立学生
{
获取{return\u student;}
设置
{
此.SetProperty(PropertyChanged,参考学生,值,“学生”);
}
}
公共事件属性更改事件处理程序属性更改;
}
最后,Xaml:
<TextBlock Text="{Binding Path=Student.Name}"></TextBlock>
首先,在许多情况下,如果您可以在模型上实现
INotifyPropertyChanged
,那么直接从视图绑定到模型是非常好的。它仍然是MVVM。这可以防止ViewModel被大量“中继到模型”代码弄乱。您只在VM中包含视图不能直接使用的内容(需要包装/反规范化/转换数据,或者模型属性不实现INPC,或者您需要另一个验证层…)
也就是说,命令是视图和ViewModel之间通信的主要手段
- 命令可能有多个接收器(可能在不同的ViewModels上)
- Execute/CanExecute模式通常不适合VM上下文之外的环境
- 即使真正的工作是在模型的方法中完成的,命令也可能有一些逻辑,而不仅仅是委托给模型(验证、与其他VM属性/方法的交互…)
- 在测试虚拟机时,如果命令在虚拟机之外,则不能存根命令的行为
如果您担心跨虚拟机的代码重复,可以创建一个
StudentViewModel
,从中继承StudentDetailsViewModel
和StudentListItemViewModel
StudentViewModel
将定义该命令及其常见行为。您能否进一步解释一下为什么将Graduate()放在模型中是错误的?这是我一时兴起的一个人工例子,但我的目标是证明模型层中存在行为,遵循没有行为的模型表现出贫血域模型反模式的原则。首先,我们使用MVVM使代码:->可读-易于理解->易于单元测试->可维护,方法是解耦组件,从而限制代码更改的影响。因此,视图和模型不应直接相互引用。其次,不管福勒怎么想,我不认为ADM是一种反模式。如果您是java或.Net(C#,Wpf)开发人员,那么编写可靠的兼容代码比编写丰富的域模型要好。我不知道
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;
}
<TextBlock Text="{Binding Path=Student.Name}"></TextBlock>