对ViewModel的MVVM视图引用

对ViewModel的MVVM视图引用,mvvm,Mvvm,我正在WPF应用程序中使用MVVM。我对这两个都很陌生。让我声明,我不是MVVM模式中的纯粹主义者,我正在尝试使用尽可能多的最佳实践,但我正在尝试做出我认为合理的妥协,以使其在我们的环境中工作。例如,我不想在我的视图中实现0%的代码代码代码隐藏 我有几个关于最佳实践的问题 1) 我知道我不想让我的虚拟机知道附加的视图,但是视图引用其虚拟机是否合理 2) 如果视图中的控件打开了另一个视图(如对话框),我应该在视图中处理这个问题吗?在虚拟机中处理它似乎是错误的,因为虚拟机对特定视图有一些了解。1)。

我正在WPF应用程序中使用MVVM。我对这两个都很陌生。让我声明,我不是MVVM模式中的纯粹主义者,我正在尝试使用尽可能多的最佳实践,但我正在尝试做出我认为合理的妥协,以使其在我们的环境中工作。例如,我不想在我的视图中实现0%的代码代码代码隐藏

我有几个关于最佳实践的问题

1) 我知道我不想让我的虚拟机知道附加的视图,但是视图引用其虚拟机是否合理

2) 如果视图中的控件打开了另一个视图(如对话框),我应该在视图中处理这个问题吗?在虚拟机中处理它似乎是错误的,因为虚拟机对特定视图有一些了解。

1)。视图需要在某个级别上引用视图模型,因为视图模型将充当视图的datacontext

2) 处理此问题的一种方法是使用表示对话框的通用viewmodel,该对话框由主viewmodel(用作views datacontext的viewmodel)拥有

您可以使用命令包装对话框viewmodel的新实例,该实例将在资源中定义相应的datatemplate。此模板将被设置为绑定到dialogviewmodel类型。

1)以下是视图“了解”ViewModel的两个简单实践。视图了解ViewModel(用于数据绑定)是合理的,但在您的案例中可能不需要它。看看这些方法是否有助于解决您的问题。还有其他方法,但这些方法应该足够简单:

public View(ViewModel vm)
{
     View.DataContext = vm;
}

public Bootstrapper(View v, ViewModel vm)
{
     v.DataContext = vm;
     //or, if you want it to have no parameters
     View v = new View();
     ViewModel vm = new ViewModel();
     v.DataContext = vm;
}
如果您有一个服务定位工具,那么第一个选项也不错,但是有一种MVVM的味道,它不喜欢视图的代码隐藏中的任何代码。第二个选项也不错,对于您的任务来说应该足够简单

2.)这个问题可能是MVVM设计中的一个难点。如果我们谈论的是一个普通的Win32 MessageBox,我通常会将该逻辑分离成一个附加对象,并将其放入VM中。这种方式更为清晰。(例如,我在列表框中选择了一个项目,我在该操作中附加了一个删除ICommand,在我的ViewModel中,当执行此ICommand时,我会戳我的MessageBoxObject以询问用户是否“真的想删除”此项目)。更高级的“对话框”将为这些视图模型使用额外的视图模型和数据模板。我更喜欢这种方法。

1)视图通过DataContext明确引用了ViewModel。并且您可以在视图中强制转换DataContext:

public class ShellView : Window 
{
   …
   public ShellViewModel { get { return DataContext as ShellViewModel; } }
这与模型-视图-模型模式没有冲突

2) 你说得对。ViewModel不应打开其他视图。更好的方法是使用控制器。他们负责应用程序的工作流

如果您对更详细的信息感兴趣,那么您可以查看

我发现Rob Eisenberg建议的方法非常有趣

要点:

  • 约定优先于配置
  • 视图模型优先
  • 这与ASP.NETMVC的理念非常相似


    我强烈建议你看这段视频。

    很晚了,但我认为这很棘手,应该有很多不同的视角


    我知道我不想让我的虚拟机知道附加的视图,但是 视图引用其VM是否合理

    如前所述,正确的视图ViewModel排列需要将ViewModel指定为视图的DataContext属性。这允许从声明性XAML“自动”建立数据绑定,或者通过代码隐藏进行微调

    有时,您可能会在代码隐藏中编写以下内容:

    var dc = DataContext as CleverViewModel;
    CleverViewModel.CleverProperty.Add(someValue); // just a simple example
    
    我认为实现这类事情的正确方法不是投射数据上下文,而是:

  • 在视图中有一些专用控件,例如ItemsControl及其ItemsSource双向数据绑定到viewmodel中的某些属性:

  • 在代码隐藏中强制转换绑定属性而不是整个ViewModel:

    var collection=(observetecollection)cleverControl.ItemsSource;
    收藏。添加(someValue)

  • 请注意重要的区别:本例中的第二种方法不要求视图知道ViewModel类型,它只需要类型为
    ObservableCollection
    的名为
    CleverProperty
    的属性。这使我能够拥有多态甚至鸭子类型的视图模型


    如果视图中的控件打开另一个视图(如对话框),我应该 在视图中处理此问题?在VM中处理它似乎是错误的,因为 然后VM对特定视图有一些了解

    这不应该发生在严格的MVVM中,并且不难避免使用数据模板。DataTemplates将给定类型的DataContext映射到给定类型的视图,因此只要ContentControl的DataContext更改,其显示也会更改,前提是您具有该类型的DataTemplate:

  • 视图中的控件可以向ViewModel发送命令,ViewModel将更新其自身的一些属性,这些属性将由视图反映

  • 一个视图可以包含另一个视图,不在ViewModel的知识范围内。在这种情况下,代码隐藏可以操作包含视图的datacontext


  • 还有更多微妙之处,但我一直在使用这种方法,并取得了良好的效果。希望这对某人有所帮助。

    +1但我认为对于第1点),OP询问View类是否可以了解确切的ViewModel类,而不仅仅是通过属性名绑定未知类型的datacontext。根据Wim,实际上我指的是View类引用ViewModel,而不仅仅是通过数据绑定(这是抽象的)。关于第2点,我使用MVVM Light toolkit创建视图,然后通过定位器服务绑定到ViewModel。所以我不是cre