目录
- 1. 什么是MVVM
- 2. 实现简单MVVM
- 2.1. Part 1
- 2.2. Part 2
1. 什么是MVVM
MVVM
是 Model-View-ViewModel
的缩写,是一种用于构建用户界面的设计模式,是一种简化用户界面的事件驱动编程方式。
MVVM
的目标是实现用户界面和业务逻辑之间的彻底分离,以便更好地管理和维护应用程序,并提供更好的可测试性和可扩展性。
MVVM
模式包含以下三个核心组件:
Model
(模型):Model
代表应用程序的数据和业务逻辑。它负责存储和管理数据,并且通常不包含任何与用户界面相关的代码。Model 可以是数据实体、数据库访问类、服务类等。
View
(视图):View
是用户界面的可视化表示,通常由 XAML 文件定义。它负责展示数据给用户,并捕获用户输入。View 并不处理业务逻辑,而是通过绑定和命令将用户操作传递给 ViewModel。
ViewModel
(视图模型):ViewModel
是 View 和 Model 之间的桥梁,负责处理 View 与 Model 之间的交互。它将 Model 中的数据转换为 View 所需的数据,并处理用户输入、命令和其他交互逻辑。ViewModel 通常实现了 INotifyPropertyChanged 接口,用于通知 View 当数据发生变化时进行更新。
MVVM 的基本原则是让 View 和 Model 完全解耦,它们之间通过 ViewModel 进行通信。View 通过数据绑定将 ViewModel 中的数据展示给用户,通过命令绑定将用户的操作传递给 ViewModel。ViewModel则通过数据绑定和命令绑定将用户输入转换为对 Model 的操作,从而实现业务逻辑的处理。
回到 WPF 里,View就是用xaml描绘的页面,负责与用户交互,响应用户操作,展示数据给用户;ViewModel负责收集需要绑定的数据和命令,通过 View 的 DataContext 属性绑定到 View,同时处理UI逻辑;Model 就作为系统中的对象,包含数据对象。
一个 View 对应一个 ViewModel ,一个 ViewModel 可以聚合多个 Model,ViewModel 可以对应多个 View,在 MVVM 下,所有事件和动作都转换成命令,如按钮的点击操作,不在是触发点击事件,而是绑定到命令,由命令去执行对应的逻辑。
MVVM 模式的优势包括:
分离关注点:MVVM 将用户界面逻辑和业务逻辑彻底分开,使代码结构更清晰,易于理解和维护。
可测试性:由于 View 和 ViewModel 之间的解耦,可以更轻松地对 ViewModel 进行单元测试,而无需涉及到用户界面的操作。
可扩展性:MVVM 支持模块化开发,不同的 View 和 ViewModel 可以独立开发和组合,使应用程序更易于扩展。
可重用性:ViewModel 可以与不同的 View进行重用,从而减少了重复编写代码的工作。
2. 实现简单MVVM
2.1. Part 1
<Window x:Class="WpfApp1.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:sys="clr-namespace:System;assembly=mscorlib"xmlns:local="clr-namespace:WpfApp1"Title="MVVM Demo" Height="450" Width="800"><Grid><Grid.RowDefinitions><RowDefinition /><RowDefinition /></Grid.RowDefinitions><StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" DataContext="{x:Static sys:DateTime.Now}"><TextBlock Text="{Binding Year, StringFormat='The year is {0}'}" /><TextBlock Text="{Binding StringFormat='The month is {0:MMMM}'}" /><TextBlock Text="{Binding Day, StringFormat='The day is {0}'}" /><TextBlock Text="{Binding StringFormat='The time is {0:T}'}" /></StackPanel></Grid>
</Window>
这段 Xaml 在 StackPanel 的 DataContext 中设置了一个绑定到当前日期和时间的 DateTime 对象,然后通过数据绑定将 DateTime 对象的各个属性显示在不同的子元素 TextBlock 中,同时对显示内容进行了格式化。
运行程序,文本中的日期和时间只会在构造和初始化的时候设置一次,不会随时间的变化而变化。
2.2. Part 2
上面的页面写出来显然是没有意义的,我们想让它实时显示当前时间,这就需要有个东西在时间发生变化时一直更新页面上的时间,这个过程一般会通过后台代码完成,当然可以通过一个 Timer 搞定,但就和这篇文章的内容没关系了,用 MVVM 的模式怎么实现呢?
首先从 MVVM 三部分来看,View 就是标题为MVVM Demo的窗体,Model 是 DateTime 对象,需要 ViewModel 来处理时间数据同时通知页面响应更改。
创建一个ClockViewModel.cs类:
public class ClockViewModel : INotifyPropertyChanged{public event PropertyChangedEventHandler PropertyChanged;private DateTime _dateTime;private Timer _timer;public DateTime DateTime{get => _dateTime;set{if (_dateTime != value){_dateTime = value;OnPropertyChanged();}}}public ClockViewModel(){DateTime = DateTime.Now;_timer = new Timer(new TimerCallback((s) => DateTime = DateTime.Now),null, TimeSpan.Zero, TimeSpan.FromSeconds(1));}~ClockViewModel() =>_timer.Dispose();public void OnPropertyChanged([CallerMemberName] string name = ""){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));}}
ClockViewModel 类实现了 INotifyPropertyChanged 接口,并通过 Timer 定时更新时间并触发属性更改通知,提供实时的系统时间。完成 ViewModel 部分以后,将 ViewModel 绑定到视图 View - MainWindow 上:
<Window x:Class="WpfApp1.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:sys="clr-namespace:System;assembly=mscorlib"xmlns:local="clr-namespace:WpfApp1"Title="MVVM Demo" Height="450" Width="800"><Window.DataContext><local:ClockViewModel /></Window.DataContext><Grid><Grid.RowDefinitions><RowDefinition /><RowDefinition /></Grid.RowDefinitions><StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" DataContext="{x:Static sys:DateTime.Now}"><TextBlock Text="{Binding Year, StringFormat='The year is {0}'}" /><TextBlock Text="{Binding StringFormat='The month is {0:MMMM}'}" /><TextBlock Text="{Binding Day, StringFormat='The day is {0}'}" /><TextBlock Text="{Binding StringFormat='The time is {0:T}'}" /></StackPanel><StackPanel Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center"><TextBlock Text="{Binding DateTime, StringFormat='The time is {0:T}'}" /></StackPanel></Grid>
</Window>
这样就可以得到一个 MVVM 模式下的时钟了: