VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > c#编程 >
  • C#关于MVVM模式——数据绑定与视图分离

第二部分:桌面应用开发
13.MVVM模式——数据绑定与视图分离
实例介绍
你有没有遇到过这样的情况?之前写的任务管理工具,视图(XAML)和逻vb.net教程C#教程python教程SQL教程access 2010教程
辑(C#后台)混在一起:比如按钮点击事件直接写在MainWindow.xaml.cs里,DataGrid的数据更新要手动调用TaskDataGrid.ItemsSource = tasks。这样的代码有个大问题——耦合度太高:改个按钮逻辑要动视图代码,换个界面布局要动逻辑代码,维护起来像拆炸弹。
后来我用MVVM模式改造了这个工具:把视图和逻辑彻底分开,视图只负责显示,逻辑放在ViewModel里,用数据绑定代替手动更新,用命令代替点击事件。改造后,我要加个“批量删除”功能,只需要在ViewModel里加个命令,视图里绑定按钮就行,完全不用碰MainWindow的代码。这一节,我就带你用MVVM改造任务管理工具,掌握数据绑定和视图分离的核心技巧。
需求分析
MVVM模式要解决的核心问题:
1.视图与逻辑分离:视图(XAML)只写界面,逻辑(数据处理、业务逻辑)放在ViewModel里,互不依赖;
2.数据自动同步:ViewModel的属性变化时,视图自动更新(不用手动调用控件的Update方法);
3.命令代替事件:按钮点击、菜单选择等交互,用ICommand绑定代替Click事件处理,避免视图和逻辑耦合;
4.可测试性:ViewModel可以独立测试,不用启动界面就能验证逻辑;
5.团队协作:设计师改界面(XAML),开发者改逻辑(ViewModel),互不干扰。
目标:用MVVM模式改造任务管理工具,实现“添加任务”“删除任务”功能,视图和逻辑完全分离,代码可维护性提升。
代码实现
前置条件:.NET 6+、WPF、CommunityToolkit.Mvvm(简化MVVM代码,NuGet安装);
延续之前的任务管理工具案例,Model复用TaskItem,View复用MainWindow.xaml。
准备工作:安装依赖
bash

	# 安装CommunityToolkit.Mvvm(提供ObservableObject、RelayCommand等)
	dotnet add package CommunityToolkit.Mvvm

场景1:定义Model(纯数据模型)
Model是纯数据载体,不包含业务逻辑,也不依赖WPF框架。
csharp

	// Model:任务实体(纯数据,无逻辑)
	public class TaskItem
	{
	public int Id { get; set; }
	public string Title { get; set; } = string.Empty;
	public DateTime DueDate { get; set; }
	public bool IsCompleted { get; set; }
	}

场景2:创建ViewModel(核心逻辑层)
ViewModel是视图和模型之间的桥梁,实现业务逻辑,支持数据绑定和命令。
csharp

	using CommunityToolkit.Mvvm.ComponentModel;
	using CommunityToolkit.Mvvm.Input;
	using System.Collections.ObjectModel;
	using System.Windows.Input;
	
	// ViewModel:任务管理逻辑(继承ObservableObject,自动实现INotifyPropertyChanged)
	public partial class TaskViewModel : ObservableObject
	{
	// 1. 数据绑定属性:任务列表(用ObservableCollection,支持集合变化通知)
	[ObservableProperty] // CommunityToolkit自动生成TaskList属性和PropertyChanged事件
	private ObservableCollection<TaskItem> _taskList = new();
	
	// 2. 数据绑定属性:新任务标题(双向绑定,视图输入框变化时更新ViewModel)
	[ObservableProperty]
	private string _newTaskTitle = string.Empty;
	
	// 3. 数据绑定属性:新任务截止日期
	[ObservableProperty]
	private DateTime _newTaskDueDate = DateTime.Now.AddDays(1);
	
	// 4. 命令:添加任务(用RelayCommand,简化ICommand实现)
	[RelayCommand] // 自动生成AddTaskCommand属性
	private void AddTask()
	{
	if (string.IsNullOrWhiteSpace(NewTaskTitle))
	return; // 标题为空不添加
	
	// 模拟添加任务到数据库(后续替换为TaskService)
	var newTask = new TaskItem
	{
	Title = NewTaskTitle,
	DueDate = NewTaskDueDate,
	IsCompleted = false
	};
	TaskList.Add(newTask);
	
	// 清空输入框(双向绑定,视图自动更新)
	NewTaskTitle = string.Empty;
	NewTaskDueDate = DateTime.Now.AddDays(1);
	}
	
	// 5. 命令:删除选中任务(带参数的RelayCommand)
	[RelayCommand]
	private void DeleteTask(TaskItem? selectedTask)
	{
	if (selectedTask == null)
	return;
	
	TaskList.Remove(selectedTask);
	}
	
	// 构造函数:加载初始数据
	public TaskViewModel()
	{
	// 模拟从数据库加载任务(后续替换为TaskService.GetPendingTasksAsync)
	TaskList.Add(new TaskItem { Title = "学习MVVM", DueDate = DateTime.Now.AddDays(3), IsCompleted = false });
	TaskList.Add(new TaskItem { Title = "写ViewModel", DueDate = DateTime.Now.AddDays(5), IsCompleted = true });
	}
	}

场景3:视图绑定ViewModel(XAML)
视图(MainWindow.xaml)通过DataContext绑定ViewModel,用{Binding}语法绑定属性和命令。
xml

	<Window x:Class="TaskManager.MainWindow"
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:viewModels="clr-namespace:TaskManager.ViewModels"
	Title="任务管理工具(MVVM版)" Width="800" Height="600" StartupLocation="CenterScreen">
	
	<!-- 设置DataContext为ViewModel(视图绑定ViewModel) -->
	<Window.DataContext>
	<viewModels:TaskViewModel/>
	</Window.DataContext>
	
	<Grid>
	<Grid.RowDefinitions>
	<RowDefinition Height="Auto"/> <!-- 输入区域 -->
	<RowDefinition Height="*"/> <!-- 任务列表 -->
	<RowDefinition Height="Auto"/> <!-- 按钮区域 -->
	</Grid.RowDefinitions>
	
	<!-- 1. 任务输入区域(双向绑定ViewModel的属性) -->
	<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="10" Spacing="10">
	<TextBox Width="300" PlaceholderText="输入任务标题" 
	Text="{Binding NewTaskTitle, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
	<DatePicker Width="150" SelectedDate="{Binding NewTaskDueDate, Mode=TwoWay}"/>
	<Button Content="添加任务" 
	Command="{Binding AddTaskCommand}"/> <!-- 绑定AddTaskCommand -->
	</StackPanel>
	
	<!-- 2. 任务列表(绑定ViewModel的TaskList) -->
	<DataGrid Grid.Row="1" Margin="10"
	ItemsSource="{Binding TaskList}" <!-- 绑定任务列表 -->
	SelectedItem="{Binding SelectedTask, Mode=TwoWay}" <!-- 双向绑定选中项 -->
	AlternatingRowBackground="#E8F4FF"
	CanUserAddRows="False" AutoGenerateColumns="False">
	<DataGrid.Columns>
	<DataGridTextColumn Header="任务标题" Binding="{Binding Title}" Width="*"/>
	<DataGridTextColumn Header="截止日期" Binding="{Binding DueDate, StringFormat='yyyy-MM-dd'}" Width="150"/>
	<DataGridCheckBoxColumn Header="已完成" Binding="{Binding IsCompleted}" Width="80"/>
	</DataGrid.Columns>
	</DataGrid>
	
	<!-- 3. 删除按钮(绑定DeleteTaskCommand,传递选中项) -->
	<Button Grid.Row="2" Content="删除选中任务" Margin="10"
	Command="{Binding DeleteTaskCommand}"
	CommandParameter="{Binding SelectedItem, ElementName=TaskDataGrid}"/> <!-- 传递选中项 -->
	</Grid>
	</Window>

场景4:后台代码简化(MainWindow.xaml.cs)
MVVM模式下,视图的后台代码几乎为空,只负责初始化窗口。
csharp

	using System.Windows;
	
	namespace TaskManager
	{
	public partial class MainWindow : Window
	{
	public MainWindow()
	{
	InitializeComponent();
	// 不需要任何逻辑代码!所有逻辑都在ViewModel里
	}
	}
	}

逐行讲解
场景2:ViewModel详解
1.ObservableObject:CommunityToolkit.Mvvm提供的基类,自动实现INotifyPropertyChanged接口,当属性变化时通知视图更新。
2.[ObservableProperty]:自动生成属性的getter、setter,以及PropertyChanged事件触发。比如[ObservableProperty] private string _newTaskTitle会生成public string NewTaskTitle { get; set; },并在设置时触发PropertyChanged。
3.[RelayCommand]:自动生成ICommand属性,比如[RelayCommand] private void AddTask()会生成public ICommand AddTaskCommand { get; },按钮绑定这个Command即可。
4.ObservableCollection:代替List,因为它实现了INotifyCollectionChanged接口,集合添加/删除元素时通知视图更新(DataGrid自动刷新)。
5.双向绑定:NewTaskTitle是双向绑定属性,当ViewModel里设置NewTaskTitle = string.Empty时,视图的TextBox自动清空;反之,用户输入时,ViewModel的NewTaskTitle自动更新。
场景3:视图绑定详解
1.Window.DataContext:设置视图的数据源为ViewModel,所有绑定都基于这个DataContext。
2.Text="{Binding NewTaskTitle, Mode=TwoWay}":双向绑定TextBox的Text到ViewModel的NewTaskTitle属性,UpdateSourceTrigger=PropertyChanged表示用户输入时立即更新ViewModel(默认是LostFocus)。
3.Command="{Binding AddTaskCommand}":按钮绑定ViewModel的AddTaskCommand,点击按钮时自动执行AddTask方法。
4.CommandParameter="{Binding SelectedItem, ElementName=TaskDataGrid}":删除按钮传递DataGrid的选中项给DeleteTaskCommand,ViewModel的DeleteTask方法接收这个参数。
基础知识拓展

  1. MVVM核心概念
概念 说明
Model 纯数据模型,包含业务数据(如TaskItem),不依赖任何框架。
View 界面展示层(XAML),负责显示数据和接收用户输入,不包含业务逻辑。
ViewModel 逻辑处理层,连接View和Model,实现INotifyPropertyChanged(通知视图更新)和ICommand(处理用户交互)。
INotifyPropertyChanged 通知视图属性变化的接口,ViewModel实现它后,属性变化时视图自动更新。
ICommand 处理用户交互的接口,代替传统的Click事件,实现命令和视图分离。
  1. 数据绑定模式
模式 说明 应用场景
OneWay ViewModel→View,视图只显示数据,不修改。 显示任务列表、状态栏文本。
TwoWay ViewModel↔View,双向同步。 输入框、日期选择器等用户输入控件。
OneTime 只绑定一次,后续变化不更新。 静态文本(如软件版本号)。
OneWayToSource View→ViewModel,视图变化时更新ViewModel,反之不更新。 密码输入框(不显示ViewModel的值)。
  1. MVVM vs 传统事件驱动
对比项 传统事件驱动 MVVM模式
耦合度 视图和逻辑高度耦合(Click事件处理写在MainWindow.xaml.cs)。 视图和逻辑完全分离(ViewModel不引用View)。
可测试性 必须启动界面才能测试逻辑。 ViewModel可独立测试(直接调用方法验证结果)。
维护性 修改逻辑需要改视图代码,风险高。 修改逻辑只需改ViewModel,视图不变。
代码量 手动更新视图(如TaskDataGrid.ItemsSource = tasks),代码多。 数据绑定自动更新,代码简洁。
  1. 最佳实践
    1.用CommunityToolkit.Mvvm简化代码:避免手动实现INotifyPropertyChanged和ICommand,用[ObservableProperty]和[RelayCommand]注解。
    2.用ObservableCollection代替List:集合变化时视图自动更新。
    3.ViewModel注入服务:通过构造函数注入TaskService、数据库上下文等,实现依赖注入,提高可测试性。
    4.避免在ViewModel里引用View:ViewModel不能包含Button、TextBox等视图控件,保持纯逻辑。
    总结
    MVVM模式的核心是数据绑定和视图分离,通过ViewModel连接View和Model,让代码更易维护、可测试。本节的关键要点:
    1.ViewModel实现INotifyPropertyChanged:属性变化时通知视图更新。
    2.用ICommand代替事件:处理用户交互,实现逻辑和视图分离。
    3.双向绑定:用户输入和ViewModel属性自动同步。
    4.视图后台代码为空:所有逻辑放在ViewModel里,视图只负责展示。
    掌握MVVM后,你可以轻松应对复杂的桌面应用开发,尤其是WPF这类支持数据绑定的框架,代码质量和开发效率会大幅提升。

本站原创,转载请注明出处:https://www.xin3721.com/ArticlecSharp/c49460.html


相关教程