WPF MVVM利器CommunityToolkit.Mvvm.入门

开发WPF桌面程序时需要用到MVVM框架,而以往用的MVVMLight 早已停更,目前大多使用微软官方的CommunityToolkit.Mvvm作为替代方案,本期做一个简易的CommunityToolkit.Mvvm入门案例,以供参考
开发环境VS2022+.NET8.0

1.新建WPF项目,nuget引用CommunityToolkit.Mvvm和Microsoft.Extensions.DependencyInjection

2.项目中新建Models文件夹

添加类 Student.cs

using Microsoft.Extensions.DependencyInjection;
using System.Configuration;
using System.Data;
using System.Windows;
using WpfApp3.Service;
using WpfApp3.ViewModels;

namespace WpfApp3
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        public IServiceProvider ServiceProvider { get; private set; }

        public new static App Current => (App)Application.Current;

        public App()
        {
            ServiceProvider = ConfigurationServices();
        }


        private static IServiceProvider ConfigurationServices()
        {
            var services = new ServiceCollection();

            services.AddTransient<MainWindowViewModel>();
            services.AddTransient<EditViewModel>();
            services.AddSingleton<StudentService>();           

            return services.BuildServiceProvider();
        }
    }

}

3.新建ViewModels文件夹,新建类EditViewModel.cs, MainWindowViewModel.cs

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using WpfApp3.Message;
using WpfApp3.Models;
using WpfApp3.Service;

namespace WpfApp3.ViewModels
{
    public partial class EditViewModel : ObservableObject
    {
        private  StudentService _studentService;
        private Student _studentInfo;

        //简易写法自动生成StudentInfo属性
        [ObservableProperty]
        private Student studentInfo; 


        public ICommand SaveCommand { get; private set; }

        //自动生成CancelCommand
        [RelayCommand]
        public void Cancel()=> Application.Current.Windows.OfType<EditWindow>().FirstOrDefault()?.Close();

        // 新增构造
        public EditViewModel()
        {
            _studentService = new StudentService();
            StudentInfo = new Student();
            InitCommand();
        }

        // 修改构造
        public EditViewModel(Student model)
        {
            _studentService = App.Current.ServiceProvider.GetRequiredService<StudentService>(); 
            StudentInfo = model;
            InitCommand();
        }

        private void InitCommand()
        {
            // 保存
            SaveCommand = new RelayCommand(() =>
            {
                if (string.IsNullOrWhiteSpace(StudentInfo.Name))
                {
                    MessageBox.Show("姓名不能为空");
                    return;
                }
                bool flag;
                if (StudentInfo.Id == 0)
                {
                    flag = _studentService.Add(StudentInfo);
                }
                else
                {
                    flag = _studentService.Update(StudentInfo);
                }
                if (flag)
                {
                    // 发送刷新消息
                    WeakReferenceMessenger.Default.Send(new RefreshMsg { IsRefresh = true });
                    // 关闭当前窗口
                    Application.Current.Windows.OfType<EditWindow>().FirstOrDefault()?.Close();
                }
            });

            //// 取消
            //CancelCommand = new RelayCommand(() =>
            //{
            //    Application.Current.Windows.OfType<EditWindow>().FirstOrDefault()?.Close();
            //});
        }
 

    }
}
View Code
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using WpfApp3.Message;
using WpfApp3.Models;
using WpfApp3.Service;

namespace WpfApp3.ViewModels
{
    public partial class MainWindowViewModel:ObservableObject
    {
        private Student student;
        private readonly StudentService _studentService;

        // 学生集合
        private ObservableCollection<Student> _students;
        public ObservableCollection<Student> Students
        {
            get => _students;
            set => SetProperty(ref _students, value);
        }
 

        public Student Student
        {
            get { return student; }
            set { student = value; }
        }

        // 选中学生
        private Student _selectedStudent;
        public Student SelectedStudent
        {
            get => _selectedStudent;
            set => SetProperty(ref _selectedStudent, value);
        }



         //简易写法
        [ObservableProperty]
        private string keyword;

        //简易写法
        [RelayCommand]
        private void Search()
        {
            Students = new ObservableCollection<Student>(_studentService.Search(Keyword));
        }

        public MainWindowViewModel()
        {
            _studentService = App.Current.ServiceProvider.GetRequiredService<StudentService>();
            student = new Student();
            LoadData();
            InitCommand();

            // 注册刷新消息
            WeakReferenceMessenger.Default.Register<RefreshMsg>(this, (recipient, msg) =>
            {
                if (msg.IsRefresh) {

                    LoadData();
                } 
            });
 
        }

 

        // 加载数据
        private void LoadData()
        {
            Students = new ObservableCollection<Student>(_studentService.GetAll());
        }

 


        // 命令
        public ICommand AddCommand { get; private set; }
        public ICommand EditCommand { get; private set; }
        public ICommand DeleteCommand { get; private set; }


        // 初始化命令
        private void InitCommand()
        {
            // 新增
            AddCommand = new RelayCommand(() =>
            {
                new EditWindow().ShowDialog();
            });

            // 编辑
            EditCommand = new RelayCommand(() =>
            {
                if (SelectedStudent == null)
                {
                    MessageBox.Show("请选择一条数据");
                    return;
                }
                new EditWindow(SelectedStudent).ShowDialog();
            });

            // 删除
            DeleteCommand = new RelayCommand(() =>
            {
                if (SelectedStudent == null)
                {
                    MessageBox.Show("请选择一条数据");
                    return;
                }
                var result = MessageBox.Show("确认删除?", "提示", MessageBoxButton.YesNo);
                if (result == MessageBoxResult.Yes)
                {
                    _studentService.Delete(SelectedStudent.Id);
                    LoadData();
                }
            });
        }

 



        private bool CanExecute()
        {
            if (student.Age > 18)
            {
                MessageBox.Show("年龄太大了...");
                return false;
            }
            else
            {
                return true;
            }

        }





        private ICommand saveCommand;

        public ICommand SaveCommand => saveCommand ??= new RelayCommand(Save,CanExecute);

        private void Save()
        {
            _studentService.Add(student);

            WeakReferenceMessenger.Default.Send(new RefreshMsg { IsRefresh = true });
            MessageBox.Show($"需要保存的信息为Id={Student.Id},Name={Student.Name}!");
        }
    }
}
View Code

4.新建Service文件夹,和StudentService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WpfApp3.Models;

namespace WpfApp3.Service
{
    public class StudentService
    {
        // 模拟数据
        private List<Student> _studentList = new List<Student>
        {
            new Student{Id=1,Name="张三",Age=17,ClassName="高一(1)"},
            new Student{Id=2,Name="李四",Age=18,ClassName="高一(2)"},
            new Student{Id=3,Name="王五",Age=17,ClassName="高一(1)"},
             new Student{Id=4,Name="宋五",Age=17,ClassName="高一(1)"}
        };

        // 查询全部
        public List<Student> GetAll() => _studentList;

        // 新增
        public bool Add(Student model)
        {
            model.Id = _studentList.Max(x => x.Id) + 1;
            _studentList.Add(model);
            return true;
        }

        // 修改
        public bool Update(Student model)
        {
            var item = _studentList.FirstOrDefault(x => x.Id == model.Id);
            if (item == null) return false;
            item.Name = model.Name;
            item.Age = model.Age;
            item.ClassName = model.ClassName;
            return true;
        }

        // 删除
        public bool Delete(int id)
        {
            var item = _studentList.FirstOrDefault(x => x.Id == id);
            if (item == null) return false;
            _studentList.Remove(item);
            return true;
        }

        // 搜索
        public List<Student> Search(string keyword)
        {
            if (string.IsNullOrWhiteSpace(keyword)) return GetAll();
            return _studentList.Where(x => x.Name.Contains(keyword)).ToList();
        }
 
    }
}
View Code

5.新建Message文件夹和RefreshMsg.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WpfApp3.Message
{
    public class RefreshMsg
    {
        // 标记是否需要刷新列表
        public bool IsRefresh { get; set; }
    }
}
View Code

 6.设置App.xaml

using Microsoft.Extensions.DependencyInjection;
using System.Configuration;
using System.Data;
using System.Windows;
using WpfApp3.Service;
using WpfApp3.ViewModels;

