C# WPF的MVVM模式:模型与视图模型

C# WPF的MVVM模式:模型与视图模型,c#,mvvm,C#,Mvvm,我真的无法完全理解以下问题: 我在应用程序中只拥有一个用于用户输入的文本框,一个用于对该输入执行背景计算的按钮,以及一个文本块。假设我必须使用MVVM,所以我有视图、视图模型和模型类 我将视图中的控件(textbox、按钮和textblock)绑定到相应属性和命令的viewmodel。但是,我不确定viewmodel功能应该在哪里结束。例如,以下是构造应用程序的一种方法吗 型号: public class Model { public string Input { get; set;

我真的无法完全理解以下问题:

我在应用程序中只拥有一个用于用户输入的
文本框
,一个用于对该输入执行背景计算的
按钮
,以及一个
文本块
。假设我必须使用MVVM,所以我有
视图
视图模型
模型

我将视图中的控件(
textbox
按钮
textblock
)绑定到相应属性和命令的
viewmodel
。但是,我不确定
viewmodel
功能应该在哪里结束。例如,以下是构造应用程序的一种方法吗

型号:

public class Model
{

    public string Input { get; set; }
    public string Output { get; set; }

    public void FancyMethod ()
    {
       // Use input to calculate output
    }

}
视图模型:

public class ViewModel
{

    public string Input {get; set;}
    public string Output {get; set;}
    public ICommand command {get; set;}
    public Model model {get; set;}

    public ViewModel() 
    {
      model = new Model();
    }

    // When the button is pressed, model.input = Input and then execute model.FancyMethod()

}

如果希望保持一个干净的图层模型,则不应在
ViewModel
中包含
publicmodel模型{get;set;}

例如,如果您有一个针对某个业务模型的命令,那么您的结构应该如下所示:

//you don't have this one... but well, maybe other cases have
public class SomeService : ISomeService
{
    //member of ISomeService
    public void SomeFancyMethod(Model model)
    {
        //do stuff..
    }
}

public class Model //might be database, or domain model.
{
   public string Input { get; set; }
   public string Output { get; set; }
}
public class ViewModel
{
    private ISomeService _someService;

    //note: someService is passed through a IoC service like ninject, unity, autofac etc.
    public ViewModel(ISomeService someService)
    {
        _someService = someService;
        //initialize the command:
        command = new RelayCommand(() =>
        {    
            _someService .SomeFancyMethod(new Model()
            {
                //properties could be mapped with an automapper.
            });
        });
    }

    public ICommand command {get; private set;}
    public string Input {get; set;}
    public string Output {get; set;}
 }
对于您的viewmodel,它将变成如下所示:

//you don't have this one... but well, maybe other cases have
public class SomeService : ISomeService
{
    //member of ISomeService
    public void SomeFancyMethod(Model model)
    {
        //do stuff..
    }
}

public class Model //might be database, or domain model.
{
   public string Input { get; set; }
   public string Output { get; set; }
}
public class ViewModel
{
    private ISomeService _someService;

    //note: someService is passed through a IoC service like ninject, unity, autofac etc.
    public ViewModel(ISomeService someService)
    {
        _someService = someService;
        //initialize the command:
        command = new RelayCommand(() =>
        {    
            _someService .SomeFancyMethod(new Model()
            {
                //properties could be mapped with an automapper.
            });
        });
    }

    public ICommand command {get; private set;}
    public string Input {get; set;}
    public string Output {get; set;}
 }
注:还涉及一些其他技术:

  • 使用,并通过服务 通过构造函数
  • 通过一个 接口(服务)
  • 可能需要一些工具来隔离与模型/视图模型之间的映射

“那么,为什么要把这件事搞得这么‘复杂’?你只是在复制而已。”这是一个反对这种模式的常见论点:

