【问题标题】:How does a View know what ViewModel to use in WPF?View 如何知道在 WPF 中使用什么 ViewModel?
【发布时间】:2012-06-21 15:37:06
【问题描述】:

有人能解释一下 View 和 ViewModel 是如何连接的吗?我在任何地方都找不到引用 ViewModel 的 View 的 xaml 或 xaml.cs,也找不到引用 View 的 ViewModel.cs 文件中的任何内容,但它们以某种方式连接,并且将 ViewModel 中的成员绑定到 View 工作.

另外,在每个的构造函数中,只有 View 的 InitializeComponent 和 ViewModel 的基本构造函数(没有 View 的声明/定义)。

谢谢!

【问题讨论】:

  • 由于不清楚您是否使用第三方 MVVM 框架,我怀疑您需要发布一些代码才能获得准确的答案。您可以以数十种不同方式中的任何一种连接视图和视图模型,而我们看不到您面前的代码。
  • 我建议回归基础——我写了一个关于 MVVM 的系列文章,它真正涵盖了一些基础知识,比如“什么是视图模型”和“什么是视图”以及它们如何结合在一起。可能值得略读:reedcopsey.com/series/windows-forms-to-mvvm

标签: c# wpf mvvm


【解决方案1】:

这里有多种选择。

Something 必须将 View 的 DataContext 设置为 ViewModel 的实例。这里有很多选择:

  • 这可以直接在 xaml 中完成(View 只是直接实例化 ViewModel)。
  • 这可以在 View 的构造函数中完成 (this.DataContext = new MyViewModel();)
  • 这可以通过DataTemplate 处理
  • “协调”类可以将它们连接在一起(即:单独的“演示者”类可以构造两者并适当地设置 DataContext

最常见的是让 View 在 xaml 中定义 VM(View-first),或者让所有内容都基于以 ViewModel 为中心的观点,并让 WPF 基于绑定的 VM 自动创建 View (ViewModel 优先)。

前一种方法被很多工具包使用,例如MVVM Light。后一种方法是我在MVVM blog series 中使用的,并被其他一些工具包使用。

【讨论】:

  • +1。这个“协调”类通常称为Controller(MVC 模式)或Presenter(MVP 模式),其中演示者同时充当控制器和视图模型。走向 MVP 意味着ViewModel 将打开视图。
  • @OlivierJacot-Descombes 除非您使用数据模板 - 这样 VM 确实需要打开视图。
  • @DannyVarod 如果您使用数据模板,WPF 将有效地成为 MVP 术语中的“演示者”,为您协调。
  • 没错,但是,它变成了一个基于反射的通用 Presenter。
  • @ReedCopsey 谢谢!我发现它在哪里,它被嵌入到某个地方,该地方调用了一些设置 DataContext 的东西。 (虽然我不会这样做......) ViewModel 被创建,然后在 EventHandler 中定义为 View 的 DataContext。 :S
【解决方案2】:

将视图连接到视图模型的“干净”方式是......

当您创建视图时,对于每个视图,将其 DataSource 设置为其视图模型:

例如

public class App
{
    private void OnAppStart()
    {
        var model = new MainModel();
        var vm = new MainVM();
        var view = new MainWindow();

        vm.Model = model;
        view.DataSource = vm;

        view.Show();
    }
}

当您查看的模型发生变化时,更新虚拟机:

public class MainVM
{
    private void OnSelectedModelItemChanged()
    {
        this.SelectedItem = new ItemVM();
        this.SelectedItem.Model = this.SelectedModelItem;
    }
}

并使用数据模板使视图为每个虚拟机选择正确的子视图。

【讨论】:

    【解决方案3】:

    视图在 xaml 中包含视图模型类的对象。

    InitializeComponent 函数创建页面上的所有控件,设置样式等

    【讨论】:

    • 它会在哪里?我找不到任何包含“ViewModel”一词的文本(这就是我的视图模型的名称)。
    • 您可能想查看 Reed 的回复。我这样做的方式是在 View 的 xaml 中有一个我的 viewmodel 的对象。
    【解决方案4】:

    正如其他人已经表明的那样,有多种选择。当然,每当您听到多种选择时,您都必须想知道每种选择的优点和缺点是什么。好吧,事实证明,除了一个之外,它们都有很大的缺点。

    以下方法不涉及外部库,不涉及额外的管理类和接口,几乎没有魔法,并且非常灵活,因为您可以拥有包含其他视图模型的视图模型,并且您可以实例化它们中的每一个,因此您可以通过构造函数参数。

    对于主窗口的viewmodel:

    using Wpf = System.Windows;
    
    public partial class TestApp : Wpf.Application
    {
        protected override void OnStartup( Wpf.StartupEventArgs e )
        {
            base.OnStartup( e );
            MainWindow = new MainView();
            MainWindow.DataContext = new MainViewModel( e.Args );
            MainWindow.Show();
        }
    }
    

    对于所有其他视图模型:

    这是在 MainViewModel.cs 中:

    using Collections = System.Collections.Generic;
    
    public class MainViewModel
    {
        public SomeViewModel SomeViewModel { get; }
        public OtherViewModel OtherViewModel { get; }
        public Collections.IReadOnlyList<string> Arguments { get; }
        
        public MainViewModel( Collections.IReadOnlyList<string> arguments )
        {
            Arguments = arguments;
            SomeViewModel = new SomeViewModel( this );
            OtherViewModel = new OtherViewModel( this );
        }
    }
    

    MainView.xaml 中的这个:

    [...]
    xmlns:local="clr-namespace:the-namespace-of-my-wpf-stuff"
    [...]
        <local:SomeView DataContext="{Binding SomeViewModel}" />
        <local:OtherView DataContext="{Binding OtherViewModel}" />
    [...]
    

    如您所见,一个视图模型可以简单地成为另一个视图模型的成员(子);在这种情况下,SomeViewModel 和 OtherViewModel 是 MainViewModel 的子项。然后,在 MainView 的 XAML 文件中,您可以只实例化每个子视图,并通过Binding 将它们的DataContext 指定给相应的子视图模型。

    【讨论】:

    • 非常感谢 Mike,这正是我正在寻找的方法。我知道这已经有一年了,但如果你看到这个,你会将两个本地元素放在 MainView.xaml 中的哪个位置?
    • @dmscs 这些元素实例化了视图,因此您只需将它们放置在您希望视图出现的位置。例如,您可以让主视图只包含一个 StackPanel,并使 StackPanel 只包含这两个视图,一个接一个。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-25
    • 1970-01-01
    • 2015-05-19
    • 2019-05-29
    • 1970-01-01
    • 2012-06-22
    • 1970-01-01
    相关资源
    最近更新 更多