项目概述

本文将详细介绍如何使用 C# 和 CommunityToolkit.Mvvm 框架开发一个功能完整的命令行 Todo 应用。我们的应用将支持以下核心功能:

  • 添加待办事项
  • 删除待办事项
  • 标记待办事项为完成/未完成
  • 按优先级排序
  • 持久化存储
  • 搜索和过滤待办事项

通过这个例子基本可能了解CommunityToolkit.Mvvm机制了。

项目准备

安装必要的 NuGet 包 

dotnet add package CommunityToolkit.Mvvm
  • 1.

项目结构 

AppTodo/
├── Models/
│   └── TodoItem.cs
├── ViewModels/
│   └── TodoViewModel.cs
├── Services/
│   └── TodoService.cs
└── Program.cs
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

模型定义 (Models/TodoItem.cs) 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;

namespace AppTodo.Models
{
    // 将优先级枚举移到类外部  
    publicenum Priority
    {
        Low,
        Medium,
        High,
        Urgent
    }

    // 待办事项模型,使用 ObservableObject 支持属性变更通知  
    public partial class TodoItem : ObservableObject
    {
        [ObservableProperty]
        private Guid _id;

        [ObservableProperty]
        privatestring _title;

        [ObservableProperty]
        privatestring _description;

        [ObservableProperty]
        privatebool _isCompleted;

        [ObservableProperty]
        private DateTime _createdAt;

        [ObservableProperty]
        private DateTime? _completedAt;

        [ObservableProperty]
        private Priority _priority;

