关于c#:WPF-MVVM视图模型优先

WPF - MVVM view model first

我正在构建一个大型WPF应用程序-一种台式机,可承载多个模块,例如通过RS232 /以太网的多个终端窗口,注册分析器,自动化工具等。
我正在使用MVVM体系结构,其中,我的视图(XAML)在其资源部分中实例化了corespondent视图模型。并在视图的数据上下文中设置视图模型。
在这种方法中,视图首先创建。

但是,我首先介绍了另一种称为VM的方法,这意味着视图模型是在视图之前实例化的,并且我理解了背后的理论。我不了解的是何时以及由谁实例化视图以及如何在不与视图耦合的情况下进行实例化。

我很高兴听到您的想法,如果有人可以提供代码示例,那就太好了。

预先感谢。


我在项目中大量使用了MVVM,可以就此发表自己的看法。
在我的项目中,视图从不实例化任何VM。通常,我有某种管理器,负责创建相应的VM。

我将其分配给某些顶级UI控件(例如Window)的datacontext。视图始终由一种样式定义,其中将目标类型设置为视图模型的类型。
启动代码仅创建一个Window和主viewmodel。可以说,VM已分配,其余的由WPF(.net)运行时完成。

所以我有一个很大的样式文件,每个视图模型的所有样式都定义了相应的视图(通常是一个用户控件)。

这是我的工作方式,当然也有其他人。

hth


在WPF / MVVM应用程序中,我将ViewModels与两个构造函数一起使用-一个用于设计时(无参数-直接设置所需组件的模拟版本),另一个用于运行时(所需组件通过IoC作为参数注入)。这允许在Visual Studio设计器中显示(模拟)数据以进行UI测试。

所以简单的情况看起来像...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MainViewModel : ViewModelBase
{
    private IDataFactory _DataFactory;

    public MainViewModel()
    {
        _DataFactory = new DesignTimeMockDataFactory();
        LoadData();
    }

    [PreferredConstructor]
    public MainViewModel(IDataFactory dataFactory)
    { _DataFactory = dataFactory; }

    public void LoadData()
    { DataItems.AddRange(_DataFactory.GetDataItems()); }

    public ExtendedObservableCollection<DataItem> DataItems { get; private set; }
}

设计时间用法可以直接在XAML中设置...

1
2
3
<Window x:Class="MainView"
        d:DataContext="{d:DesignInstance Type=MainViewModel, IsDesignTimeCreatable=True}"
...

运行时ViewModel在View后面的代码中设置...

1
2
3
4
5
6
7
8
public MainView()
{
    InitializeComponent();

    var viewModel = SimpleIoc.Default.GetInstance<MainViewModel>();
    DataContext = viewModel;
    Loaded += (s, e) => viewModel.LoadData();
}

将View的Loaded事件设置为在显示View后调用ViewModel的LoadData()方法来触发数据加载。如果LoadData()缓慢,则可以将其更改为异步方法,以防止UI阻塞。

对于那些抱怨这是一个太紧密耦合的结构的人,我的观点是它们应该是这样。尽管View和ViewModel是单独的实体,但是每个View都确切知道它需要哪种ViewModel类型,并且在项目开发生命周期中不太可能改变。我认为使用Locator类型类隐藏ViewModel构造函数调用是不必要的抽象级别。


当您的应用程序增长时,您通常会遇到这些决定。通常,您同时将ViewViewModel都同时包含在两个元素中,这与先出现的内容无关,而更像是用来实例化两个元素(ViewViewModel)的内容。
对于较大的项目,在需要时,我使用了名为ViewModelResolver的类。它显然具有接口IViewModelResolver,因此可以很好地注入。
它可以返回基于类型的约定或字符串表示形式的ViewModel,并使用反射实例化它。
您还可以传入ViewModel(或类型)并获得与传入的view model匹配的View作为DataContext(查看ViewModel婚姻),或者可以定义实例化ViewViewModel

希望有所帮助

因此,重点是要有一个中间类,其作用类似于某种工厂服务,它会带动将视图和视图模型放到一起并实例化它们。
这为您提供了更大的自由度,也是一个将这些决策直接从ViewModel中分离出来的好地方。


要使视图与视图模型脱钩,还需要进行其他一些操作以实例化视图模型并管理其生命周期和共享。这项工作可能属于IoC容器,或者是简单的手动依赖项注入。这完全取决于您。

例如来自Paul Stovell的文章:

1
2
3
4
5
public CalculatorView(CalculatorViewModel viewModel)
{
    InitializeComponent();
    DataContext = viewModel;
}

这完全取决于您要通过去耦来实现。一个原因可能是您可以在同一个视图模型上拥有多个视图-在这种情况下,无论创建视图如何,都需要创建视图模型。

另一种可能是在不破坏视图的情况下将现有视图的视图模型与另一个视图模型交换出去。在这种情况下,也许您已经有两个现有的视图模型,并根据需要将它们分配给视图的DataContext

1
2
view.DataContext = viewModels[0];
view.DataContext = viewModels[1];