Пользовательские шаблоны и расширения для Visual Studio под проект (Часть 3: проекты типа «шаблон»)

Прочитав предыдущие 2 статьи ([1], [2]), вы могли задуматься, а как поставить все это дело на поток? Как распространять свежие версии шаблонов, отделив доставку от разработки? Как вообще эти шаблоны разрабатывать? До сих пор мы только пользовались магией Visual Studio для их автоматического создания на основе готовых файлов. Оказывается, в Visual Studio есть готовые шаблоны проектов для шаблонов проектов и элементов (классов). Их мы и рассмотрим в этой статье.

Знакомьтесь, C# Project Template и C# Item Template.

Давайте попробуем создать Item Template.

Проект типа шаблона элемента

Видим здесь уже знакомые нам файл cs, vstemplate и иконку.

Откроем наш класс.

using System;
using System.Collections.Generic;
$
if$ ($targetframeworkversion$ >= 3.5)using System.Linq;
$endif$using System.Text;

namespace $rootnamespace$
{
	class $safeitemrootname$
	{
	}
}

Обнаруживаем тут условную конструкцию. Внезапно! Да тут не шаблон, а целый язык программирования! Есть где развернуться! Условия, переменные, циклы…

Спойлер

На самом деле, нам доступны только условия. И то тут все очень примитивно. Сначала парсер шаблонов заменит все переменные на их значения, затем посмотрит на условие в блоке if, увидит там 3.5 >= 3.5, сравнит и вставит текст между условием и окончанием блока в итоговый файл. Никаких тебе логических операций OR/AND (в явном виде), никаких преобразований… Моему унынию не было предела. Да и ладно, в конце концов, мы же просто делаем шаблон класса! Вообще то, я хотел запилить проверку, что если пространство имен нашего будущего класса начинается с того самого пространства, в котором находится наш базовый контроллер из прошлых статей, то нам не нужен using на это пространство имен, иначе нужен. Однако, столкнувшись с этой неудачей, я решил не тратить много времени на это и добавить using в любом случае.

Посмотрим, что у нас в vstemplate файле.

<?xml version="1.0" encoding="utf-8"?>
<VSTemplate Version="3.0.0" Type="Item" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" xmlns:sdk="http://schemas.microsoft.com/developer/vstemplate-sdkextension/2010">
  <TemplateData>
    <Name>ItemTemplate1</Name>
    <Description>ItemTemplate1</Description>
    <Icon>ItemTemplate1.ico</Icon>
    <TemplateID>6ff668aa-87da-4b97-be77-1d274212e5ec</TemplateID>
    <ProjectType>CSharp</ProjectType>
    <RequiredFrameworkVersion>2.0</RequiredFrameworkVersion>
    <NumberOfParentCategoriesToRollUp>1</NumberOfParentCategoriesToRollUp>
    <DefaultName>Class.cs</DefaultName>
  </TemplateData>
  <TemplateContent>
    <References>
      <Reference>
        <Assembly>System</Assembly>
      </Reference>
    </References>
    <ProjectItem ReplaceParameters="true">Class.cs</ProjectItem>
  </TemplateContent>
</VSTemplate>

В прошлый раз мы не видели тегов TemplateID, RequiredFrameworkVersion, NumberOfParentCategoriesToRollUp.

TemplateID — задает идентификатор шаблона. Если не указан, в качестве идентификатора будет выступать имя шаблона.

NumberOfParentCategoriesToRollUp — указывает, насколько родительских категорий вверх будет виден ваш шаблон. Например, в прошлой статье мы разместили наш шаблон элемента в папке MyProject. по умолчанию, он будет виден только там. Но если указать уровень видимости в родительских категориях, то он попадет и на это количество уровней выше и будет виден, в т. ч., в категории C#.

RequiredFrameworkVersion — указывает минимальную версию фрэймворка. Если честно, я не нашел способа указать минимальную версию для .Net Core или .Net. Это просто не работает. Указывая net5.0, я все еще вижу этот шаблон при добавлении элемента в проекты .Net Framework. Легального способа ограничить это я не нашел. но не очень то и старался, так как у нас все еще есть возможность указывать папки и таким образом мы можем отделить мух от слонов. Для простых и средних задач этого должно хватить.

    <References>
      <Reference>
        <Assembly>System</Assembly>
      </Reference>
    </References>

Вот это для Net и Net Core не понадобится. Это говорит о том, какие зависимости должны быть добавлены в проект при создании элемента.

Артефактом сборки проекта типа шаблон будет zip архив с шаблоном.

Теперь посмотрим, что мы можем в проекте типа шаблон проекта.

Проект типа шаблон проекта

Создадим его.

Как видим, изначально шаблон создается под проект для .Net Framework. Избавимся от лишних сущностей (AssemblyInfo.cs). От самого файла и его описания в vstemplate. Так же сдернем содержимое файла csproj с пустого .Net проекта или создадим его сами.

Компиляция проекта так же создаст zip файл.

  <TemplateContent>
    <Project TargetFileName="VsExtensionsPolygon.ProjectForTemplate.csproj" File="VsExtensionsPolygon.ProjectForTemplate.csproj" ReplaceParameters="true">
      <Folder Name="Controllers" TargetFolderName="Controllers">
        <ProjectItem ReplaceParameters="true" TargetFileName="MyController.cs">MyController.cs</ProjectItem>
      </Folder>
      <ProjectItem ReplaceParameters="true" TargetFileName="Startup.cs">Startup.cs</ProjectItem>
    </Project>
  </TemplateContent>

Обратите внимание на блок Folder. Этот блок говорит о том, что при создании проекта будет создана папка, а файлы, находящиеся внутри этого блока попадут в эту папку.

Еще важный момент. Если вы в шаблон проекта захотите добавить еще классов и использовать в них переменные шаблона, компилятор будет очень сильно ругаться. Поэтому файл надо исключить из компиляции.

Так же вместо шаблона проекта можно сделать шаблон группы проектов. Это когда у вас вместо одного проекта создается несколько. Для этого нужно внести в vstemplate некоторые изменения.

В VSTemplate.Type установить значение ProjectGroup.

Вместо TemplateContent.Project теперь используется TemplateContent.ProjectCollection, а внутри него список из ProjectTemplateLink, которые при желании можно расположить в SolutionFolder. Как вы, наверное, поняли, под каждый проект нужен отдельный файл vstemplate с его настройками и содержимым. Подробнее тут.

Меня спрашивали в комментариях, как создать окно с настройками проекта, которое будет отображаться при создании проекта из нашего шаблона. Я хотел рассказать об этом здесь, но оставлю это до статьи про расширения, там это будет больше к месту.

А зачем нам все это?

Мы ведь можем создавать шаблоны на основе готовых элементов. Зачем нам такие заморочки?

Так как мы имеем проекты, мы можем их собрать. Причем собрать через командную строку. А значит мы можем поставить сборку, доставку и все прилагающееся на поток. Есть, конечно, утилита projectgen.exe, но группу проектов вы с ее помощью не создадите, а группа проектов — это целая архитектура.

Заключение

На этом я закончу. Если у вас будут вопросы по деталям, можете найти их в официальной документации от Microsoft или задать здесь в комментариях.

В следующей статье и расскажу про расширения (так же кратко) и о том, как с их помощью связать все воедино.

Источник 📢