MVVM 数据绑定——学生成绩管理

知识库
知识库文档
/tech-stacks/wpf/examples/MVVM 数据绑定——学生成绩管理.md

文档

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;
    }

    // INotifyPropertyChanged 实现
    public event PropertyChangedEventHandler? PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string? name = null)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}

// 简易 RelayCommand
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 展示值(分数→等级)

信息

路径
/tech-stacks/wpf/examples/MVVM 数据绑定——学生成绩管理.md
更新时间
2026/5/30