WPF MVVM 数据绑定——学生成绩管理
目标
展示 WPF 中 MVVM 模式的核心用法:数据绑定、命令、INotifyPropertyChanged,做一个简易学生成绩录入界面。
完整代码
项目结构
StudentManager/
├── Models/Student.cs
├── ViewModels/MainViewModel.cs
├── Views/MainWindow.xaml
├── Views/MainWindow.xaml.cs
├── App.xaml
└── App.xaml.cs
Models/Student.cs
namespace StudentManager.Models;
public class Student
{
public string Name { get; set; } = "";
public int Score { get; set; }
}
ViewModels/MainViewModel.cs
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using StudentManager.Models;
namespace StudentManager.ViewModels;
public class MainViewModel : INotifyPropertyChanged
{
private string _newName = "";
private int _newScore;
public string NewName
{
get => _newName;
set { _newName = value; OnPropertyChanged(); }
}
public int NewScore
{
get => _newScore;
set { _newScore = value; OnPropertyChanged(); }
}
public ObservableCollection<Student> Students { get; } = new();
public ICommand AddStudentCommand { get; }
public MainViewModel()
{
AddStudentCommand = new RelayCommand(AddStudent, CanAddStudent);
}
private bool CanAddStudent() =>
!string.IsNullOrWhiteSpace(NewName) && NewScore >= 0 && NewScore <= 100;
private void AddStudent()
{
Students.Add(new Student { Name = NewName, Score = NewScore });
NewName = "";
NewScore = 0;
}
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string? name = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public class RelayCommand : ICommand
{
private readonly Action _execute;
private readonly Func<bool> _canExecute;
public RelayCommand(Action execute, Func<bool> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object? p) => _canExecute();
public void Execute(object? p) => _execute();
public event EventHandler? CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
}
Views/MainWindow.xaml
<Window x:Class="StudentManager.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:StudentManager.ViewModels"
Title="学生成绩管理" Height="400" Width="500">
<Window.DataContext>
<vm:MainViewModel/>
</Window.DataContext>
<Grid Margin="15">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- 输入区域 -->
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,0,0,10">
<TextBlock Text="姓名:" VerticalAlignment="Center" Margin="0,0,5,0"/>
<TextBox Text="{Binding NewName, UpdateSourceTrigger=PropertyChanged}"
Width="120" Margin="0,0,10,0"/>
<TextBlock Text="成绩:" VerticalAlignment="Center" Margin="0,0,5,0"/>
<TextBox Text="{Binding NewScore, UpdateSourceTrigger=PropertyChanged}"
Width="60" Margin="0,0,10,0"/>
<Button Content="添加" Command="{Binding AddStudentCommand}"
Width="60"/>
</StackPanel>
<!-- 统计 -->
<TextBlock Grid.Row="1" Margin="0,0,0,8">
<Run Text="学生人数: "/>
<Run Text="{Binding Students.Count}" FontWeight="Bold"/>
</TextBlock>
<!-- 数据表格 -->
<DataGrid Grid.Row="2" ItemsSource="{Binding Students}"
AutoGenerateColumns="False" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="姓名" Binding="{Binding Name}" Width="*"/>
<DataGridTextColumn Header="成绩" Binding="{Binding Score}" Width="80"/>
<DataGridTextColumn Header="等级" Width="80"
Binding="{Binding Score, Converter={StaticResource ScoreConverter}}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
App.xaml(含 ScoreConverter)
<Application x:Class="StudentManager.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:StudentManager">
<Application.Resources>
<local:ScoreConverter x:Key="ScoreConverter"/>
</Application.Resources>
</Application>
ScoreConverter(分数→等级)
using System.Windows.Data;
using System.Globalization;
namespace StudentManager;
public class ScoreConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is int score)
return score >= 90 ? "A" : score >= 80 ? "B" : score >= 70 ? "C" : score >= 60 ? "D" : "F";
return "F";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotImplementedException();
}
运行步骤
dotnet new wpf -n StudentManager
dotnet run
关键点
- DataContext 是 MVVM 的桥梁,View 通过 Binding 与 ViewModel 通信
- INotifyPropertyChanged:属性变化时自动通知 UI 更新
- ObservableCollection:集合增删自动刷新 DataGrid
- ICommand:将按钮操作绑定到 ViewModel 方法,避免 Code-Behind
- IValueConverter:把数据值转换为 UI 展示值(分数→等级)