MVVM设计模式

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

https://i.loli.net/2021/01/05/sTyLofBEG8bZWN4.png

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,直接绑定ViewModel就好。

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框架。孰优孰劣此处不予讨论,请自行了解及斟酌。

  1. Microsoft.Toolkit.Mvvm
  2. MVVM Cross
  3. MVVM Light

注意事项

如前所述,MVVM的核心是绑定,在UWP中使用绑定是再寻常不过的了。但在使用过程中也存在一些问题,需要谨慎对待:

UWP 经验 - 页面导航中的暗坑

云之幻

云之幻

热衷于敲代码,喜欢设计和产品
China