WPF + Microsoft.ToolKit.Mvvm 技术指南与实战项目

CommunityToolkit.Mvvm学习笔记(1)——概述 先简单谈谈为什么选择学习使用这个MVVM框架。 接触.NET和WPF有一阵子了,用过WPF的应该都听说过MVVM模式,它是一种分离前后端、松耦合的模式。某种程度上来说是MVC和MVP的升级版,也一定程度上解决了前两者存在的一些问题。因为是模式,所以不仅限于WPF这一种开发,许多涉及UI的开发都会用到它,比如Vue。 既然它这么优秀,我自然想使用它。但由于我接触.NET和WPF时间都不长,很多基础知识掌握不好,所以我决定先使用原生的MVVM,即不用任何框架,自己先徒手实现试试。这样做或许比较费 阅读详情

第一部分:Microsoft.ToolKit.Mvvm 框架深度解析(含更名说明)

1.1 框架定位与现状
框架核心介绍

Microsoft.ToolKit.Mvvm(现正式更名为CommunityToolkit.Mvvm)是微软官方推出的轻量级 MVVM 框架,前身是 Windows Community Toolkit 的核心组件,专为 WPF、WinUI、MAUI 等 .NET 桌面/跨平台应用设计。其核心目标是简化 MVVM 架构实现,减少样板代码,实现 UI 与业务逻辑的彻底解耦,是当前 WPF 开发中官方推荐的 MVVM 解决方案。

关键更名说明(必看)

重要提示:2021 年后,Microsoft.ToolKit.Mvvm 正式更名为 CommunityToolkit.Mvvm,两者本质是同一个框架,由同一团队维护,功能完全兼容、无缝衔接。旧包Microsoft.ToolKit.Mvvm 已被 NuGet 标记为废弃,不再更新,当前开发需使用新包名 CommunityToolkit.Mvvm(当前最新稳定版 8.4.2)。

核心特性
特性说明
极简无样板基于编译时源生成,通过 [ObservableProperty]、[RelayCommand]等特性,自动生成属性通知、命令绑定代码,无需手动实现接口
高性能基于编译时源生成,无反射开销,性能优于基于反射的 MVVM 框架,且接近手写实现
异步友好原生支持 AsyncRelayCommand,无需手动封装,轻松处理异步操作(如 API 请求、数据库查询),避免 UI 阻塞
强兼容性基于 .NET Standard 2.0+ 构建,兼容 .NET Core 3.0+、.NET 5+ 及后续版本,完美适配 WPF 所有版本,支持跨平台(WinUI、MAUI 等)
功能完备内置数据验证(ObservableValidator)、消息通信(WeakReferenceMessenger)、依赖注入(Ioc),满足各类 WPF 项目需求
框架优势(对比旧版及其他 MVVM 框架)
  • 对比旧版 Microsoft.ToolKit.Mvvm:新包 CommunityToolkit.Mvvm 持续迭代,新增异步命令、编译时生成等核心功能,修复旧版兼容性问题,是官方唯一维护版本

  • 对比 MvvmLight:无需手动编写 Set() 方法,编译时生成代码更高效,无反射开销,且有微软官方支持,替代 MvvmLight 成为新项目首选

  • 对比 Prism:轻量级无依赖,学习曲线平缓,无需引入复杂的架构概念,适合中小型 WPF 项目快速开发;若需复杂导航,可灵活集成


1.2 核心架构原理(沿用 MVVM 三层架构)
三层架构解析(贴合 WPF 实际开发)

Microsoft.ToolKit.Mvvm(CommunityToolkit.Mvvm)严格遵循 MVVM 三层架构,各层职责清晰、完全解耦,结合 WPF 绑定特性优化设计,具体分层如下:

【View 层】:负责 UI 布局与绑定,仅包含 XAML 界面和少量后台代码(仅用于绑定 DataContext、初始化,无任何业务逻辑),通过 DataContext 与 ViewModel 实现双向绑定,完全不依赖 ViewModel

【ViewModel 层】:核心中间层,基于框架核心组件实现属性通知、命令绑定、业务逻辑、状态管理,通过服务调用与 Model 层交互,不直接操作 UI,可独立测试

【Model 层】:纯数据与核心逻辑层,包含实体类(POCO)、数据访问(数据库/API)、业务规则,完全无 UI 依赖,可在不同项目中复用

关键技术原理(框架核心组件)
(1)属性通知机制(ObservableObject + 特性生成)

框架核心基类 ObservableObject 实现了 INotifyPropertyChanged 接口,封装了属性变更通知逻辑。结合[ObservableProperty] 特性,可自动生成属性的 get/set 方法及属性变更通知,彻底消除手写样板代码。

核心用法与源码解析:

/// <summary>
/// 可观察对象基类,实现 INotifyPropertyChanged 接口
/// 作用:属性变化时通知UI自动刷新,是所有ViewModel的基类
/// </summary>
public class ObservableObject : INotifyPropertyChanged
{
    // 属性变更事件:属性值改变后触发,通知UI更新
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// 触发属性变更通知
    /// [CallerMemberName]:自动获取调用者的属性名,避免手写字符串出错
    /// </summary>
    /// <param name="propertyName">属性名(自动获取)</param>
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    /// <summary>
    /// 赋值 + 触发属性通知(简化写法)
    /// </summary>
    protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName] string propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals(field, newValue))
            return false;
        field = newValue;
        OnPropertyChanged(propertyName);
        return true;
    }
}

// 实际开发用法(无需手写get/set和通知逻辑)
public partial class MainViewModel : ObservableObject
{
    // [ObservableProperty] 特性自动生成 public string Title { get; set; } 及通知逻辑
    [ObservableProperty]
    private string _title = "WPF + CommunityToolkit.Mvvm 实战";

    // 自动生成 Count 属性及 OnCountChanged 方法(可选重写)
    [ObservableProperty]
    private int _count = 0;
}

关键亮点:通过 partial 类配合编译时生成,无需手动编写属性通知代码,减少人为错误,提升开发效率;支持重写自动生成的变更通知方法,灵活扩展逻辑。

(2)命令绑定原理(RelayCommand + AsyncRelayCommand)

框架提供RelayCommand(同步命令)和 AsyncRelayCommand(异步命令),均实现 ICommand 接口,无需手动实现 CanExecute 和 Execute 方法,可直接绑定到 WPF 控件(如 Button、MenuItem)。

核心用法与源码解析:

/// <summary>
/// 同步中继命令:包装同步方法,实现命令绑定
/// </summary>
public class RelayCommand : ICommand
{
    private readonly Action _execute;
    private readonly Func<bool> _canExecute;

    public event EventHandler CanExecuteChanged;

    // 构造方法:传入执行方法和可执行条件(可执行条件可选)
    public RelayCommand(Action execute, Func<bool> canExecute = null)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }

    // 判断命令是否可执行(控件自动启用/禁用)
    public bool CanExecute(object parameter) => _canExecute?.Invoke() ?? true;

    // 执行命令逻辑
    public void Execute(object parameter) => _execute();

    // 手动刷新命令可执行状态
    public void NotifyCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}

// 异步中继命令(原生支持异步操作,避免UI阻塞)
public class AsyncRelayCommand : ICommand
{
    private readonly Func<Task> _execute;
    private readonly Func<bool> _canExecute;
    private bool _isExecuting;

    // 核心逻辑:异步执行方法,自动管理执行状态
    public async void Execute(object parameter)
    {
        if (!CanExecute(parameter) || _isExecuting)
            return;
        _isExecuting = true;
        try
        {
            await _execute();
        }
        finally
        {
            _isExecuting = false;
            NotifyCanExecuteChanged(); // 执行完成后刷新状态
        }
    }

    public bool CanExecute(object parameter) => !_isExecuting && (_canExecute?.Invoke() ?? true);

    public event EventHandler CanExecuteChanged;

    public void NotifyCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}

// 实际开发用法(特性自动生成命令)
public partial class MainViewModel : ObservableObject
{
    [ObservableProperty]
    private int _count = 0;

    // [RelayCommand] 特性自动生成 IncrementCommand(ICommand 类型)
    [RelayCommand]
    private void Increment()
    {
        Count++;
    }

    // 带可执行条件的命令
    [RelayCommand(CanExecute = nameof(CanDelete))]
    private void Delete()
    {
        // 删除逻辑
    }

    private bool CanDelete() => Count > 0;

    // 异步命令(自动生成 LoadDataCommand)
    [RelayCommand]
    private async Task LoadDataAsync()
    {
        // 模拟异步请求(如API调用)
        await Task.Delay(1000);
        Title = "数据加载完成";
    }
}
(3)数据验证机制(ObservableValidator)

框架内置 ObservableValidator 类,继承自 ObservableObject,支持数据注解(如 [Required]、[MinLength]),自动收集验证错误,触发验证通知,无缝集成 WPF 绑定验证。

核心用法:

using CommunityToolkit.Mvvm.ComponentModel;
using System.ComponentModel.DataAnnotations;

public partial class LoginViewModel : ObservableValidator
{
    // 结合数据注解和 ObservableProperty 特性,自动实现验证
    [ObservableProperty]
    [Required(ErrorMessage = "用户名不能为空")]
    [StringLength(20, ErrorMessage = "用户名长度不能超过20个字符")]
    private string _username;

    [ObservableProperty]
    [Required(ErrorMessage = "密码不能为空")]
    [MinLength(6, ErrorMessage = "密码长度至少6位")]
    private string _password;

    [RelayCommand]
    private async Task LoginAsync()
    {
        // 验证所有属性
        ValidateAllProperties();
        if (HasErrors) return; // 有验证错误,终止执行

        // 执行登录逻辑
        await AuthService.Login(Username, Password);
    }
}
(4)消息通信机制(WeakReferenceMessenger)

框架提供 WeakReferenceMessenger 消息总线,基于弱引用设计,实现 ViewModel 之间、View 与 ViewModel 之间的解耦通信,避免强引用导致的内存泄漏,用法简洁高效。同时支持 StrongReferenceMessenger(高性能,适合无内存泄漏风险的场景)。

核心用法:

// 1. 定义消息类型(可携带数据)
public class MessageModel
{
    public string Content { get; set; }
}

// 2. 发送消息(ViewModel A)
[RelayCommand]
private void SendMessage()
{
    WeakReferenceMessenger.Default.Send(new MessageModel { Content = "数据更新通知" });
}

// 3. 接收消息(ViewModel B)
public MainViewModel()
{
    // 注册消息监听,自动解耦
    WeakReferenceMessenger.Default.Register<MessageModel>(this, (recipient, message) =>
    {
        // 处理消息
        Title = message.Content;
    });
}

// 4. 注销消息(避免内存泄漏,在页面关闭时调用)
public void Cleanup()
{
    WeakReferenceMessenger.Default.Unregister<MessageModel>(this);
    // 注销所有消息监听(推荐)
    // WeakReferenceMessenger.Default.UnregisterAll(this);
}

1.3 环境搭建与安装

开发环境要求
  • Visual Studio 2026

  • .NET 6+/7+/8+(推荐 .NET 8,长期支持版本,兼容性最佳,与 CommunityToolkit.Mvvm 8.4.2 完美适配)

  • WPF 项目(默认启用 XAML 绑定,支持 MVVM 开发)

安装 CommunityToolkit.Mvvm(原 Microsoft.ToolKit.Mvvm)

由于旧包Microsoft.ToolKit.Mvvm 已废弃,当前开发需安装新包(最新稳定版 8.4.2),三种安装方式任选其一:

  1. NuGet 包管理器(推荐):右键 WPF 项目 → 管理 NuGet 包 → 搜索 CommunityToolkit.Mvvm → 安装最新稳定版(8.4.2)

  2. .NET CLI 命令行:打开终端,进入项目目录,执行命令: dotnet add package CommunityToolkit.Mvvm --version 8.4.2

  3. 项目文件直接引用:编辑 .csproj 文件,添加如下配置: <ItemGroup> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" /> </ItemGroup>


第二部分:完整实战项目(WPF + CommunityToolkit.Mvvm)

2.1 项目概述

本实战项目为「WPF 任务管理系统」,基于 CommunityToolkit.Mvvm(原 Microsoft.ToolKit.Mvvm)8.4.2 版本开发,实现任务的增、删、改、查、数据验证、异步加载等核心功能,严格遵循 MVVM 三层架构,贴合实际开发场景,演示框架核心组件的用法。

