Mvvm 向域模型对象添加ICommand是否合适?

Mvvm 向域模型对象添加ICommand是否合适?,mvvm,Mvvm,我正在为Windows应用商店应用程序应用MVVM模式(并在过程中学习) 现在我倾向于在视图和ViewModel之间建立1:1的对应关系,其中多个ViewModel依赖于同一基础模型 例如,假设我有一个实体“Student”。我有两种方式来查看学生:全屏详细信息页面或教室中的学生列表。这将产生以下视图/视图模型对: 学生详细信息视图/学生详细信息视图模型 StudentListItemView/StudentListItemViewModel 目前,我假设我的ViewModel将直接公开该模

我正在为Windows应用商店应用程序应用MVVM模式(并在过程中学习)

现在我倾向于在视图和ViewModel之间建立1:1的对应关系,其中多个ViewModel依赖于同一基础模型

例如,假设我有一个实体“Student”。我有两种方式来查看学生:全屏详细信息页面或教室中的学生列表。这将产生以下视图/视图模型对:

  • 学生详细信息视图/学生详细信息视图模型
  • StudentListItemView/StudentListItemViewModel
目前,我假设我的ViewModel将直接公开该模型,我的Xaml将绑定到ViewModel.Model.property-name(我意识到这是有争议的)

假设我可以从任何一个角度对学生执行一些操作(例如,“毕业生”)。我希望在我的模型中有毕业生行为(以避免贫血的领域模型),并且我希望避免在依赖同一模型的ViewModels之间重复行为

我的目的是在视图中创建一个可以绑定Graduate按钮的ICommand(例如RelayCommand)。我的问题是:

是否有任何理由不将ICommand作为模型类的属性?

基本上,这意味着如下内容(忽略对存储库的需求):

这样,StudentDetailsView和StudentListItemsView都可以拥有绑定到该命令的Xaml(其中DataContext是StudentViewModel,Model是公共属性):



显然,我可以将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>