Найти в Дзене

Создание WPF-приложения для Revit. Библиотека Community.Toolkit.Mvvm

Оглавление

Всем привет! Сегодня расскажу, как с помощью применения новой технологии от Microsoft мы сможем упростить нашу ViewModel, избавиться от RelayCommand и в принципе сделаем наш код более читаемым.

В этой статье мы:

  • Научимся добавлять nuget-пакеты
  • Узнаем, как работают генераторы кода
  • Расстроимся, что плагины под Ревит не поддерживают .NET (а только .NET Framework 4.8)

Дисклеймер: некоторые действия, приведённые в этой статье, могут показаться вам странными. Не переживайте: в следующей статье я расскажу про создание шаблонов Visual Studio, и повторять их в будущем особо не придётся.

Библиотека Community.Toolkit.Mvvm

Это библиотека, разработанная для создания WPF-приложений. Почитать о ней можно на официальном сайте Microsoft, или скачав приложение-образец, где в принципе рассказано тоже самое. Что умеет делать эта библиотека:

1. Генерирует свойства из полей. Как мы писали раньше для создания одного свойства:

private string _prefix = string.Empty;
public string Prefix
{
get => _prefix;
set
{
_prefix = value;
OnPropertyChanged();
}
}

А это 10 строчек кода. А ViewModel может быть очень большая в сложных плагинах. Существуют рекомендации, что файл класса не должен превышать 500 строк. А если у нас 25 переменных во ViewModel — сразу 250 строк занято.

Что мы пишем с Community.Toolkit.Mvvm:

[ObservableProperty] private string _prefix = string.Empty;

А остальное за нас дописывает генератор кода. Да, и создает публичное свойство Prefix. Да и ещё несколько методов создаёт, которые мы можем переопределить при необходимости:

Сгенерированный код
Сгенерированный код

Код от генератора лежит в отдельном файле и никак не мешает нам.

2. Генерирует команды (в том числе с параметром и асинхронные команды)

Нет, всё, отдельный RelayCommand, два обязательных метода для неё во ViewModel и публичное свойство, как мы делали в предыдущей части, писать не нужно. Просто перед методом, являющимся реализацией команды, пишем:

[RelayCommand]

или

[RelayCommand(CanExecute = nameof(YourCanExecute)]

И в вашей ViewModel появится публичной свойство с типом RelayCommand, с названием, равным названию вашего метода + слово Command.

3. Нет необходимости реализовывать INotifyPropertyChanged

Так же эта библиотека позволяет обмениваться сообщениями между классами и выполнять инъекцию зависимостей.

Подключение nuget-пакета

1. В Visual Studio проходим Средства — Диспетчер пакетов nuget — Управление пакетами nuget:

-2

2. Устанавливаем пакет:

-3

3. В обозревателе решений выделяем появившийся файл packages.config, жмём на него правой кнопкой мыши и выбираем "перенести в PackageReference":

-4
-5

4. Устанавливаем таким же образом пакет Microsoft.CodeDom.Providers.DotNetCompilerPlatform — это нужно для генераторов кода Roslyn.

5. В Обозревателе решений жмём правой кнопкой на Проект — Выгрузить проект:

-6

6. Затем опять правой кнопкой мыши на выгруженный проект — Изменить файл проекта. Откроется файл csproj. Его надо отредактировать. Нам нужно перенести проект из обычного csproj в SDK-style csproj.

Перенос файла csproj в SDK-style

0. Делаем бэкап в соседнюю папку или коммит, если дружим с гитом. На всякий случай.

1. Установим .NET SDK по ссылке:

Download .NET (Linux, macOS, and Windows)

Далее изменим наш открытый csproj:

2. В самом начале удаляем первые 3 строки и пишем

<Project Sdk="Microsoft.NET.Sdk">

Сразу за ним идут PropertyGroup:

-7

Особое внимание обратите на версию языка, она должна быть последней.

<LangVersion>latest</LangVersion>

3. Удаляем ItemGroup, в котором есть слово Compile. Если где-то осталась ссылка на наш xaml-файл, эту группу тоже удаляем.

-8

4. Сохраняем csproj и перезагружаем проект с зависимостями (также через правую кнопку мыши)

5. В проекте находим файл AssemblyInfo (в папке Properties) и исключаем его из проекта.

Готово! Теперь можно использовать генераторы кода и все возможности Community.Toolkit.

Упрощение ViewModel

Осталась самая простая часть. Вместо реализации INotifyPropertyChanged выполним наследование от ObservableObject. Далее делаем следующее:

  • Объявляем класс частичным: public partial class ViewModel : ObservableObject
  • Перед приватными полями ставим атрибут [ObservableProperty].
  • Перед методом Execute ставим атрибут [RelayCommand].
  • У методов Execute и CanExecute удаляем (object parameter) из сигнатуры.
  • В конце класса удаляем PropertyChanged и OnPropertyChanged (они уже есть в базовом классе).
  • Удаляем публичные свойства, для которых есть приватные поля, и публичные свойства для команд.

Чтобы метод CanExecute работал правильно, надо добавить ещё немного атрибутов (распишу для своего примера с GitHub):

  • [RelayCommand(CanExecute = nameof(CanNumerate))] вместо просто [RelayCommand].
  • [NotifyCanExecuteChangedFor(nameof(NumerateCommand))] для приватных полей, рядом с [ObservableProperty] — у всех полей, значения которых обновляются в CanExecute.

Теперь выполним сборку и проверим результат в режиме отладки с помощью addin-менеджера. Доступность кнопок должна появляться лишь тогда, когда мы введём число в "Стартовый номер" и выберем параметр, и по нажатию кнопки "Запуск" команда должна отрабатывать штатно.

Результат

Мы узнали, как работают генераторы кода и научились подключать этот мощный инструмент к своим проектам.

Код ViewModel в примере сократился со 147 до 110 строк (и ещё можно удалить рукописный класс RelayCommand).

Итоговый код в моём репозитории на Github.

Подписывайтесь на мой телеграм-канал, не забывайте ставить лайки и звёздочки в репозитории. До новых встреч!

-9