2.2 项目结构(标准 MVVM 分层)
00024.WPF + Microsoft.ToolKit.Mvvm 技术指南与实战项目/
├── Helpers/                    // 辅助工具类
│   ├── BooleanToVisibilityConverter.cs  // 布尔值转可见性转换器
│   └── MessengerHelper.cs      // 消息通信辅助类(封装WeakReferenceMessenger)
├── Models/                     // 数据模型层(纯数据,无UI依赖)
│   └── TaskItem.cs             // 任务实体类
├── ViewModels/                 // 视图模型层(业务逻辑核心)
│   ├── MainViewModel.cs        // 主窗口ViewModel
│   └── TaskViewModel.cs        // 任务列表ViewModel(拆分复用)
├── Views/                      // 视图层(纯UI,无业务逻辑)
│   ├── MainWindow.xaml         // 主窗口(任务列表+新增表单)
│   ├── MainWindow.xaml.cs      // 主窗口后台代码(仅初始化)
│   └── TaskItemView.xaml       // 任务项子视图(可选,用于复用)
├── App.xaml                    // 应用程序资源配置文件
├── App.xaml.cs                 // 应用程序入口代码文件
└── AssemblyInfo.cs             // 程序集信息配置文件
2.3 各层实现代码
2.3.1 Model 层:TaskItem.cs(任务实体)

纯数据实体,继承 ObservableObject 实现属性通知,支持 UI 绑定刷新,无任何业务逻辑和 UI 依赖。

using CommunityToolkit.Mvvm.ComponentModel;

namespace _00024.WPF___Microsoft.ToolKit.Mvvm_技术指南与实战项目.Models
{
    /// <summary>
    /// 任务实体类(Model层)
    /// 继承ObservableObject,实现属性变更通知
    /// </summary>
    public class TaskItem : ObservableObject
    {
        // 私有字段
        private int _id;              // 任务唯一标识
        private string _title;        // 任务标题
        private string _description;  // 任务描述
        private DateTime _dueDate;    // 截止日期
        private bool _isCompleted;    // 完成状态

        /// <summary>
        /// 任务ID(唯一标识)
        /// </summary>
        public int Id
        {
            get => _id;              // 获取任务ID
            set => SetProperty(ref _id, value);  // 设置任务ID并通知变更
        }

        /// <summary>
        /// 任务标题
        /// </summary>
        public string Title
        {
            get => _title;           // 获取标题
            set => SetProperty(ref _title, value);  // 设置标题并通知变更
        }

        /// <summary>
        /// 任务描述
        /// </summary>
        public string Description
        {
            get => _description;     // 获取描述
            set => SetProperty(ref _description, value);  // 设置描述并通知变更
        }

        /// <summary>
        /// 截止日期
        /// </summary>
        public DateTime DueDate
        {
            get => _dueDate;         // 获取截止日期
            set => SetProperty(ref _dueDate, value);  // 设置截止日期并通知变更
        }

        /// <summary>
        /// 任务完成状态
        /// </summary>
        public bool IsCompleted
        {
            get => _isCompleted;     // 获取完成状态
            set => SetProperty(ref _isCompleted, value);  // 设置完成状态并通知变更
        }

        /// <summary>
        /// 构造函数(简化对象创建)
        /// </summary>
        /// <param name="id">任务ID</param>
        /// <param name="title">任务标题</param>
        /// <param name="description">任务描述</param>
        /// <param name="dueDate">截止日期</param>
        /// <param name="isCompleted">是否已完成,默认false</param>
        public TaskItem(int id, string title, string description, DateTime dueDate, bool isCompleted = false)
        {
            Id = id;                  // 初始化ID
            Title = title;            // 初始化标题
            Description = description;  // 初始化描述
            DueDate = dueDate;        // 初始化截止日期
            IsCompleted = isCompleted; // 初始化完成状态
        }
    }
}

2.3.2 ViewModel 层:核心业务逻辑
(1)TaskViewModel.cs(任务列表业务)

处理任务的增、删、改逻辑,使用框架特性自动生成属性和命令,实现业务逻辑与 UI 解耦。

using _00024.WPF___Microsoft.ToolKit.Mvvm_技术指南与实战项目.Helpers;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;

namespace _00024.WPF___Microsoft.ToolKit.Mvvm_技术指南与实战项目.Models
{
    /// <summary>
    /// 任务列表ViewModel(处理任务相关业务逻辑)
    /// 继承ObservableObject,实现属性通知
    /// </summary>
    public partial class TaskViewModel : ObservableObject
    {
        public TaskViewModel()
        {
            // 注册删除消息
            MessengerHelper.Register<DeleteTaskMessage>(this, OnDeleteTaskReceived);
        }

        // 接收删除消息 → 执行删除
        private void OnDeleteTaskReceived(DeleteTaskMessage message)
        {
            ExecuteDeleteTask(message.TaskId);
        }

        // 真正执行删除
        private void ExecuteDeleteTask(int taskId)
        {
            var taskToRemove = Tasks.FirstOrDefault(t => t.Id == taskId);
            if (taskToRemove != null)
                Tasks.Remove(taskToRemove);
        }

        // 任务集合(绑定到UI的ListView,自动刷新)
        [ObservableProperty]
        private ObservableCollection<TaskItem> _tasks = new();

        // 新增任务表单属性(绑定到UI输入框)
        [ObservableProperty]
        private string _newTaskTitle = string.Empty;

        [ObservableProperty]
        private string _newTaskDescription = string.Empty;

        [ObservableProperty]
        private DateTime _newTaskDueDate = DateTime.Now.AddDays(1);

        // 加载状态(用于异步加载时显示加载提示)
        [ObservableProperty]
        private bool _isLoading;

        // 新增任务命令
        [RelayCommand]
        private void AddTask()
        {
            if (string.IsNullOrWhiteSpace(NewTaskTitle))
                return;

            var newTask = new TaskItem(
                id: Tasks.Count + 1,
                title: NewTaskTitle,
                description: NewTaskDescription,
                dueDate: NewTaskDueDate
            );

            Tasks.Add(newTask);

            NewTaskTitle = string.Empty;
            NewTaskDescription = string.Empty;
        }

        // 删除任务命令 → 只发消息
        [RelayCommand]
        private void DeleteTask(int taskId)
        {
            MessengerHelper.Send(new DeleteTaskMessage(taskId));
        }

        // 标记完成
        [RelayCommand]
        private void ToggleTaskCompleted(TaskItem task)
        {
            if (task != null)
                task.IsCompleted = !task.IsCompleted;
        }