namespace WpfApp3
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        public IServiceProvider ServiceProvider { get; private set; }

        public new static App Current => (App)Application.Current;

        public App()
        {
            ServiceProvider = ConfigurationServices();
        }


        private static IServiceProvider ConfigurationServices()
        {
            var services = new ServiceCollection();

            services.AddTransient<MainWindowViewModel>();
            services.AddTransient<EditViewModel>();
            services.AddSingleton<StudentService>();           

            return services.BuildServiceProvider();
        }
    }

}
View Code

7.新建窗体EditWindow

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.Shapes;
using WpfApp3.Models;
using WpfApp3.ViewModels;

namespace WpfApp3
{
    /// <summary>
    /// EditWindow.xaml 的交互逻辑
    /// </summary>
    public partial class EditWindow : Window
    {
        public EditWindow()
        {
            InitializeComponent();
        }


        // 修改
        public EditWindow(Student model)
        {
            InitializeComponent();
            // 传递数据
            (DataContext as EditViewModel).StudentInfo = model;
        }
 
    }
}
View Code
<Window x:Class="WpfApp3.EditWindow"
        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:WpfApp3" xmlns:vm="clr-namespace:WpfApp3.ViewModels"
        mc:Ignorable="d"
        Title="学生信息" Height="400" Width="400" ResizeMode="NoResize">

    <Window.DataContext>
        <vm:EditViewModel/>
    </Window.DataContext>
    <Grid Margin="20">
        <StackPanel>
            <StackPanel Margin="0 10">
                <TextBlock Text="姓名"/>
                <TextBox Text="{Binding StudentInfo.Name,Mode=TwoWay}" Height="25"/>
            </StackPanel>
            <StackPanel Margin="0 10">
                <TextBlock Text="年龄"/>
                <TextBox Text="{Binding StudentInfo.Age,Mode=TwoWay}" Height="25"/>
            </StackPanel>
            <StackPanel Margin="0 10">
                <TextBlock Text="班级"/>
                <TextBox Text="{Binding StudentInfo.ClassName,Mode=TwoWay}" Height="25"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0 15 0 0">
                <Button Content="保存" Command="{Binding SaveCommand}" 
                        Width="70" Height="28" Margin="5"/>
                <Button Content="取消" Command="{Binding CancelCommand}" 
                        Width="70" Height="28" Margin="5"/>
            </StackPanel>
        </StackPanel>
    </Grid>
 
</Window>
View Code

8.新建窗体 MainWindow

using System.Text;
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;
using WpfApp3.ViewModels;

namespace WpfApp3
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
      
            this.DataContext = App.Current.ServiceProvider.GetService(typeof(MainWindowViewModel));
        }
    }
}
View Code
<Window x:Class="WpfApp3.MainWindow"
        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:WpfApp3"
        mc:Ignorable="d"
        Title="学生管理系统" Height="450" Width="700">
    <Grid Margin="15">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <!--搜索栏-->
        <StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0 0 0 10">
            <TextBox Text="{Binding Keyword,Mode=TwoWay}" 
                     Width="200" Height="25" />
            <Button Content="搜索" Command="{Binding SearchCommand}" Width="80" Height="28" Margin="5"/>
        </StackPanel>

        <!--数据列表-->
        <DataGrid Grid.Row="1" ItemsSource="{Binding Students}" 
                  SelectedItem="{Binding SelectedStudent}"
                  AutoGenerateColumns="False" CanUserAddRows="False">
            <DataGrid.Columns>
                <DataGridTextColumn Header="学号" Binding="{Binding Id}" Width="80"/>
                <DataGridTextColumn Header="姓名" Binding="{Binding Name}" Width="120"/>
                <DataGridTextColumn Header="年龄" Binding="{Binding Age}" Width="80"/>
                <DataGridTextColumn Header="班级" Binding="{Binding ClassName}" Width="150"/>
            </DataGrid.Columns>
        </DataGrid>

        <!--操作按钮-->
        <StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0 10 0 0">
            <Button Content="新增" Command="{Binding AddCommand}" Width="80" Height="28" Margin="5"/>
            <Button Content="编辑" Command="{Binding EditCommand}" Width="80" Height="28" Margin="5"/>
            <Button Content="删除" Command="{Binding DeleteCommand}" Width="80" Height="28" Margin="5"/>
        </StackPanel>
    </Grid>
 
</Window>
View Code

 

posted @ 2026-09-19 08:59  桃花雪  阅读(12)  评论(0)    收藏  举报