UWP沿袭前辈的实现(指WPF),同样使用MVVM作为应用构建的核心设计模式。所以掌握,至少是了解MVVM,对我们之后处理UWP应用开发还是很有帮助的。

MVVM就是Model,View,ViewModel,ViewModel就是Model和View之间的桥梁,MVVM的一大核心优势就是绑定。但这么说难免枯燥,我先给出一些有用的文档,再举3个常见的例子来描述一下具体的MVVM实现。
辅助文档
1. 标准实现
标准实现就是遵循MVVM的设计规范。
我创建了一个Person
类,但是我的View
并不直接绑定到这个Person
类中的属性,而是在ViewModel
中创建一个属性,然后进行绑定。
Model
public class Person
{
public string Name {get; set;}
}
ViewModel
public class PersonViewModel
{
private Person model;
public string Name
{
get => model.Name;
set
{
model?.Name = value;
}
}
public PersonViewModel(Person input)
{
this.model = input;
}
}
View
public PersonViewModel vm;
public PersonPage()
{
// ...
this.vm = new PersonViewModel(somePerson);
}
<TextBlock Text="{x:Bind vm.Name}" />
为什么要这么干,因为我们要介入赋值、取值的过程。
最常见的,当我们修改属性的时候,我们需要通知UI进行改变,这时候在属性的setter中我们会去调用PropertyChanged
方法。
public class PersonViewModel:INotifyPropertyChanged
{
private Person model;
public string Name
{
get => model.Name;
set
{
model?.Name = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
有时候,我们会需要对值的类型进行转换。比如两个Person
,男的显示蓝色,女的显示红色。
但是Person类中并没有定义一个Brush
类型的属性,这时候我们就需要进行一些转换。
其它的情况也有不少,比如我们要对传入传出的值进行验证,比如在修改某个属性的同时去修改其它的属性。
2. 增强版Model
如果你的数据类型并没有什么复杂的要求,不需要验证,不需要更改属性类型,就需要通知一下UI,那么就让你的Model
实现一下INotifyPropertyChanged
接口就行了。
这种情况在UWP中非常普遍,广泛用在各个DataTemplate
里面。当然,如果连通知UI也不需要,那么这里也没必要创建一个ViewModel
,直接绑定View
和Model
就好。
3. 分拆实现
这里要引入一个概念,Code-Behind
。
与之对应的还有两个概念,这俩概念在网页开发中用得很多。
一个是Code-Inline
,一个是Code-Block
。
Code-Inline
就是在UI里直接写逻辑代码,Code-Block
则是将逻辑代码抽离了出来,但还是和UI在同一文件里。
而Code-Behind
则是把逻辑代码从UI文件里分离出来,放在一个单独的文件中。在UWP里,就是xaml对应的xaml.cs文件。
而我们说的分拆,其实就是把与UI控件直接相关的代码抽离出来,放在Code-Behind
之中,这在UWP中是原生支持的。
比如当我们为一个Page附加Loaded事件的时候,Visual Studio直接帮我们将事件回调写在了Code-Behind之中。那我们的ViewModel就可以专心负责纯数据的处理。当不直接牵涉到UI控件的时候,ViewModel在此时就被解放了,它可以被最大程度地复用,而且也有利于我们进行单元测试。
而我们要做的,就是在Code-behind之中实例化一个ViewModel,调用其中的方法,然后根据方法传回的结果来对UI进行直接的控制。这是一种分离度更高的设计方式,而且特别适合UWP。所以我们能经常在页面这一层级上看到这种分拆式的实现。
需要注意的是,分拆实现看上去分工明确,颇有一些工程化实践的味道。但实际上想实现它也不太容易。 这种方式将整个工作流程拆成了4个部分,最糟糕的情况你可能需要连续修改四个文件,只为了某一个小bug。 这是一把斩马刀,当我们需要切水果的时候,其实并不需要分得这么明确。 所以分拆实现我推荐是就在页面这一层级来使用,对于更细粒度的操作,可以酌情选择前两种实现方式。
辅助工具
UWP还提供了两个实用的小工具可以帮助我们进一步简化ViewModel
。
一个是函数绑定,另一个就是转换器。
函数绑定通过x:Bind
进行,在14393以上可用。我们可以直接绑定数据上下文的函数,换句话说,我们不光可以绑定属性,也可以直接绑定一段逻辑。
转换器则是将类型转换的代码提取出来,不影响绑定的路径,而是介入到绑定过程中修改绑定结果。 这两种方法主要都是为了转换绑定源的数据类型,而这会是我们之后经常面临的场景。我们可以通过这两个工具抽离转换代码,并进行复用。
扩展阅读
MVVM框架
MVVM作为一种设计模式,是一种指导思想,市面上有颇多针对MVVM进行的工程化实践,也有一些框架可以帮助我们更轻松地实现MVVM。以下列举在UWP中常用的MVVM框架。孰优孰劣此处不予讨论,请自行了解及斟酌。
注意事项
如前所述,MVVM的核心是绑定,在UWP中使用绑定是再寻常不过的了。但在使用过程中也存在一些问题,需要谨慎对待: