【问题标题】:MVVM View reference to ViewModelMVVM 视图对 ViewModel 的引用
【发布时间】:2010-03-29 16:30:36
【问题描述】:

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

我有几个关于最佳做法的问题。

1) 我知道我不希望我的 VM 知道附加的 View,但是 View 引用其 VM 是否合理?

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

【问题讨论】:

标签: mvvm


【解决方案1】:

1) View 肯定通过 DataContext 引用了 ViewModel。并且您可以在您的视图中投射 DataContext:

public class ShellView : Window 
{
   …
   public ShellViewModel { get { return DataContext as ShellViewModel; } }

这不违反 Model-View-ViewModel 模式。

2) 你是对的。 ViewModel 不应该打开另一个 View。更好的方法是使用控制器。他们负责应用程序的工作流程。

如果您对更详细的信息感兴趣,可以查看 WPF Application Framework (WAF)

【讨论】:

  • +1 为了稍微扩展 jbe 对 #2 的解释,您可以在 VM 上公开一个命令,该命令连接到控制器的命令处理程序,该处理程序将处理显示第二个视图。除了 WAF,您还可以查看 P&P 团队的复合应用程序库(又名 Prism):compositewpf.codeplex.com/Wikipage
  • 是的,显然 View 通过 DataContext 了解 VM,我想我只是没有看到,因为我的 xaml 中的所有数据绑定都非常依赖于 VM,因此具有更强的参考在我的 Views 代码隐藏中,这似乎不是一件坏事。感谢您提供有关控制器的提示,我将研究该模式。
  • public ShellViewModel Model { get { return DataContext as ShellViewModel; } }
  • 有两个非常不同的东西:一个是 View 通过 DataContext 引用 ViewModel。这是正常的和可取的。不希望的(尽管有时需要)是 View 需要知道 ViewModel 类型,就像在铸造 DataContext as SomeVMType 中一样。理想情况下,只有公共 VM properties 的类型(和名称)才能构成 V 和 VM 之间的“通用接口”。
  • @heltonbiker:我更喜欢类型安全。 VS2013 XAML 编辑器在绑定路径表达式中带有 IntelliSense。如果绑定路径包含错误,Resharper 插件会显示其他警告。这只有在 View 知道 ViewModel 的类型时才有效。
【解决方案2】:

1) 下面是 View 的两个简单实践来“了解”一个 ViewModel。 View 了解 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 风格,它不喜欢 View 的代码隐藏中的任何代码。第二个选项也不错,应该对您的任务足够简单。

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

【讨论】:

  • 谢谢,非常感谢您的反馈
【解决方案3】:

1)。视图需要在某种程度上引用视图模型,因为视图模型将充当视图的数据上下文。

2) 处理此问题的一种方法是使用表示对话框的通用视图模型,该视图模型由主视图模型(用作视图数据上下文的视图模型)拥有。

您可以使用命令来创建对话框视图模型的新实例,该实例将在您的资源中定义相应的数据模板。此模板将设置为绑定到 dialogviewmodel 类型。

【讨论】:

  • +1 但我认为对于第 1 点),OP 是在询问 View 类是否可以知道确切的 ViewModel 类,而不是仅仅通过绑定未知类型的数据上下文属性名称。
  • Per Wim,实际上我指的是 View 类具有对 ViewModel 的引用,而不仅仅是通过数据绑定(这是抽象的)。
  • 第 2 点。我正在使用 MVVM Light 工具包,在其中创建视图,然后通过 Locator 服务绑定到 ViewModel。所以我没有创建虚拟机,因为他们不知道如何将视图附加到自己身上。所以看来我需要一种方法来创建对话框视图,应该在哪里发生(视图或虚拟机)?
【解决方案4】:

已经很晚了,但我认为这已经够棘手了,应该有很多不同的观点。


我知道我不希望我的虚拟机知道附加的视图,但是 View 引用其 VM 是否合理?

正如已经回答的那样,正确的 View-ViewModel 安排涉及将 ViewModel 分配为 View 的 DataContext 属性。这允许 DataBindings 从声明性 XAML 中“自动”建立,或通过后面的代码进行微调。

有时,您会很想在后面的代码中编写如下内容:

var dc = DataContext as CleverViewModel;
CleverViewModel.CleverProperty.Add(someValue); // just a simple example

我相信实现这类事情的正确方法不是强制转换 DataContext,而是:

  1. 在 View 中有一些专用控件,例如 ItemsControl,其 ItemsSource 双向数据绑定到 viewmodel 中的某些 属性

    <ItemsSource x:Name="cleverControl" Visibility="Collapsed" ItemsSource="{Binding CleverProperty, Mode=TwoWay}"/>

  2. 在后面的代码中转换绑定的属性而不是整个 ViewModel:

    var collection = (ObservableCollection<double>)cleverControl.ItemsSource; collection.Add(someValue);

注意重要的区别:本例中的第二种方法不需要 View 知道 ViewModel 类型,它只需要一个名为 CleverProperty 类型为 ObservableCollection&lt;double&gt; 的属性。这让我可以拥有多态甚至鸭子类型的 ViewModel。


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

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

  1. 视图中的控件可以向 ViewModel 发送一个命令,这反过来会更新它自己的一些属性,这些属性将被视图反映。

  2. 一个视图可以包含另一个视图,在视图模型的知识之外。在这种情况下,后面的代码可以操作包含视图的数据上下文。

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

【讨论】:

    【解决方案5】:

    Build Your Own MVVM Framework

    我发现 Rob Eisenberg 提出的方法非常有趣。

    关键点:

    1. 约定优于配置
    2. ViewModel 优先

    这与 ASP.NET MVC 理念非常相似。

    我强烈推荐观看视频。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-27
      • 1970-01-01
      • 2011-01-31
      • 2010-11-27
      • 2010-12-01
      • 1970-01-01
      相关资源
      最近更新 更多