WPF MVVM 入门示例讲解
M-V-VM是Model-View-ViewModel的简写,Model,ViewModel是个类文件(.cs文件),View是前台文件(,xaml文件)。假设我们的工程只有一个前台文件和一个后台文件,当设计要求越来越多的时候,前后台文件可能会高达上千行,甚至上万行,此时要想找到对应的代码,鼠标滚轮会滑的头大。学习MVVM便于工程调试,修改,移植。用MVVM如果只是修改或者更好显示界面,后台文件只需稍微的改动甚至不用改动。下面这个例子是V-VM模式就是view和viewmodel,先以简单的入门,工程集如下:

<Window x:Class="Students.View" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:Students" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="30"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Grid x:Name ="StudetGrid" Grid.Row ="0"> <Grid.DataContext> <!--声明创建一个ViewModel的实例,这个声明确定了ViewModel.cs是VM,这个声明建立了View.xaml与ViemModel之间的桥梁,这个myGrid所有绑定的属性(Name,Age,Sex)都值的是ViewModel.cs类中的成员--> <local:StudetViewModel/> </Grid.DataContext> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <TextBlock Text="姓名:" Grid.Column="0" HorizontalAlignment="Right"/> <TextBlock Text="{Binding Name}" Grid.Column="1"/> <TextBlock Text="年龄:" Grid.Column="2" HorizontalAlignment="Right" /> <TextBlock Text="{Binding Age}" Grid.Column="3" /> <TextBlock Text="性别:" Grid.Column="4" HorizontalAlignment="Right" /> <TextBlock Text="{Binding Sex}" Grid.Column="5"/> <Button Content="更新" Grid.Column="6" Click="BtnClick" /> </Grid> </Grid> </Window>

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace Students { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class View : Window { public static StudetViewModel viewModel;//声明一个类,但是没有实例化,把这个viewModel设为static方便其他页面文件互相访问绑定的属性 public View() { InitializeComponent(); viewModel = StudetGrid.DataContext as StudetViewModel;//在构造方法中实例化viewModel,这个viewModel就是View.xaml中声明的那个ViewModel实例,就是那个桥梁。 } private void BtnClick(object sender, RoutedEventArgs e) { viewModel.Name = "小明"; viewModel.Age = "15"; viewModel.Sex = "男"; } } }

using System; using System.Collections.Generic; using System.ComponentModel; using System.Text; namespace Students { public class StudetViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName)); } private string name; public String Name { get { return name; } set { name = value; OnPropertyChanged("Name"); } } private string age; public String Age { get { return age; } set { age = value; OnPropertyChanged("Age"); } } private string sex; public String Sex { get { return sex; } set { sex = value; OnPropertyChanged("Sex"); } } } }
View.xaml是View,那如何确定StudetViewModel.cs就是VM呢?在View.xaml文件中有如下声明
在前台文件中声明数据上下文指定类,指定的那个类就是ViewModel。先看一下运行效果吧
这个工程文件里的StudetViewModel.cs其实就是Model和ViewModel合在一起了
增加完Model类后的程序集如下:
如何确定PeronModel就Model呢?因为它在ViewModel中有实例化

using System; using System.Collections.Generic; using System.ComponentModel; using System.Text; namespace Students { public class StudetViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName)); } private PersonModel _studentModel = new PersonModel();//实例化Model中的类 public PersonModel StudentModel { get { return _studentModel; } set { _studentModel = value; OnPropertyChanged("StudentModel"); } } } }

using System; using System.Collections.Generic; using System.ComponentModel; using System.Text; namespace Students { public class PersonModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName)); } private string name; public String Name { get { return name; } set { name = value; OnPropertyChanged("Name"); } } private string age; public String Age { get { return age; } set { age = value; OnPropertyChanged("Age"); } } private string sex; public String Sex { get { return sex; } set { sex = value; OnPropertyChanged("Sex"); } } } }

<Window x:Class="Students.View" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:Students" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="30"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Grid x:Name ="StudetGrid" Grid.Row ="0"> <Grid.DataContext> <!--声明创建一个ViewModel的实例,这个声明确定了ViewModel.cs是VM,这个声明建立了View.xaml与ViemModel之间的桥梁,这个myGrid所有绑定的属性(Name,Age,Sex)都值的是ViewModel.cs类中的成员--> <local:StudetViewModel/> </Grid.DataContext> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <TextBlock Text="姓名:" Grid.Column="0" HorizontalAlignment="Right"/> <TextBlock Text="{Binding StudentModel.Name}" Grid.Column="1"/> <TextBlock Text="年龄:" Grid.Column="2" HorizontalAlignment="Right" /> <TextBlock Text="{Binding StudentModel.Age}" Grid.Column="3" /> <TextBlock Text="性别:" Grid.Column="4" HorizontalAlignment="Right" /> <TextBlock Text="{Binding StudentModel.Sex}" Grid.Column="5"/> <Button Content="更新" Grid.Column="6" Click="BtnClick" /> </Grid> </Grid> </Window>

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace Students { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class View : Window { public static StudetViewModel viewModel;//声明一个类,但是没有实例化,把这个viewModel设为static方便其他页面文件互相访问绑定的属性 public View() { InitializeComponent(); viewModel = StudetGrid.DataContext as StudetViewModel;//在构造方法中实例化viewModel,这个viewModel就是View.xaml中声明的那个ViewModel实例,就是那个桥梁。 } private void BtnClick(object sender, RoutedEventArgs e) { viewModel.StudentModel.Name = "小明"; viewModel.StudentModel.Age = "15"; viewModel.StudentModel.Sex = "男"; } } }
View中实例化过ViewModel,而ViewModel中实例化过Model,所以说ViewModel建立起了View与Model之间沟通的桥梁。一个ViewModel类中可以有多个不同的Model类的实例,比如我们还可以创建一个成绩类(Model),然后在ViewModel中去实例化,最后在View界面中去绑定各科成绩,这个好理解。
View中也可以实例化多个ViewModel
程序集如下:

using System; using System.Collections.Generic; using System.ComponentModel; using System.Text; namespace Students { public class TeacherViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName)); } private PersonModel _teacherModel = new PersonModel();//实例化Model中的类 public PersonModel TeacherModel { get { return _teacherModel; } set { _teacherModel = value; OnPropertyChanged("TeacherModel"); } } } }