        // 异步加载
        [RelayCommand(AllowConcurrentExecutions = false)]
        private async Task LoadTasksAsync()
        {
            IsLoading = true;
            try
            {
                await Task.Delay(1500);

                var mockTasks = new List<TaskItem>
                {
                    new TaskItem(1, "学习CommunityToolkit.Mvvm", "掌握属性通知、命令绑定核心用法", DateTime.Now.AddDays(2),true),
                    new TaskItem(2, "完成WPF实战项目", "实现任务管理系统所有功能", DateTime.Now.AddDays(5),false),
                    new TaskItem(3, "整理技术文档", "总结框架使用技巧和注意事项", DateTime.Now.AddDays(3),true)
                };

                Tasks.Clear();
                foreach (var task in mockTasks)
                {
                    Tasks.Add(task);
                }
            }
            catch (Exception ex)
            {
                System.Windows.MessageBox.Show($"异步加载失败:{ex.Message}", "加载异常");
            }
            finally
            {
                IsLoading = false;
            }
        }
    }

    // 消息类(你也可以单独放文件)
    public class DeleteTaskMessage
    {
        public int TaskId { get; set; }
        public DeleteTaskMessage(int taskId) => TaskId = taskId;
    }
}
(2)MainViewModel.cs(主窗口业务)

主窗口 ViewModel,嵌入任务列表 ViewModel,管理主窗口全局状态(如窗口标题),实现 ViewModel 拆分复用。

using _00024.WPF___Microsoft.ToolKit.Mvvm_技术指南与实战项目.Models;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

namespace _00024.WPF___Microsoft.ToolKit.Mvvm_技术指南与实战项目.ViewModels
{
    /// <summary>
    /// 主窗口ViewModel(全局状态管理)
    /// </summary>
    public partial class MainViewModel : ObservableObject
    {
        // 主窗口标题(绑定到UI标题栏)
        [ObservableProperty]
        private string _appTitle = "WPF + CommunityToolkit.Mvvm 任务管理系统";

        // 嵌入任务列表ViewModel(实现业务逻辑拆分)
        public TaskViewModel TaskVm { get; } = new();

        // 退出应用命令
        [RelayCommand]
        private void ExitApp()
        {
            System.Windows.Application.Current.Shutdown();
        }
    }
}

2.3.3 View 层:XAML 界面(纯UI,无业务逻辑)
(1)MainWindow.xaml(主窗口)

纯 XAML 布局,修复语法错误,通过 DataContext 绑定 ViewModel,实现 UI 与业务逻辑的完全解耦,所有交互通过命令绑定实现。

  <Window.Resources>
      <helper:BooleanToVisibilityConverter x:Key="InvertBool" Invert="True"/>
  </Window.Resources>

  <Window.DataContext>
      <vm:MainViewModel />
  </Window.DataContext>

  <Grid Margin="20">
      <Grid.RowDefinitions>
          <RowDefinition Height="Auto"/>
          <RowDefinition Height="Auto"/>
          <RowDefinition Height="Auto"/>
          <RowDefinition Height="*"/>
      </Grid.RowDefinitions>

      <!-- 标题 -->
      <TextBlock Text="{Binding AppTitle}" FontSize="26" FontWeight="Bold" Margin="0 0 0 15"/>

      <!-- 异步加载任务按钮 -->
      <Button Grid.Row="1" Content="异步加载任务数据" 
              Command="{Binding TaskVm.LoadTasksCommand}"
              Visibility="{Binding TaskVm.IsLoading, Converter={StaticResource InvertBool}}"
              Background="#2196F3" Foreground="White" 
              Padding="10" Margin="0 0 0 15"/>

      <!-- 新增任务区域 -->
      <GroupBox Grid.Row="2" Header="新增任务" Margin="0 0 0 15" Padding="10">
          <StackPanel>
              <TextBox Text="{Binding TaskVm.NewTaskTitle, UpdateSourceTrigger=PropertyChanged}" 
                        Height="32" Margin="0 0 0 8"/>
              <TextBox Text="{Binding TaskVm.NewTaskDescription, UpdateSourceTrigger=PropertyChanged}" 
                       Height="32" Margin="0 0 0 8"/>
              <DatePicker SelectedDate="{Binding TaskVm.NewTaskDueDate}" Height="32" Margin="0 0 0 8"/>
              <Button Content="添加任务" Command="{Binding TaskVm.AddTaskCommand}" 
                      Background="#4CAF50" Foreground="White" Height="34"/>
          </StackPanel>
      </GroupBox>

      <!-- 任务列表 -->
      <ListView Grid.Row="3" ItemsSource="{Binding TaskVm.Tasks}" Margin="0 10 0 0">
          <ListView.View>
              <GridView>
                  <GridViewColumn Header="ID" DisplayMemberBinding="{Binding Id}" Width="60"/>
                  <GridViewColumn Header="标题" DisplayMemberBinding="{Binding Title}" Width="200"/>
                  <GridViewColumn Header="描述" DisplayMemberBinding="{Binding Description}" Width="280"/>
                  <GridViewColumn Header="截止日期" DisplayMemberBinding="{Binding DueDate, StringFormat=yyyy-MM-dd}" Width="120"/>
                  <GridViewColumn Header="完成" Width="80">
                      <GridViewColumn.CellTemplate>
                          <DataTemplate>
                              <CheckBox IsChecked="{Binding IsCompleted, Mode=TwoWay}" HorizontalAlignment="Center"/>
                          </DataTemplate>
                      </GridViewColumn.CellTemplate>
                  </GridViewColumn>
                  <GridViewColumn Header="操作" Width="80">
                      <GridViewColumn.CellTemplate>
                          <DataTemplate>
                              <Button Content="删除" Background="#F44336" Foreground="White"
                                      Command="{Binding DataContext.TaskVm.DeleteTaskCommand, RelativeSource={RelativeSource AncestorType=ListView}}"
                                      CommandParameter="{Binding Id}"/>
                          </DataTemplate>
                      </GridViewColumn.CellTemplate>
                  </GridViewColumn>
              </GridView>
          </ListView.View>
      </ListView>
  </Grid>
(2)BooleanToVisibilityConverter.cs(辅助转换器)

补充缺失的转换器,解决按钮禁用状态绑定问题,放在 Helpers 文件夹下。

