多读书多实践,勤思考善领悟

使用 Prism Unity 实现 .NET Core WPF MVVM 之一 数据绑定

本文于1465天之前发表,文中内容可能已经过时。

一.安装Prism

1.使用程序包管理控制台

1
Install-Package Prism.Unity

2.使用管理解决方案的Nuget包

为啥安装prism会跟Prism.Unity有关系,我们知道Unity是个IOC容器,而Prism本身就支持IOC,且目前官方支持几种IOC容器:

img

1.且unity由于是微软官方的,且支持prism的组件化,由此我推荐使用prism.unity,在官方文档中prism7不支持prism.Mef,Prism 7.1将不支持prism.Autofac

2.安装完prism.unity就已经包含着所有prism的核心库了,架构如下:

img

二.实现数据绑定

我们先创建Views文件夹和ViewModels文件夹,将MainWindow放在Views文件夹下,再在ViewModels文件夹下面创建MainWindowViewModel类,如下:

img

xmal代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<Window x:Class="PrismSample.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prism="http://prismlibrary.com/"
xmlns:local="clr-namespace:PrismSample"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800" prism:ViewModelLocator.AutoWireViewModel="True">
<StackPanel>
<TextBox Text="{Binding Text}" Margin="10" Height="100" FontSize="50" Foreground="Black" BorderBrush="Black"/>
<Button Height="100" Width="300" Content="Click Me" FontSize="50" Command="{Binding ClickCommnd}"/>
</StackPanel>
</Window>

ViewModel代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using Prism.Commands;
using Prism.Mvvm;

namespace PrismSample.ViewModels
{
public class MainWindowViewModel:BindableBase
{
private string _text;
public string Text
{
get { return _text; }
set { SetProperty(ref _text, value); }
}

private DelegateCommand _clickCommnd;
public DelegateCommand ClickCommnd =>
_clickCommnd ?? (_clickCommnd = new DelegateCommand(ExecuteClickCommnd));

void ExecuteClickCommnd()
{
this.Text = "Click Me!";
}

public MainWindowViewModel()
{
this.Text = "Hello Prism!";
}
}
}

启动程序

可以看到,我们已经成功的用prism实现数据绑定了,且View和ViewModel完美的前后端分离

但是现在我们又引出了另外一个问题,当我们不想按照prism的规定硬要将View和ViewModel放在Views和ViewModels里面,又或许自己的项目取名规则各不相同怎么办,这时候就要用到另外几种方法:

1.更改命名规则

如果,公司命名规则很变态,导致项目结构变成这样(这种公司辞职了算了):

img

首先我们在App需要引入prism,修改‘Application’为‘prism:PrismApplication’且删除StartupUri

xmal代码如下:

1
2
3
4
5
6
7
8
9
<prism:PrismApplication x:Class="PrismSample.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
xmlns:local="clr-namespace:PrismSample">
<Application.Resources>

</Application.Resources>
</prism:PrismApplication>

cs后台代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
using Prism.Unity;
using Prism.Ioc;
using Prism.Mvvm;
using System.Windows;
using PrismSample.Viewsb;
using System;
using System.Reflection;

namespace PrismSample
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : PrismApplication
{
//设置启动起始页
protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();
}

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{

}

//配置规则
protected override void ConfigureViewModelLocator()
{
base.ConfigureViewModelLocator();
ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver((viewType) =>
{
var viewName = viewType.FullName.Replace(".Viewsb.", ".ViewModelsa.OhMyGod.");
var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
var viewModelName = $"{viewName}Test, {viewAssemblyName}";
return Type.GetType(viewModelName);
});
}
}
}

上面这两句是关键:

“.Viewsb.” 表示View所在文件夹namespace,”.ViewModelsa.OhMyGod.” 表示ViewModel所在namespace

1
var viewName = viewType.FullName.Replace(".Viewsb.", ".ViewModelsa.OhMyGod.");

Test表示ViewModel后缀

1
var viewModelName = $"{viewName}Test, {viewAssemblyName}";

2.自定义ViewModel注册

我们新建一个Foo类作为自定义类,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using Prism.Commands;
using Prism.Mvvm;

namespace PrismSample
{
public class Foo:BindableBase
{

private string _text;
public string Text
{
get { return _text; }
set { SetProperty(ref _text, value); }
}

public Foo()
{
this.Text = "Foo";
}

private DelegateCommand _clickCommnd;
public DelegateCommand ClickCommnd =>
_clickCommnd ?? (_clickCommnd = new DelegateCommand(ExecuteClickCommnd));

void ExecuteClickCommnd()
{
this.Text = "Oh My God!";
}
}
}

修改App.cs代码:

1
2
3
4
5
6
7
8
9
10
11
12
protected override void ConfigureViewModelLocator()
{
base.ConfigureViewModelLocator();
ViewModelLocationProvider.Register<MainWindow, Foo>();
//ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver((viewType) =>
//{
// var viewName = viewType.FullName.Replace(".Viewsb.", ".ViewModelsa.OhMyGod.");
// var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
// var viewModelName = $"{viewName}Test, {viewAssemblyName}";
// return Type.GetType(viewModelName);
//});
}

运行.

就算是不注释修改命名规则的代码,我们发现运行结果还是一样,因此我们可以得出结论,

这种直接的,不通过反射注册的自定义注册方式优先级会高点,在官方文档也说明这种方式效率会高点

且官方提供4种方式,其余三种的注册方式如下:

1
2
3
ViewModelLocationProvider.Register(typeof(MainWindow).ToString(), typeof(MainWindowTest)); 
ViewModelLocationProvider.Register(typeof(MainWindow).ToString(), () => Container.Resolve<Foo>());
ViewModelLocationProvider.Register<MainWindow>(() => Container.Resolve<Foo>());

三.源码

 本篇源代码:PrismSample源码