        public TodoItem()
        {
            Id = Guid.NewGuid();
            CreatedAt = DateTime.Now;
            Priority = Priority.Medium; // 设置默认优先级  
        }

    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.

服务层 (Services/TodoService.cs) 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using AppTodo.Models;
using CommunityToolkit.Mvvm.Input;

namespace AppTodo.Services
{
    publicclass TodoService
    {
        privateconststring StorageFile = "todos.json";
        private List<TodoItem> _todos;

        public TodoService()
        {
            LoadTodos();
        }

        // 加载待办事项
        private void LoadTodos()
        {
            if (File.Exists(StorageFile))
            {
                var json = File.ReadAllText(StorageFile);
                _todos = JsonSerializer.Deserialize<List<TodoItem>>(json) ?? new List<TodoItem>();
            }
            else
            {
                _todos = new List<TodoItem>();
            }
        }

        // 保存待办事项
        public void SaveTodos()
        {
            var json = JsonSerializer.Serialize(_todos, new JsonSerializerOptions { WriteIndented = true });
            File.WriteAllText(StorageFile, json);
        }

        // 获取所有待办事项
        public List<TodoItem> GetAllTodos() => _todos;

        // 添加待办事项
        public void AddTodo(TodoItem todo)
        {
            _todos.Add(todo);
            SaveTodos();
        }

        // 删除待办事项
        public bool RemoveTodo(Guid id)
        {
            var todo = _todos.FirstOrDefault(t => t.Id == id);
            if (todo != null)
            {
                _todos.Remove(todo);
                SaveTodos();
                returntrue;
            }
            returnfalse;
        }

        // 更新待办事项状态
        public bool UpdateTodoStatus(Guid id, bool isCompleted)
        {
            var todo = _todos.FirstOrDefault(t => t.Id == id);
            if (todo != null)
            {
                todo.IsCompleted = isCompleted;
                todo.CompletedAt = isCompleted ? DateTime.Now : null;
                SaveTodos();
                returntrue;
            }
            returnfalse;
        }

        // 按优先级排序
        public List<TodoItem> GetTodosByPriority()
        {
            return _todos.OrderBy(t => t.Priority).ToList();
        }

        // 搜索待办事项
        public List<TodoItem> SearchTodos(string keyword)
        {
            return _todos.Where(t =>
                t.Title.Contains(keyword, StringComparison.OrdinalIgnoreCase) ||
                t.Description.Contains(keyword, StringComparison.OrdinalIgnoreCase)
            ).ToList();
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.

视图模型 (ViewModels/TodoViewModel.cs) 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AppTodo.Models;
using AppTodo.Services;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

namespace AppTodo.ViewModels
{
    public partial class TodoViewModel : ObservableObject
    {
        private readonly TodoService _todoService;

        [ObservableProperty]
        private List<TodoItem> _todos;

        public TodoViewModel()
        {
            _todoService = new TodoService();
            Todos = _todoService.GetAllTodos();
        }


        // 载入所有
        [RelayCommand]
        private void ReloadAllTodos()
        {
            Todos = _todoService.GetAllTodos();
        }

        [RelayCommand]
        private void AddTodo(string title)
        {
            var todo = new TodoItem
            {
                Title = title,
                Description = "",
                Priority = AppTodo.Models.Priority.Medium
            };
            _todoService.AddTodo(todo);
            Todos = _todoService.GetAllTodos();
        }

        [RelayCommand]
        private void RemoveTodo(Guid id)
        {
            _todoService.RemoveTodo(id);
            Todos = _todoService.GetAllTodos();
        }

        [RelayCommand]
        private void ToggleTodoStatus(Guid id)
        {
            var todo = Todos.Find(t => t.Id == id);
            if (todo != null)
            {
                _todoService.UpdateTodoStatus(id, !todo.IsCompleted);
                Todos = _todoService.GetAllTodos();
            }
        }

        [RelayCommand]
        private void SortByPriority()
        {
            Todos = _todoService.GetTodosByPriority();
        }

        [RelayCommand]
        private void SearchTodos(string keyword)
        {
            Todos = _todoService.SearchTodos(keyword);
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.

主程序 (Program.cs) 

using AppTodo.ViewModels;

namespace AppTodo
{
    internal class Program
    {
        static void Main(string[] args)
        {
            var viewModel = new TodoViewModel();

            while (true)
            {
                Console.Clear();
                Console.WriteLine("==== Todo 应用 ====");
                Console.WriteLine("1. 显示所有待办事项");
                Console.WriteLine("2. 添加待办事项");
                Console.WriteLine("3. 删除待办事项");
                Console.WriteLine("4. 切换待办事项状态");
                Console.WriteLine("5. 按优先级排序");
                Console.WriteLine("6. 搜索待办事项");
                Console.WriteLine("0. 退出");
                Console.Write("请选择操作: ");

                var choice = Console.ReadLine();
                switch (choice)
                {
                    case"1":
                        viewModel.ReloadAllTodosCommand.Execute(null);
                        DisplayTodos(viewModel);
                        break;
                    case"2":
                        AddTodo(viewModel);
                        break;
                    case"3":
                        RemoveTodo(viewModel);
                        break;
                    case"4":
                        ToggleTodoStatus(viewModel);
                        break;
                    case"5":
                        viewModel.SortByPriorityCommand.Execute(null);
                        DisplayTodos(viewModel);
                        break;
                    case"6":
                        SearchTodos(viewModel);
                        break;
                    case"0":
                        return;
                    default:
                        Console.WriteLine("无效的选择,请重试。");
                        break;
                }

                Console.WriteLine("\n按任意键继续...");
                Console.ReadKey();
            }
        }

        static void DisplayTodos(TodoViewModel viewModel)
        {
            Console.Clear();
            Console.WriteLine("==== 待办事项列表 ====");
            foreach (var todo in viewModel.Todos)
            {
                Console.WriteLine($"ID: {todo.Id}");
                Console.WriteLine($"标题: {todo.Title}");
                Console.WriteLine($"状态: {(todo.IsCompleted ? "已完成" : "未完成")}");
                Console.WriteLine($"优先级: {todo.Priority}");
                Console.WriteLine($"创建时间: {todo.CreatedAt}");
                Console.WriteLine("-------------------");
            }
        }

        static void AddTodo(TodoViewModel viewModel)
        {
            Console.Write("请输入待办事项标题: ");
            var title = Console.ReadLine();
            if (!string.IsNullOrWhiteSpace(title))
            {
                viewModel.AddTodoCommand.Execute(title);
                Console.WriteLine("待办事项添加成功!");
            }
        }

        static void RemoveTodo(TodoViewModel viewModel)
        {
            Console.Write("请输入要删除的待办事项ID: ");
            if (Guid.TryParse(Console.ReadLine(), out Guid id))
            {
                viewModel.RemoveTodoCommand.Execute(id);
                Console.WriteLine("待办事项删除成功!");
            }
            else
            {
                Console.WriteLine("无效的ID!");
            }
        }

        static void ToggleTodoStatus(TodoViewModel viewModel)
        {
            Console.Write("请输入要切换状态的待办事项ID: ");
            if (Guid.TryParse(Console.ReadLine(), out Guid id))
            {
                viewModel.ToggleTodoStatusCommand.Execute(id);
                Console.WriteLine("待办事项状态已切换!");
            }
            else
            {
                Console.WriteLine("无效的ID!");
            }
        }

        static void SearchTodos(TodoViewModel viewModel)
        {
            Console.Write("请输入搜索关键词: ");
            var keyword = Console.ReadLine();
            viewModel.SearchTodosCommand.Execute(keyword);
            DisplayTodos(viewModel);
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.

C#下使用CommunityToolkit.Mvvm快速构建Todo应用_优先级

C#下使用CommunityToolkit.Mvvm快速构建Todo应用_System_02

总结

这个 Todo 应用展示了如何使用 CommunityToolkit.Mvvm 快速构建一个功能丰富的命令行应用。通过使用 MVVM 模式,我们实现了:

  1. 清晰的代码结构
  2. 数据持久化
  3. 命令绑定
  4. 属性变更通知
  5. 灵活的待办事项管理

希望这个教程能帮助你快速理解如何使用 CommunityToolkit.Mvvm 构建 Todo 应用!