<Window x:Class="Students.View" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:Students" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="30"/> <RowDefinition Height="30"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Grid x:Name ="StudetGrid" Grid.Row ="0"> <Grid.DataContext> <!--声明创建一个ViewModel的实例,这个声明确定了ViewModel.cs是VM,这个声明建立了View.xaml与ViemModel之间的桥梁,这个myGrid所有绑定的属性(Name,Age,Sex)都值的是ViewModel.cs类中的成员--> <local:StudetViewModel/> </Grid.DataContext> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <TextBlock Text="学生姓名:" Grid.Column="0" HorizontalAlignment="Right"/> <TextBlock Text="{Binding StudentModel.Name}" Grid.Column="1"/> <TextBlock Text="年龄:" Grid.Column="2" HorizontalAlignment="Right" /> <TextBlock Text="{Binding StudentModel.Age}" Grid.Column="3" /> <TextBlock Text="性别:" Grid.Column="4" HorizontalAlignment="Right" /> <TextBlock Text="{Binding StudentModel.Sex}" Grid.Column="5"/> <Button Content="更新" Grid.Column="6" Click="BtnClick" /> </Grid> <Grid x:Name ="TeacherGrid" Grid.Row ="1"> <Grid.DataContext> <!--声明创建一个ViewModel的实例,这个声明确定了ViewModel.cs是VM,这个声明建立了View.xaml与ViemModel之间的桥梁,这个myGrid所有绑定的属性(Name,Age,Sex)都值的是ViewModel.cs类中的成员--> <local:TeacherViewModel/> </Grid.DataContext> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <TextBlock Text="老师姓名:" Grid.Column="0" HorizontalAlignment="Right"/> <TextBlock Text="{Binding TeacherModel.Name}" Grid.Column="1"/> <TextBlock Text="年龄:" Grid.Column="2" HorizontalAlignment="Right" /> <TextBlock Text="{Binding TeacherModel.Age}" Grid.Column="3" /> <TextBlock Text="性别:" Grid.Column="4" HorizontalAlignment="Right" /> <TextBlock Text="{Binding TeacherModel.Sex}" Grid.Column="5"/> <Button Content="更新" Grid.Column="6" Click="BtnClickTeacher" /> </Grid> </Grid> </Window>

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace Students { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class View : Window { public static StudetViewModel studentViewModel;//声明一个类,但是没有实例化,把这个viewModel设为static方便其他页面文件互相访问绑定的属性 public static TeacherViewModel teacherViewModel; public View() { InitializeComponent(); studentViewModel = StudetGrid.DataContext as StudetViewModel;//在构造方法中实例化viewModel,这个viewModel就是View.xaml中声明的那个ViewModel实例,就是那个桥梁。 teacherViewModel = TeacherGrid.DataContext as TeacherViewModel; } private void BtnClick(object sender, RoutedEventArgs e) { studentViewModel.StudentModel.Name = "小明"; studentViewModel.StudentModel.Age = "15"; studentViewModel.StudentModel.Sex = "男"; } private void BtnClickTeacher(object sender, RoutedEventArgs e) { teacherViewModel.TeacherModel.Name = "王老师"; teacherViewModel.TeacherModel.Age = "30"; teacherViewModel.TeacherModel.Sex = "女"; } } }
效果如下:
上面的示例中,控件的成员属性和View分开了,但是按键的Click事件内容还是在后台文件实现的,那怎么把事件属性也和View分开呢?

<Button Content="更新" Grid.Column="6" Command="{Binding CommadUpdata}" />
把Button 的Click用Commad代替,以TeacherViewModel例

using Prism.Commands; using System; using System.Collections.Generic; using System.ComponentModel; using System.Text; namespace Students { public class TeacherViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName)); } private PersonModel _teacherModel = new PersonModel();//实例化Model中的类 public PersonModel TeacherModel { get { return _teacherModel; } set { _teacherModel = value; OnPropertyChanged("TeacherModel"); } } public TeacherViewModel() { this.CommadUpdata = new DelegateCommand(new Action(Updata));//命令属性与方法联系起来 } private void Updata()//方法 { this.TeacherModel.Name = "王老师"; this.TeacherModel.Age = "30"; this.TeacherModel.Sex = "女"; } public DelegateCommand CommadUpdata { get; set; }//命令属性 } }
StudentViewModel也是类似,这样改完后,后台文件View.xaml.cs没有添加任何东西

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace Students { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class View : Window { public View() { InitializeComponent(); } } }
这样成员属性和命令属性都和前台文件去耦合了
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」