using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace _00024.WPF___Microsoft.ToolKit.Mvvm_技术指南与实战项目.Helpers
{
    /// <summary>
    /// 布尔值转可见性转换器
    /// </summary>
    public class BooleanToVisibilityConverter : IValueConverter
    {
        /// <summary>
        /// 是否反转布尔值判断
        /// </summary>
        public bool Invert { get; set; }

        /// <summary>
        /// 正向转换:bool 转为 Visibility
        /// </summary>
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            bool boolValue = (bool)value;
            // 开启反转则取反
            if (Invert) boolValue = !boolValue;
            // true 显示,false 折叠
            return boolValue ? Visibility.Visible : Visibility.Collapsed;
        }

        /// <summary>
        /// 反向转换 暂不实现
        /// </summary>
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}
(3)MainWindow.xaml.cs(后台代码)

仅包含初始化代码,无任何业务逻辑,符合 MVVM 规范。

using System.Windows;

namespace _00024.WPF___Microsoft.ToolKit.Mvvm_技术指南与实战项目.Views
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

2.3.4 辅助类:MessengerHelper.cs(消息通信封装)

修正命名空间与项目一致,优化代码逻辑,封装WeakReferenceMessenger,简化消息发送、接收、注销操作,避免重复代码,防止内存泄漏,重点修复泛型约束错误(添加引用类型约束 where TMessage : class)。

using CommunityToolkit.Mvvm.Messaging;

namespace _00024.WPF___Microsoft.ToolKit.Mvvm_技术指南与实战项目.Helpers
{
    /// <summary>
    /// 消息通信辅助类
    /// 用于ViewModel和页面解耦通信,防止内存泄漏
    /// </summary>
    public static class MessengerHelper
    {
        // 默认消息标识(代替null,避免框架报错)
        private const string DefaultToken = "";

        /// <summary>
        /// 注册全局消息(无Token)
        /// </summary>
        /// <typeparam name="TMessage">消息类型</typeparam>
        /// <param name="recipient">消息接收者</param>
        /// <param name="action">收到消息后的处理方法</param>
        public static void Register<TMessage>(object recipient, Action<TMessage> action) where TMessage : class
        {
            if (recipient == null)
                throw new ArgumentNullException(nameof(recipient), "接收者不能为null");
            if (action == null)
                throw new ArgumentNullException(nameof(action), "消息处理逻辑不能为null");

            WeakReferenceMessenger.Default.Register<object, TMessage, string>(
                recipient,
                token: DefaultToken,
                handler: (_, message) => action(message));
        }

        /// <summary>
        /// 注册带Token的消息(分组隔离)
        /// </summary>
        public static void Register<TMessage>(object recipient, string token, Action<TMessage> action) where TMessage : class
        {
            if (recipient == null)
                throw new ArgumentNullException(nameof(recipient), "接收者不能为null");
            if (string.IsNullOrWhiteSpace(token))
                throw new ArgumentException("消息标识Token不能为空", nameof(token));
            if (action == null)
                throw new ArgumentNullException(nameof(action), "消息处理逻辑不能为null");

            WeakReferenceMessenger.Default.Register<object, TMessage, string>(
                recipient,
                token,
                handler: (_, message) => action(message));
        }

        /// <summary>
        /// 发送全局消息
        /// </summary>
        public static void Send<TMessage>(TMessage message) where TMessage : class
        {
            if (message == null)
                throw new ArgumentNullException(nameof(message), "消息不能为null");

            WeakReferenceMessenger.Default.Send<TMessage, string>(message, token: DefaultToken);
        }

        /// <summary>
        /// 发送带Token的消息
        /// </summary>
        public static void Send<TMessage>(TMessage message, string token) where TMessage : class
        {
            if (message == null)
                throw new ArgumentNullException(nameof(message), "消息不能为null");
            if (string.IsNullOrWhiteSpace(token))
                throw new ArgumentException("消息标识Token不能为空", nameof(token));

            WeakReferenceMessenger.Default.Send<TMessage, string>(message, token);
        }

        /// <summary>
        /// 注销接收者的所有消息订阅
        /// </summary>
        public static void UnregisterAll(object recipient)
        {
            if (recipient == null)
                throw new ArgumentNullException(nameof(recipient), "接收者不能为null");

            WeakReferenceMessenger.Default.UnregisterAll(recipient);
        }

        /// <summary>
        /// 注销指定类型的全局消息
        /// </summary>
        public static void Unregister<TMessage>(object recipient) where TMessage : class
        {
            if (recipient == null)
                throw new ArgumentNullException(nameof(recipient), "接收者不能为null");

            WeakReferenceMessenger.Default.Unregister<TMessage, string>(recipient, token: DefaultToken);
        }
    }
}
2.4 项目运行与功能说明
2.4.1 运行步骤
  1. 创建 WPF 项目(.NET 8),安装 CommunityToolkit.Mvvm 8.4.2 包;

  2. 按照上述项目结构,创建 Models、ViewModels、Views、Helpers 文件夹及对应文件;

  3. 复制上述代码到对应文件,确保命名空间与项目一致;

  4. 设置 MainWindow.xaml 为启动窗口,运行项目。

2.4.2 核心功能演示
  • 异步加载任务:点击「加载任务数据」按钮,触发异步命令,加载过程中按钮禁用,加载完成后任务列表显示模拟数据;

  • 新增任务:输入任务标题、描述、截止日期,点击「添加任务」,任务列表实时更新,表单自动清空;

  • 删除任务:点击任务行的「删除」按钮,删除对应任务,列表自动刷新;

  • 标记任务完成:勾选任务行的复选框,切换任务完成状态,UI 实时同步;

  • 数据绑定:所有 UI 元素与 ViewModel 属性双向绑定,属性变化时 UI 自动刷新,无需手动操作。


第三部分:高级特性与最佳实践

3.1 高级特性用法
3.1.1 关联属性通知(NotifyPropertyChangedFor)

当一个属性的变化依赖另一个属性时,使用 [NotifyPropertyChangedFor] 特性,自动触发关联属性的变更通知。

public partial class UserViewModel : ObservableObject
{
    [ObservableProperty]
    private string _firstName;

    [ObservableProperty]
    private string _lastName;

    // 当FirstName或LastName变化时,自动触发FullName的变更通知
    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(FirstName))]
    [NotifyPropertyChangedFor(nameof(LastName))]
    private string _fullName;

    // 重写FullName的set方法,实现拼接逻辑
    partial void OnFullNameChanged(string value)
    {
        FullName = $"{FirstName} {LastName}";
    }
}
3.1.2 命令参数传递与多参数支持

