Перейти к основному содержимому

Практикум WPF — итоговый проект TaskDesk

Разработчику Архитектору

Практикум, шаг 6 из 6. Сводим WPF и XAML, MVVM, API, Prism и тесты в один репозиторий TaskDesk.


Целевая архитектура

ПроектТипНазначение
TaskDesk.CoreClass LibraryTaskItem, DTO, ITaskRepository (интерфейс клиента)
TaskDesk.ApiASP.NET Core Web APIREST, ITaskStore, Swagger
TaskDesk.ClientWPF + PrismShell, регионы, ApiTaskRepository
TaskDesk.Api.TestsxUnitWebApplicationFactory
TaskDesk.Client.TestsxUnit + MoqViewModel

Финальная структура репозитория

TaskDesk/
├── TaskDesk.sln
├── README.md
├── src/
│ ├── TaskDesk.Core/
│ │ ├── Models/TaskItem.cs
│ │ └── Contracts/TaskDto.cs
│ ├── TaskDesk.Api/
│ │ ├── Program.cs
│ │ ├── Controllers/TasksController.cs
│ │ └── Services/InMemoryTaskStore.cs
│ └── TaskDesk.Client/
│ ├── App.xaml.cs
│ ├── appsettings.json
│ ├── Views/ShellWindow.xaml, TaskListView.xaml
│ ├── ViewModels/
│ └── Services/ApiTaskRepository.cs
└── tests/
├── TaskDesk.Api.Tests/
└── TaskDesk.Client.Tests/

Сборка solution одной командой

dotnet new sln -n TaskDesk
dotnet sln add src/TaskDesk.Core/TaskDesk.Core.csproj
dotnet sln add src/TaskDesk.Api/TaskDesk.Api.csproj
dotnet sln add src/TaskDesk.Client/TaskDesk.Client.csproj
dotnet sln add tests/TaskDesk.Api.Tests/TaskDesk.Api.Tests.csproj
dotnet sln add tests/TaskDesk.Client.Tests/TaskDesk.Client.Tests.csproj

Проверка:

dotnet build
dotnet test

Сценарий демонстрации (5 минут)

  1. Запустить API: dotnet run --project src/TaskDesk.Api --urls http://localhost:5100.
  2. Открыть Swagger — убедиться, что GET /api/v1/tasks отвечает [].
  3. Запустить клиент: dotnet run --project src/TaskDesk.Client.
  4. Добавить задачу «Подготовить отчёт» — она появляется в списке.
  5. Обновить Swagger — та же задача в JSON.
  6. Остановить API — в клиенте видно сообщение об ошибке сети.
  7. Запустить API снова — кнопка «Обновить» / повторная навигация подтягивает данные.

Критерии готовности «итогового проекта»

КритерийВыполнено, если
Клиент-серверКлиент не хранит список только в памяти после перезапуска API
MVVMНет бизнес-логики в *.xaml.cs Views
PrismРегистрация VM и View через RegisterForNavigation, DI для репозитория
RESTCRUD по /api/v1/tasks, JSON через System.Text.Json
Тестыdotnet test ≥ 4 осмысленных теста (API + VM)
КонфигурацияURL API в appsettings.json, не захардкожен в десяти местах

Типичные сбои при интеграции

СимптомПричинаРешение
Connection refusedAPI не запущенСтарт Api на 5100, проверка BaseUrl
Пустой список после добавленияPOST OK, GET другой storeSingleton ITaskStore, один экземпляр
Десериализация падаетcamelCase vs PascalCaseJsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase на сервере и клиенте
Prism не находит ViewИмя навигацииnameof(TaskListView) совпадает с классом
Дубликаты при двойном кликеНет IsBusyБлокировка команды на время await

Расширения после базового TaskDesk

Выберите 1–2 пункта для портфолио:

  1. SQLite + EF Core на API вместо in-memory (453).
  2. JWT — login в клиенте, защищённые эндпоинты (4515).
  3. Второй регион Prism — master/detail (список + карточка задачи).
  4. Фильтр по статусу — ComboBox в View, query ?status= на API.
  5. Публикацияdotnet publish клиента self-contained + MSI (117.md).
  6. Интеграция с OrderDesk — тот же REST-подход, что в практикуме REST.

Связь с материалами энциклопедии

Вы изучилиКуда углубиться
WPF UI1192.md, 119.md
Потоки, память112.md
Clean Architecture API2143
MAUI (кроссплатформа)4513
Electron (веб в десктопе)118.md

Чек-лист самопроверки по практикуму

  • Могу объяснить роли Model, View, ViewModel на примере TaskDesk.
  • Могу добавить новый REST-эндпоинт и вызвать его из ApiTaskRepository.
  • Понимаю, зачем Prism регистрирует View в регионе.
  • Написал unit-тест ViewModel с Moq без запуска окна.
  • Прогнал CRUD в Postman или Swagger.

Поздравляем — у вас полноценное клиент-серверное десктоп-приложение на стеке WPF + Prism + ASP.NET Core.


Назад к оглавлению

Практикум WPF и клиент-сервер — о разделе


См. также

Другие статьи этого же раздела в боковом меню (как на странице "О разделе").