项目概述
本文将详细介绍如何使用 C# 和 CommunityToolkit.Mvvm 框架开发一个功能完整的命令行 Todo 应用。我们的应用将支持以下核心功能:
- 添加待办事项
- 删除待办事项
- 标记待办事项为完成/未完成
- 按优先级排序
- 持久化存储
- 搜索和过滤待办事项
通过这个例子基本可能了解CommunityToolkit.Mvvm机制了。
项目准备
安装必要的 NuGet 包
项目结构
模型定义 (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.
总结
这个 Todo 应用展示了如何使用 CommunityToolkit.Mvvm 快速构建一个功能丰富的命令行应用。通过使用 MVVM 模式,我们实现了:
- 清晰的代码结构
- 数据持久化
- 命令绑定
- 属性变更通知
- 灵活的待办事项管理
希望这个教程能帮助你快速理解如何使用 CommunityToolkit.Mvvm 构建 Todo 应用!