支持传递单个参数、多个参数(通过匿名对象或实体类),满足复杂交互场景。

// 带多个参数的命令
[RelayCommand]
private void EditTask(int id, string title)
{
    // 编辑逻辑
}

// XAML绑定(传递多个参数)
<Button Content="编辑">
    <Button.CommandParameter>
        <Tuple Int32="{Binding Id}" String="{Binding Title}"/>
    </Button.CommandParameter>
</Button>
3.1.3 数据验证进阶(自定义验证规则)

除了数据注解,还可自定义验证规则,实现复杂业务场景的验证需求。

public partial class TaskViewModel : ObservableValidator
{
    [ObservableProperty]
    [Required(ErrorMessage = "截止日期不能为空")]
    private DateTime _dueDate;

    // 自定义验证规则:截止日期不能小于当前日期
    partial void OnDueDateChanged(DateTime value)
    {
        if (value < DateTime.Now.Date)
        {
            AddValidationError(nameof(DueDate), "截止日期不能小于当前日期");
        }
        else
        {
            ClearValidationError(nameof(DueDate));
        }
    }
}
3.2 最佳实践
  • ViewModel 拆分复用:将复杂业务拆分为多个 ViewModel(如本项目的 MainViewModel 和 TaskViewModel),提升代码复用性和可维护性;

  • 优先使用特性生成:尽量使用[ObservableProperty]、[RelayCommand] 等特性,减少手写样板代码,避免人为错误;

  • 异步操作必用 AsyncRelayCommand:所有异步操作(如 API 请求、数据库查询)必须使用 AsyncRelayCommand,避免 UI 阻塞;

  • 及时注销消息监听:在页面关闭或 ViewModel 销毁时,注销所有消息监听,防止内存泄漏;

  • 避免 ViewModel 依赖 View:ViewModel 中不能直接引用 View(如实例化 Window、操作控件),需通过命令、消息实现通信;

  • 集合使用 ObservableCollection:绑定到 UI 列表的集合必须使用 ObservableCollection<T>,确保集合变更时 UI 自动刷新;

  • 统一包版本:确保项目中 CommunityToolkit.Mvvm 版本统一(推荐 8.4.2),避免版本冲突。

3.3 常见问题与解决方案
常见问题解决方案
属性通知不生效1. 确保 ViewModel 是 partial 类;2. 确保属性添加了 [ObservableProperty] 特性;3. 检查 DataContext 绑定是否正确
命令绑定后按钮不可用1. 检查 CanExecute 条件是否满足;2. 手动调用命令的 NotifyCanExecuteChanged 方法刷新状态;3. 检查命令绑定路径是否正确
异步命令执行后 UI 不刷新确保异步方法使用 await 关键字,且属性变更在主线程执行(框架自动处理,无需手动切换线程)
内存泄漏1. 及时注销消息监听;2. 避免强引用持有 ViewModel;3. 使用 WeakReferenceMessenger 而非自定义事件

第四部分:总结与扩展学习

4.1 核心要点回顾
  • 框架更名:Microsoft.ToolKit.Mvvm 已正式更名为 CommunityToolkit.Mvvm,旧包废弃,新项目需使用新包;

  • 核心组件:ObservableObject(属性通知)、RelayCommand/AsyncRelayCommand(命令绑定)、ObservableValidator(数据验证)、WeakReferenceMessenger(消息通信);

  • 核心优势:编译时生成、无反射、异步友好、轻量级无依赖,是 WPF 新项目 MVVM 首选框架;

  • 实战核心:严格遵循 MVVM 三层架构,View 纯 UI、ViewModel 纯逻辑、Model 纯数据,实现完全解耦。

4.2 扩展学习方向
  1. 依赖注入:集成 Microsoft.Extensions.DependencyInjection,实现 ViewModel、服务(如 API 调用、数据库访问)的依赖注入,提升项目可测试性;

  2. 数据持久化:结合 SQLite、Entity Framework Core,实现任务数据的本地存储,完善项目功能;

  3. UI 美化:集成 HandyControl、MaterialDesignInXamlToolkit 等 UI 框架,打造现代化 WPF 界面;

  4. 多页面导航:结合 WPF 的 Frame、ContentControl 或 Prism 框架,实现多页面应用的导航管理;

  5. 单元测试:针对 ViewModel 编写单元测试,验证业务逻辑的正确性,提升项目稳定性。

4.3 资源推荐

通过本文的学习,你已掌握 Microsoft.ToolKit.Mvvm(CommunityToolkit.Mvvm)的核心用法和 WPF MVVM 开发的最佳实践,可快速上手开发中小型 WPF 项目。建议结合实战项目多动手练习,深入理解框架特性,提升开发效率和代码质量。


👋 关注我!持续分享 C# 实战技巧、代码示例 & 技术干货

  • 获取示例代码,轻松上手!
WPF CommunityToolkit.Mvvm CommunityToolkit.Mvvm(以下简称Toolkit)是WPF最有名的两个框架,一个是Prism,另一个就是Toolkit。Prism可以看我的Prism详解WPF Prims框架详解。 阅读详情

相关推荐

CommunityToolkit.MVVM

MVVM工具包,以前名为 Microsoft.Toolkit.Mvvm 由 Microsoft 维护和发布,是 .NET Foundation 的一部分。支持:.NET Standard 2.0、 .NET Standard 2.1 和 .NET 6(UI Framework 不可知,基本使用没有问题/编译特性用不了)注:可以当做MvvmLight的替代品 大部分方法一样。

代码改变世界 4402

浅谈WPF之MVVM工具包

点击项目右键,选择“管理NuGet程序包”,打开NuGet管理窗口,然后搜索“CommunityToolkit.Mvvm”,进行安装即可,目前最新版本为8.2.2。在之前的WPF示例中,都会用到一个MVVM框,也是一个比较常的MVVM框架,就是MVVM工具包【CommunityToolkit.Mvvm】,今天专门以一个简单的小例子,简述一下MVVM工具包的常见用法,仅供学习分享使用,如有不足之处,还请指正。在本实例中,只是简单介绍了MVVM工具包的使用,如若想具体了解更详细内容,可参考官方文档,

绳锯木断,水滴石穿,专心写文,无问西东!!! 2766

WPF -- MVVM框架 - CommunityToolkit.Mvvm

