Prism Step4 ViewModelLocationProvider

Prism Step4 ViewModelLocationProvider

ViewModel 是对应的 View(数据和行为)的抽象,View 只是 ViewModel 的一个消费者,那么还有其他的消费者吗?当然有了,那就是单元测试(Unit Test),这个后面说。ViewModel 为 View 提供数据上下文(DataContext),简单的说,你 View 需要展示的东西,都在我这里,你需要跟我绑定,包括数据和命令,Prism 提供了自动的 ViewModel 匹配。

约定的绑定方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using Prism.Ioc;
using Prism.Unity;
using System.Windows;
using ViewModelLocator.Views;

namespace ViewModelLocator
{
/// <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)
{

}
}
}

ViewModel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using Prism.Mvvm;

namespace ViewModelLocator.ViewModels
{
public class MainWindowViewModel : BindableBase
{
private string _title = "Prism Unity Application";
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}

public MainWindowViewModel()
{

}
}
}

View 视图

1
2
3
4
5
6
7
8
9
10
11
<Window x:Class="ViewModelLocator.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"

Title="{Binding Title}" Height="350" Width="525">
<Grid>
<ContentControl prism:RegionManager.RegionName="ContentRegion" />
</Grid>
</Window>

正常的绑定

方式 1

1
2
3
<UserControl.DataContext>
<vm:NumberChangeLogViewModel />
</UserControl.DataContext>

方式 2

1
2
3
<vw:NumberView
DockPanel.Dock="Top"
DataContext="{Binding Path=Number, Mode=OneTime}" />

Prism 源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/// <summary>
/// ViewModelfactory that provides the View instance and ViewModel type as parameters.
/// </summary>
static Func<object, Type, object> _defaultViewModelFactoryWithViewParameter;

/// <summary>
/// Default view type to view model type resolver, assumes the view model is in same assembly as the view type, but in the "ViewModels" namespace.
/// </summary>
static Func<Type, Type> _defaultViewTypeToViewModelTypeResolver =
viewType =>
{
var viewName = viewType.FullName;
viewName = viewName.Replace(".Views.", ".ViewModels.");
var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
var suffix = viewName.EndsWith("View") ? "Model" : "ViewModel";
var viewModelName = String.Format(CultureInfo.InvariantCulture, "{0}{1}, {2}", viewName, suffix, viewAssemblyName);
return Type.GetType(viewModelName);
};

自定义约束

约定就是要来被打破的,有人可能觉得后缀加一个 ViewModel 实在是不太完美,但是可以改变吗? 当然可以。

prism 为我们提供了一个可重写的 ConfigureViewModelLocator 的方法来配置 ViewModel 的定位器,如果你想修改默认的约定为 View 的名字后面+VM,你可以在 app.xaml.cs 这样写:

1
2
3
4
5
6
7
8
9
10
11
12
protected override void ConfigureViewModelLocator()
{
base.ConfigureViewModelLocator();

ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver((viewType) =>
{
var viewName = viewType.FullName;
var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
var viewModelName = $"{viewName}VM, {viewAssemblyName}";
return Type.GetType(viewModelName);
});
}

如果你想指定你绑定的 ViewModel 对象又不想遵循一定的规则,你同样可以在 ConfigureViewModelLocator 方法中注册绑定,像下面这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
protected override void ConfigureViewModelLocator()
{
base.ConfigureViewModelLocator();

// type / type
ViewModelLocationProvider.Register(typeof(MainWindow).ToString(), typeof(CustomViewModel));

// type / factory
ViewModelLocationProvider.Register(typeof(MainWindow).ToString(), () => Container.Resolve<CustomViewModel>());

// generic factory
ViewModelLocationProvider.Register<MainWindow>(() => Container.Resolve<CustomViewModel>());

// generic type
ViewModelLocationProvider.Register<MainWindow, CustomViewModel>();
}

评论