MV-VM - 在ViewModel中使用命令的任何示例?

Mic*_*cah 16 wpf mvvm

我一直在使用我的MV-VM开发一个非常大的LOB应用程序,我称之为MV-MC(Model-View-ModelController),它是MVC和MV-VM之间的一种组合.我已经发布了这个答案,关于如何在MV-VM中实例化视图,问题是" 什么是最常见的错误 - 在wpf开发中制造 ".

Sam就我的回答发表了以下评论:

这会产生一个后续问题:如何创建视图?我使用RelayCommands将视图中的操作绑定到ViewModel,因此视图甚至不知道某个操作已被触发,也不知道他应该打开一个新视图.解决方案:在VM中创建一个事件以供View订阅?

当我最初开始MV-VM开发时,我认为一切都应该存在于ViewModel中,并且已经研究了很多像Josh SmithKarl Shifflett这样的人的例子.但是,我还没有提出一个很好的例子,说明命令何时需要存在于ViewModel中.

例如,假设我有一个显示客户的ListView,以及我点击的按钮,允许我编辑当前选定的客户.ListView(View)绑定到CustomerVM(ViewModel).单击该按钮将触发EditCustomerCommand,这将打开一个弹出窗口,允许我编辑CustomerVM的所有属性.这个EditCustomerCommand在哪里?如果它涉及打开一个窗口(UI功能),它不应该在视图的代码隐藏中定义吗? 替代文字

有没有人有任何关于何时应该在View和ViewModel中定义命令的例子?

Matthew Wright在下面说:

从列表中删除和删除将是很好的例子.在这些情况下,会添加空白记录或ViewModel删除当前记录.视图采取的任何操作都应该响应发生的事件.

所以,如果我点击新按钮,会发生什么?Customer ViewModel创建了一个CustomerVM的新实例并添加到它的集合中吗?那么我的编辑屏幕怎么会打开呢?该视图应该创建Customer ViewModel的新实例,并将其传递给ParentVM.Add(newlyCreatedVM)方法吗?

假设我通过VM上的DeleteCommand删除客户记录.VM调用业务层并尝试删除记录.它不能这样它会向VM返回一条消息.我想在对话框中显示此消息.视图如何从命令操作中获取消息?

Sam*_*Sam 3

从来没有想过我会看到自己被引用在一个问题中。

我自己思考了这个问题一段时间,并为我的代码库做出了一个相当务实的决定:

在我的代码库中,当操作发生时,ViewModel 就会被调用,我希望它保持这种状态。另外,我不希望 ViewModel 控制视图。

我做了什么?
我添加了一个导航控制器:

public interface INavigation
{
  void NewContent(ViewModel viewmodel);
  void NewWindow(ViewModel viewmodel);
}
Run Code Online (Sandbox Code Playgroud)

该控制器确实包含两个操作:NewContent() 在当前窗口中显示新内容,NewWindow() 创建一个新窗口,用内容填充它并显示它。
当然,我的视图模型不知道要显示哪个视图。但他们确实知道他们想要显示哪个视图模型,因此根据您的示例,当执行 DeleteCommand 时,它将调用导航服务函数NewWindow(new ValidateCustomerDeletedViewModel())来显示一个窗口,说明“客户已被删除”(对于这个简单的消息框,但是对于简单的消息框来说,很容易有一个特殊的导航器功能)。

viewmodel如何获取导航服务?

我的视图模型类有一个导航控制器的属性:

public class ViewModel
{
  public INavigation Navigator { get; set; }
  [...]
}
Run Code Online (Sandbox Code Playgroud)

当视图模型附加到窗口(或显示视图的任何内容)时,窗口将设置 Navigator 属性,以便视图模型可以调用它。

导航器如何创建视图模型的视图?

您可以有一个简单的列表,为哪个视图模型创建哪个视图,在我的例子中,我可以使用简单的反射,因为名称是匹配的:

public static FrameworkElement CreateView(ViewModel viewmodel)
{
  Type vmt = viewmodel.GetType();
  // big bad dirty hack to get the name of the view, but it works *cough*
  Type vt = Type.GetType(vmt.AssemblyQualifiedName.Replace("ViewModel, ", "View, ")); 
  return (FrameworkElement)Activator.CreateInstance(vt, viewmodel);
}
Run Code Online (Sandbox Code Playgroud)

当然,视图需要一个接受视图模型作为参数的构造函数:

public partial class ValidateCustomerDeletedView : UserControl
{
  public ValidateCustomerDeletedView(ValidateCustomerDeletedViewModel dac)
  {
    InitializeComponent();
    this.DataContext = dac;
  }
}
Run Code Online (Sandbox Code Playgroud)

我的窗户是什么样子的?

很简单:我的主窗口确实实现了 INavigation 接口,并在创建时显示一个起始页。你自己看:

public partial class MainWindow : Window, INavigation
{
  public MainWindow()
  {
    InitializeComponent();
    NewContent(new StartPageViewModel());
  }

  public MainWindow(ViewModel newcontrol)
  {
    InitializeComponent();
    NewContent(newcontrol);
  }

  #region INavigation Member
  public void NewContent(ViewModel newviewmodel)
  {
    newviewmodel.Navigator = this;
    FrameworkElement ui = App.CreateView(newviewmodel);
    this.Content = ui;
    this.DataContext = ui.DataContext;
  }

  public void NewWindow(ViewModel viewModel)
  {
    MainWindow newwindow = new MainWindow(viewModel);
    newwindow.Show();
  }
  #endregion
}
Run Code Online (Sandbox Code Playgroud)

(这对于导航窗口和将视图包装到页面中同样有效)

当然,这是可测试的,因为可以轻松模拟导航控制器。

我不太确定这是否是一个完美的解决方案,但它现在对我来说效果很好。有什么想法和意见欢迎留言!