使用依赖项注入,它包括创建大量注入后端类的服务,这允许使用这些服务的代码不依赖于这些服务的实现详细信息,并且还可以轻松地交换这些服务的具体实现。添加引用 using CommunityToolkit.Mvvm.DependencyInjection;MVVM 工具包由 Microsoft 维护和发布,也是 .NET Foundation 的一部分。此模式还使平台特定的功能更易于后端代码使用,方法是通过服务进行抽象化,然后在需要时注入这些功能。目标,用于在 .NET 6 上运行时启用更多内部优化。...

哈怂的博客 1万+

Wpf在.Net 6 下该用哪个Mvvm框架-Microsoft.Toolkit.Mvvm

前言 在Wpf下最常使用的就是Mvvm模式了,有自己造轮子构建Mvvm框架的,也有使用现成的开源项目,我之前一直使用的是轻量级的MvvmLight了,这个框架还是非常不错的,使用也简单,不占用太大空间,其中最喜欢的莫过于全局Messenger了,可谓是神器。最近有个项目使用.Net6开发,在NuGet发现MvvmLight已经很久不更新了,上一次还是2018年9月12日,偶然发现微软官方出的Microsoft.Toolkit.Mvvm 完全继承了Messenger的优良传统,这是一个官方社区套件(Windo

chenlinhsfl的博客 1万+

在.NET Framework WPF项目中利用Microsoft.Toolkit.Mvvm生成器提升MVVM开发效率

在XAML客户端开发中,MVVM模式是实现数据绑定和UI逻辑分离的核心架构。其原理是通过数据通知机制(如INotifyPropertyChanged接口)和命令模式(ICommand)实现视图与数据的动态同步。这项技术的核心价值在于提升代码的可维护性、可测试性和开发效率。然而,手动实现属性通知和命令接口会引入大量重复的样板代码,容易出错且难以维护。针对这一痛点,C#源生成器技术应运而生,它能在编译时自动生成这些重复代码,实现零运行时开销。Microsoft.Toolkit.Mvvm正是这一技术的典型应用,它

weixin_34356555的博客 474

在.NET Framework项目中利用MVVM Toolkit源生成器告别样板代码

在XAML系UI框架开发中,MVVM(Model-View-ViewModel)模式是实现关注点分离的核心架构。其核心原理在于通过数据绑定与命令绑定,将视图逻辑与业务逻辑解耦,ViewModel作为桥梁负责状态管理与用户交互响应。传统手动实现需要为每个可绑定属性编写INotifyPropertyChanged通知代码,为每个命令创建ICommand实现类,这种重复劳动不仅降低开发效率,还容易因属性名硬编码引入错误。MVVM Toolkit的源生成器技术,作为C#编译时功能,通过[ObservableProp

weixin_34315485的博客 395

WPF之Microsoft.Toolkit.Mvvm(一)

第一步: 安装package从nuget:查询Microsoft.Toolkit.MVVM. 第二步: 在Model和ViewModel中引入命名空间 Model.cs ViewModel.cs 第三步: 在UI界面绑定数据源 小结: Microsoft继承了mvvm的属性绑定,命令绑定 、以及Messager(Weak...

wanxiweilai的博客 5443

C#WPF框架Microsoft.Toolkit.MvvM应用实例

本文实例演示C#WPF框架Microsoft.Toolkit.MvvM应用

qq_30725967的博客 1964

WPF MVVM框架

MVVM即Model-View-ViewModel,MVVM模式与MVP(Model-View-Presenter)模式相似,主要目的是分离视图(View)和模型(Model),具有低耦合、可重用性、独立开发、可测试性等优点。

hccee的博客 3430

别再算错了!深入SAP FI后台,看懂外币清账时汇兑损益的自动计算逻辑

刚接手一家跨国企业的SAP财务模块优化项目时,我遇到了一个棘手案例:每月末外币清账产生的汇兑损益金额总是与财务团队的预期存在差异。最令人困惑的是,系统似乎"擅自"计算出了1989.11元的损益,而财务人员反复手工验算都无法复现这个数字。这让我意识到,要真正掌握SAP FI的外币处理能力,必须深入理解系统后台的汇兑损益计算逻辑——不是停留在操作层面,而是穿透到算法内核。这个过程的精妙之处在于,损益金额不是简单比较两个时点的市场汇率,而是反映业务执行全周期的汇率影响。:损益金额与手工计算不符。

weixin_32932149的博客 473

量化回测框架三选一:Backtrader、Zipline、PyAlgoTrade,新手到底该跟谁学?

本文对比分析了三大量化回测框架Backtrader、Zipline和PyAlgoTrade,帮助新手选择最适合的入门工具。从安装配置、策略编写到文档查阅,详细解析各框架的特点和适用场景,为量化交易初学者提供实用指南。

weixin_28745309的博客 267

WPF使用Microsoft.Toolkit.Mvvm框架记录

为了解决WPF UI与程序逻辑之间得到解耦,所以使用Microsoft.Toolkit.Mvvm框架来实现,说真的开发逻辑真的有些不适应,不过理解就好。框架大体支持ICommand、IMessenger等。

爪哇盘古 1656

【图像超分】论文精读:RepSR: Training Efficient VGG-style Super-Resolution Networks with Structural Re-Paramete

摘要: 本文提出RepSR,一种基于结构重新参数化和批量归一化(BN)的高效VGG式超分辨率网络。针对SR任务中BN易引发伪影的问题,作者通过分析发现训练-测试阶段的不一致性是主因,并提出先使用小批量统计训练、后期切换为总体统计的优化策略。RepSR块设计包含干净的残差路径和带改进BN的扩展-压缩卷积路径,有效平衡了训练非线性与推理效率。实验表明,RepSR在不同模型规模下优于现有重参数化方法,且实际推理速度更快。该工作为SR提供了性能与效率兼优的解决方案,相关代码未开源。 关键词: 超分辨率、结构重参数化

主要更新底层视觉(去噪、超分等)相关的科研内容,形式为【论文精读】+【论文复现】 303

06 MAUI,WPF使用 MVVM Toolkit 框架 构建 MVVM 程序

注意发布的消息类型中,如果发布的是string类型,所有订阅了string类型的消息,都能接收到所有发布的string类型的消息。4.实现一个功能,页面点击按钮时,把TextBox的值传到后台页面,并且把该值绑定回来前端页面中。4.5另外一种取前端页面值的方式,但这种方式与MVVM无关,也能取到TextBox输入的值,4.8Address是TextBox定义的Name,随意起的名称。4.2在S71200ViewModel类中,添加一个命令,并且进行实例化。...

build2024 4761

STM32H750VB与AD5292通讯,写入阻值后,AD5292 的阻值没有变化,如何解决?

🏆 本文收录于《全栈Bug调优(实战版)》专栏,致力于分享我在项目实战过程中遇到的各类Bug及其原因,并提供切实有效的解决方案。无论你是初学者还是经验丰富的开发者,本文将为你指引出一条更高效的Bug修复之路,助你早日登顶,迈向财富自由的梦想🚀!同时,欢迎大家关注、收藏、订阅本专栏,更多精彩内容正在持续更新中。让我们一起进步,Up!Up!Up!    备注: 部分问题/难题源自互联网,经过精心筛选和整理,结合数位十多年大厂实战经验资深大佬经验总结所得,数条可行方案供所需之人参考。

**My Coding Family** 1098

【CANoe示例分析】0002_SOMEIPDemo

该工程由Vector官方提供,作为仿真SOME/IP节点的示例。Demo中介绍了两种仿真SOME/IP节点的方法,一种是基于arxml数据库的仿真,另一种是没有数据库(arxml、fibex)的仿真。无论是哪种形式的仿真,如果想要通过CAPL程序接收或者发送SOME/IP信息,都需要添加交互层的信息,这些信息可以通过添加DLL文件实现,DLL文件路径在CANoe安装目录下的Exec32文件中。

jasonj333 3514

WPF MVVM Community Toolkit. Mvvm 社区框架

Community Toolkit.Mvvm 框架摘要 该框架是微软提供的MVVM开发工具包,主要功能包括: 基础功能:提供ObservableObject基类实现属性通知,RelayCommand简化命令创建 源生成器:通过特性自动生成代码(需.NET 8+) [ObservableProperty]自动生成属性 [RelayCommand]自动生成命令 命令系统:支持同步/异步命令(AsyncRelayCommand)和CanExecute条件 验证功能:继承ObservableValidator实现数

weixin_48990772的博客 3451

【2026OD新机考】【不定滑窗】20260415-API请求日志去重分析【Py/Java/C++/C/JS/Go六种语言OD真题】【欧弟算法】全网注释最详细分类最全的华子OD真题题解

中找到所有的连续的相同元素,同时统计对应的连续相同元素对应的值的平均值。:当右指针为第一个元素(没有前一个元素),或者右指针对应的元素。某微服务系统的日志监控平台需分析API调用记录,日志含大量。相等,说明当前元素仍然属于同一个相同元素窗口,将当前元素值。中的所有元素均相等,并且计算窗口和情况即可。请实现一个函数,对给定的日志数据去重合并处理。位置,表示开启一个新的窗口,此时需要重置窗口和。,为了优化重复的请求记录,存储和后续分析需对。:对应的响应时间数组(毫秒)。重新置为当前右指针的元素值。

weixin_48157259的博客 111

WPF中MVVM常用的框架

WPF中MVVM常用的框架

hpx666的博客 2592

C# ScottPlot 5.0 WinForms 十字光标示例:轻松实现动态数值显示,你学会了吗?

ScottPlot是一个免费、开源的.NET交互式绘图库,能够轻松地实现大型数据集的交互式显示。它支持多种图表类型,如折线图、柱状图、饼图、散点图等。ScottPlot的性能非常出色,即使是处理千万级别的数据也游刃有余。

java专栏 2214

基于CNN-LSTM融合模型的环卫车动态称重算法研究:从频率感知到精准质量估计

摘要: 针对环卫车辆动态称重需求,本研究提出一种基于CNN-LSTM融合网络的频率分析方法。通过在实验室构建振动频率采集系统,获取不同载重下的结构响应数据,经MATLAB进行信号处理和特征提取后,建立频率-质量的非线性映射模型。实验表明,该算法在静态和模拟动态场景下平均绝对误差小于1.5%,显著优于传统方法,为环卫车智能化管理提供了高精度实时称重方案。 关键词: 动态称重;深度学习;频率分析;CNN-LSTM;MATLAB。

AI的内部世界的博客 374

MVVM Light Toolkit 项目推荐

MVVM Light Toolkit 项目推荐 1. 项目基础介绍和主要编程语言 MVVM Light Toolkit 是一个开源项目,旨在加速 MVVM(Model-View-ViewModel)应用程序的创建和开发。该项目主要支持 Xamarin Android、Xamarin iOS、Xamarin Forms、Windows 10 UWP、Windows Presentation Foun...

gitblog_00533的博客 814

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

WPF使用MVVM模式开发

本文用到的有: WPF(.net5) Microsoft.Toolkit.Mvvm 按钮不带参数/带参数点击事件绑定 文本框Text绑定,点击事件绑定 步骤如下: 创建wpf项目:WpfMVVM 创建Views、ViewModels两个文件夹 nuget添加Microsoft.Toolkit.Mvvm 在ViewModels文件夹添加类MainViewModel using Microsoft.Toolkit.Mvvm.ComponentModel; using Microsof

nightingale1985的博客 1240

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

CommunityToolkit.Mvvm(又称MVVM Toolkit) 与 MvvmLight 的核心区别

摘要:CommunityToolkit.Mvvm是微软官方推出的现代化MVVM框架,作为MvvmLight的继任者,具有平台无关、轻量模块化和高性能等特点。其核心优势包括革命性的源生成器技术,大幅减少样板代码;创新的命令系统;以及完整的MVVM组件套件,支持异步操作和消息传递。相比MvvmLight,它由微软持续维护更新,采用更现代的API设计,特别适合WPF等.NET应用开发。通过示例代码展示了其简洁的ViewModel实现方式,包括属性、命令和消息系统的使用,显著提升开发效率和代码可维护性。

ultramand的博客,重生之我在cad上二次开发猛猛敲C#代码~ 536

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

WPF框架(Prism 和 Community Toolkit)区别点

WPF框架(Prism 和 Community Toolkit)区别点

Blue 3543
上一篇: WPF + MvvmLight 完整技术指南与实战项目
下一篇: WPF 路径动画完全指南:自绘制控件实战
bugcome_com
博客等级 码龄3年 32粉丝 131原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bugcome_com

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值