嗯:

  • 这并不复杂
  • 这样做将分离您的层。这意味着数据层中的更改不会破坏您的视图。从长远来看,您将从中受益,因为将发生变化,您需要维护代码

  • 如果希望保持一个干净的图层模型,则不应在
    ViewModel
    中包含
    publicmodel模型{get;set;}

    例如,如果您有一个针对某个业务模型的命令,那么您的结构应该如下所示:

    //you don't have this one... but well, maybe other cases have
    public class SomeService : ISomeService
    {
        //member of ISomeService
        public void SomeFancyMethod(Model model)
        {
            //do stuff..
        }
    }
    
    public class Model //might be database, or domain model.
    {
       public string Input { get; set; }
       public string Output { get; set; }
    }
    
    public class ViewModel
    {
        private ISomeService _someService;
    
        //note: someService is passed through a IoC service like ninject, unity, autofac etc.
        public ViewModel(ISomeService someService)
        {
            _someService = someService;
            //initialize the command:
            command = new RelayCommand(() =>
            {    
                _someService .SomeFancyMethod(new Model()
                {
                    //properties could be mapped with an automapper.
                });
            });
        }
    
        public ICommand command {get; private set;}
        public string Input {get; set;}
        public string Output {get; set;}
     }
    
    对于您的viewmodel,它将变成如下所示:

    //you don't have this one... but well, maybe other cases have
    public class SomeService : ISomeService
    {
        //member of ISomeService
        public void SomeFancyMethod(Model model)
        {
            //do stuff..
        }
    }
    
    public class Model //might be database, or domain model.
    {
       public string Input { get; set; }
       public string Output { get; set; }
    }
    
    public class ViewModel
    {
        private ISomeService _someService;
    
        //note: someService is passed through a IoC service like ninject, unity, autofac etc.
        public ViewModel(ISomeService someService)
        {
            _someService = someService;
            //initialize the command:
            command = new RelayCommand(() =>
            {    
                _someService .SomeFancyMethod(new Model()
                {
                    //properties could be mapped with an automapper.
                });
            });
        }
    
        public ICommand command {get; private set;}
        public string Input {get; set;}
        public string Output {get; set;}
     }
    
    注:还涉及一些其他技术:

    • 使用,并通过服务 通过构造函数
    • 通过一个 接口(服务)
    • 可能需要一些工具来隔离与模型/视图模型之间的映射

    “那么,为什么要把这件事搞得这么‘复杂’?你只是在复制而已。”这是一个反对这种模式的常见论点:

    嗯:

  • 这并不复杂
  • 这样做将分离您的层。这意味着数据层中的更改不会破坏您的视图。从长远来看,您将从中受益,因为将发生变化,您需要维护代码

  • 我认为没有必要将
    输入
    输出
    属性交给另一个类。原因是这些属性反映了视图的输入和输出。因此,它们必须位于viewmodel中。
    您可以在服务类中放弃
    SomeFancyMethod
    ,以将逻辑从viewmodel anlogous分离到mvc。

    我认为没有必要在另一个类中放弃
    输入
    输出
    属性。原因是这些属性反映了视图的输入和输出。因此,它们必须位于viewmodel中。
    您可以在服务类中取消
    SomeFancyMethod
    ,将逻辑从viewmodel和Logous分离到mvc。

    我猜
    FancyMethod()
    包含您的业务逻辑,并生成您希望在视图中显示的值。在这种情况下,
    FancyMethod()
    属于您的模型,因为它包含一些相同的业务逻辑,而不管它是在客户端应用程序的上下文中执行还是在其他组件的上下文中执行

    因此,您的模型看起来像这样,即它接受输入并生成输出,但不公开视图可能绑定到的任何属性:

    public class Model
    {
        public string FancyMethod(string input)
        {
            // Use input to calculate output
        }
    }
    
    然后,当用户通过单击视图中的
    按钮执行命令时,您可以向视图模型注入模型并调用
    FancyMethod

    public class ViewModel
    {
        private readonly Model _model;
        public ViewModel(Model model)
        {
            _model = model;
            command = new RelayCommand(Execute, CanExecute);
        }
    
        public string Input { get; set; }
        public string Output { get; set; }
        public ICommand command { get; private set; }
    
        private bool CanExecute(object _)
        {
            return !string.IsNullOrEmpty(Input);
        }
        private void Execute(object _)
        {
            Output = _model.FancyMethod(Input);
        }
    }
    
    显然,视图模型类还应该实现
    INotifyPropertyChanged
    接口,并向视图发出更改通知


    简而言之,业务逻辑属于模型,而应用程序逻辑(例如,当用户单击
    按钮时发生的情况)属于视图模型。

    我猜
    FancyMethod()
    包含您的业务逻辑,并生成您希望在视图中显示的值。在这种情况下,
    FancyMethod()
    属于您的模型,因为它包含一些相同的业务逻辑,而不管它是在客户端应用程序的上下文中执行还是在其他组件的上下文中执行

    因此,您的模型看起来像这样,即它接受输入并生成输出,但不公开视图可能绑定到的任何属性:

    public class Model
    {
        public string FancyMethod(string input)
        {
            // Use input to calculate output
        }
    }
    
    然后,当用户通过单击视图中的
    按钮执行命令时,您可以向视图模型注入模型并调用
    FancyMethod

    public class ViewModel
    {
        private readonly Model _model;
        public ViewModel(Model model)
        {
            _model = model;
            command = new RelayCommand(Execute, CanExecute);
        }
    
        public string Input { get; set; }
        public string Output { get; set; }
        public ICommand command { get; private set; }
    
        private bool CanExecute(object _)
        {
            return !string.IsNullOrEmpty(Input);
        }
        private void Execute(object _)
        {
            Output = _model.FancyMethod(Input);
        }
    }
    
    显然,视图模型类还应该实现
    INotifyPropertyChanged
    接口,并向视图发出更改通知


    简而言之,业务逻辑属于模型,而应用程序逻辑(例如,当用户单击
    按钮时发生的事情)属于视图模型。

    也许这篇文章会帮助您:模型是数据库表的表示形式,因此您的
    FancyMethod
    需要在模型类之外。将其移动到ViewModel类,并在单击按钮或所需的任何操作/事件时调用它。FancyMethod是否实际生成值?它根据输入计算值,然后将计算出的值分配给OutputThis post