精通
英语
和
开源
,
擅长
开发
与
培训
,
胸怀四海
第一信赖
锐英源精品原创,禁止全文或局部转载,禁止任何形式的非法使用,侵权必究。点名“简易百科”和闲暇巴盗用锐英源原创内容。
昨天发了一个问题是说ViewModelLocator的,今天找了个英文文章深入学习下ViewModelLocator,翻译给大家共同提高。要点是框架接口和ViewModelLocator的static函数结合,同时也使用了Type类型机制。注意不是MVVMLight框架,但原理类似。下面是正文:
Uno 平台入门博客系列是我在 2020 年 10 月整理的博客系列。它包含几篇文章,可帮助您开始在 Uno 平台中构建可扩展的企业应用程序。请务必前往第 1 篇文章 - Uno 平台入门系列查看该系列中的所有博客
使用 Uno Platform 构建跨平台应用程序时,使用 Model-View-ViewModel (MVVM) 架构是一种常用技术。这是一种将用户界面逻辑与为页面提供支持的业务规则分离的方法。AViewModel是一个特殊的类,它为视图提供数据绑定。许多 MVVM 框架提供了预构建的ViewModelLocator. 本文介绍了如何ViewModelLocator为您的项目构建自定义。
此处描述的 ViewModelLocator 已改编自Prism Library中使用的那个。结构仍然非常相似,因为他们设计了一个很棒的 ViewModelLocator
MVVM 代表 Model-View-ViewModel 一种由微软著名的应用程序设计模式。它使开发人员能够将他们的用户界面逻辑与业务规则分离。例如,屏幕上的自定义动画或渲染项目将成为您的页面。然后用于检索数据并将该数据加载到集合中的 API 调用将是 ViewModel 代码。
(图片由 Microsoft Xamarin 文档提供)
如果您不熟悉该模式,则应查看 MVVM 的 Xamarin 文档,因为它彻底解释了该模式。
考虑这个简单ViewModel的,它存储一个Message,进而要在 Hello World 应用程序的屏幕上显示。
1
2
3
4 |
public class MainViewModel
{
public string Message => "Hello World from MainViewModel!";
}
|
一旦ViewModel创建,我们不需要对它做任何特别的事情。我们只是简单地实例化它或在后面的 View 代码中新建它并将其分配给DataContext.
1
2
3
4
5
6
7
8 |
public sealed partial class MainPage
{
public MainPage()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}
|
现在您可以更新视图代码以绑定到Message属性,而不是在 XAML 中硬编码字符串。
1
2
3
4
5
6
7
8
9
10
11
12
13 |
<Page
x:Class="ViewModelLocator.Views.MainPage"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Text="{Binding Message}" Margin="20" FontSize="30" />
</Grid>
</Page>
|
View Model Locator 是 MVVM 应用程序中的一种技术,可以自动将 ViewModel分配给 View 的DataContext,而无需 View 中的其他代码或隐藏代码。当您想在 MVVM 应用程序中使用依赖注入时,这种技术变得越来越有用。这将在ViewModel实例化时自动解决任何依赖关系。
我们不会添加BasePage或重新编写核心代码来解决这个问题。我们将使用一个称为附加属性的概念。附加属性允许您对类中不存在的对象的公共成员执行逻辑。此技术可应用于构造函数或 xaml。如果您一直在使用 Uno 平台、UWP 或 Xamarin.Forms 应用程序,您很可能已经使用了附加属性并且甚至不知道它。例如,通过使用附加属性来定义Grid和定义行或列。Grid.Row="0"
1
2
3
4
5
6
7
8 |
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="This is using an attached property!" />
</Grid>
|
如果您有兴趣了解有关附加属性的更多信息,您应该阅读有关此技术的 Microsoft UWP 文档
我们将从我的大多数文章中稍微倒退一点。在开始之前查看最终的 API 会很有帮助。
目标是向我们的 XAML 添加 2 行代码来注册ViewModelLocator. 让我们计划ViewModelLocator在 Mvvm 命名空间中定义 。我们需要将以下几行添加到我们的视图中
1
2 |
xmlns:mvvm="using:ViewModelLocator.Mvvm"
mvvm:ViewModelLocator.AutoWireViewModel="True"
|
这应该是自动将 ViewModel 绑定到 View 所需的所有代码。现在,随着您创建越来越多的 View/ViewModel 关系,它会自动实例化它并将其分配给 View。这是我们更新的整个视图代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 |
<Page
x:Class="ViewModelLocator.Views.MainPage"
xmlns:mvvm="using:ViewModelLocator.Mvvm"
mvvm:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Text="{Binding Message}" Margin="20" FontSize="30" />
</Grid>
</Page>
|
现在我们已经定义了 API 用法,我们可以创建ViewModelLocator,将实现附加属性以将正确分配ViewModel给DataContext. 让我们首先在您的共享代码中创建一个名为 的新文件夹Mvvm,然后创建ViewModelLocator类。
首先创建包含访问器、设置器和属性更改方法的基本附加属性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 |
public static class ViewModelLocator
{
public static DependencyProperty AutoWireViewModelProperty = DependencyProperty.RegisterAttached("AutoWireViewModel", typeof(bool),
typeof(ViewModelLocator), new PropertyMetadata(false, AutoWireViewModelChanged));
public static bool GetAutoWireViewModel(UIElement element)
{
return (bool)element.GetValue(AutoWireViewModelProperty);
}
public static void SetAutoWireViewModel(UIElement element, bool value)
{
element.SetValue(AutoWireViewModelProperty, value);
}
private static void AutoWireViewModelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// TODO - Implement binding
}
}
|
这是实现附加属性所需的最基本代码。当AutoWireViewModelChanged调用它时,它将作为参数传入页面。Uno Platform 中继承的任何对象DependencyObject都是传递的基础对象。这可以实现更高级的场景,但我们将专注于简单的页面绑定。
为此,我们需要实施 2 个步骤
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 |
private static Type FindViewModel(Type viewType)
{
string viewName = string.Empty;
if (viewType.FullName.EndsWith("Page"))
{
viewName = viewType.FullName
.Replace("Page", string.Empty)
.Replace("Views", "ViewModels");
}
var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
var viewModelName = string.Format(CultureInfo.InvariantCulture, "{0}ViewModel, {1}", viewName, viewAssemblyName);
return Type.GetType(viewModelName);
}
|
我们这里的逻辑查看当前页面或DependencyObject尝试根据名称查找匹配的 ViewModel。例如,我们的代码查看MainPage并理解我们的约定映射MainViewModel。这两个类都包含Main生成映射的关键字。然后该方法使用反射来获取对象类型并返回它。FindViewModel如果您的 ViewModel 存在不同的命名空间,您的逻辑可能会有所不同。
一旦你定义了你的FindViewModel方法,我们就可以实现这个Bind方法。此方法将采用返回的类型并将其实例化,该类型将应用于DataContext.
private static void Bind(DependencyObject view)
{
if (view is FrameworkElement frameworkElement)
{
var viewModelType = FindViewModel(frameworkElement.GetType());
frameworkElement.DataContext = Activator.CreateInstance(viewModelType);
}
}
|
此示例使用Activator来创建 View Model 的新实例,并且是 View Model Locator 的基本示例。为了对此进行扩展,我将Activator使用依赖注入容器来更新调用来解析视图模型。这将确保在没有任何额外工作的情况下注入所有依赖项。
如果您使用的是 Microsoft.Extensions.DependencyInjection 库,则可以将Activator调用换成ActivatorUtilities. 我们不会在本文中介绍依赖注入,但是如果您想进一步扩展此技术,您可能会发现此代码段很有用
1 |
frameworkElement.DataContext = ActivatorUtilities.GetServiceOrCreateInstance(((App)App.Current).Container, viewModelType);
|
创建Bind方法后,您可以更新AutoWireViewModelChanged实现以使用该Bind方法。作为安全检查,我始终确保在应用绑定之前它是一个新变量,您不希望不断地重新连接视图模型。
1
2
3
4
5 |
private static void AutoWireViewModelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue)
Bind(d);
}
|
您现在已经构建ViewModelLocator了您的代码以确保您的代码是正确的,我将完整的代码放在一个片段中。
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
41
42
43
44
45
46
47 |
public static class ViewModelLocator
{
public static DependencyProperty AutoWireViewModelProperty = DependencyProperty.RegisterAttached("AutoWireViewModel", typeof(bool),
typeof(ViewModelLocator), new PropertyMetadata(false, AutoWireViewModelChanged));
public static bool GetAutoWireViewModel(UIElement element)
{
return (bool)element.GetValue(AutoWireViewModelProperty);
}
public static void SetAutoWireViewModel(UIElement element, bool value)
{
element.SetValue(AutoWireViewModelProperty, value);
}
private static void AutoWireViewModelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue)
Bind(d);
}
private static void Bind(DependencyObject view)
{
if (view is FrameworkElement frameworkElement)
{
var viewModelType = FindViewModel(frameworkElement.GetType());
frameworkElement.DataContext = Activator.CreateInstance(viewModelType);
}
}
private static Type FindViewModel(Type viewType)
{
string viewName = string.Empty;
if (viewType.FullName.EndsWith("Page"))
{
viewName = viewType.FullName
.Replace("Page", string.Empty)
.Replace("Views", "ViewModels");
}
var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
var viewModelName = string.Format(CultureInfo.InvariantCulture, "{0}ViewModel, {1}", viewName, viewAssemblyName);
return Type.GetType(viewModelName);
}
}
|
此时您可以运行您的应用程序,您的视图模型将自动连接到视图。您创建的任何新 View/ViewModel paris 都将通过添加前面提到的 2 个 xaml 语句来连接。
这就是在 MVVM 应用程序中设置 View Model Locator 所需的全部内容。View Model Locator 的目的是自动将您的 View 连接到 ViewModel。这里技术的一个进步是在你的视图模型定位器中使用依赖注入来自动将依赖注入你的视图模型。