【问题标题】:MVVM pattern for WPF: Model vs ViewModelWPF 的 MVVM 模式:模型与视图模型
【发布时间】:2018-07-16 13:01:15
【问题描述】:

我无法真正解决以下问题:

我在应用程序中只有一个textbox 用于用户输入,一个button 用于对该输入执行后台计算,以及一个textblock。想象一下,我必须使用 MVVM,所以我有 viewviewmodelmodel 类。

我将视图中的控件(textboxbuttontextblock)绑定到相应属性和命令上的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()

}

【问题讨论】:

  • 也许这篇文章会对你有所帮助:stackoverflow.com/questions/23648832/…
  • Model 是您的数据库表的表示形式,因此您的 FancyMethod 需要在您的模型类之外。将其移至您的 ViewModel 类,并在您单击按钮或您想要的任何操作/事件时调用它。
  • FancyMethod 真的会产生值吗?
  • 它从输入计算值,然后将计算值分配给输出

标签: c# mvvm


【解决方案1】:

如果你想保持一个干净的层模型,你不应该在你的ViewModel 中包含public Model model {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;}
 }

注意:还涉及到一些额外的技术:

  • 使用inversion of control container,并通过服务 通过构造函数。
  • 通过一个抽象的服务 接口(ISomeService)
  • 可能有一些 automapper 来隔离您与模型/视图模型之间的映射

“那么,为什么要让它变得如此‘复杂’?你只是在复制。”,一个常见的反对这种模式的论点:

嗯:

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

【讨论】:

  • 请注意,每次访问 getter 时都会创建一个新的 RelayCommand 实例,而它可能只是一个私有字段(只读或在首次访问时创建一次)。
  • 添加然后,一旦你走得更远一点,你就可以使用依赖注入框架(即 Ninject)将 SomeService 注入 ViewModel。
  • @blins:感谢提醒,我修改了代码。
【解决方案2】:

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

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

public class Model
{
    public string FancyMethod(string input)
    {
        // Use input to calculate output
    }
}

然后您可以将模型注入您的视图模型,并在用户通过单击视图中的Button 执行命令时调用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 接口并向视图发出更改通知。

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

【讨论】:

    【解决方案3】:

    我认为没有必要在另一个类中超越 InputOutput 属性。原因是属性反映了视图的输入和输出。所以他们必须在视图模型中。 您可以在服务类中外包SomeFancyMethod 以将逻辑与类似于 mvc 的视图模型分开。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-07-14
      • 1970-01-01
      • 1970-01-01
      • 2020-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-07
      相关资源
      最近更新 更多