Делаю шаблон проекта VS и хочу, чтобы создаваемый проект содержал настройки и аргументы
запуска внешней программы. Файл с настройками должен быть просто скопирован в каталог
проекта.
Сами настройки VS хранит в файле $projectname$.csproj.user на который нет никаких
ссылок из файла проекта (.csproj).
Тут возникает проблема: VS не копирует файлы, которые не задействованы в файле проекта.
В $projectname$.csproj должно быть прописано что-то вроде такого , чтобы файл $projectname$.csproj.user был, все-таки, скопирован. Но тогда он появляется
в Solution Explorer и настройки из него считываются только после переоткрытия решения.
Project.vstemplate:
AssemblyInfo.csProjectTemplate.csproj.user
Как можно решить эту проблему?
Ответы
Ответ 1
Можно скрыть элемент в Solution Explorer, если указать значение метасвойства Visible:
За старый формат проекта не отвечаю. Скорее всего, там надо указывать отдельным элементом:
false
Это поможет избавиться от лишнего элемента в проекте, но вряд ли поможет с чтением
настроек только после переоткрытия решения. Это попахивает ограничением IDE: разработчики,
скорее всего, просто не рассчитывали, что пользовательские настройки будут создаваться
не пользователем.
Кстати, если перейдёте на новый формат проекта, то там профили запуска лежат отдельным
красивым файлом launchSettings.json, и профилей может быть много с разными настройками.
Рекомендую.
Товарищи, проблема следующая: привык в своих проектах сортировать usingи (и не только)
по длине строки. То есть код рода:
using System.Collections.Generic;
using System;
using System.Linq;
using System.IO;
Я обязательно переделаю в:
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
И только тогда успокоюсь)
Так вот. Я немного устал делать это вручную, так что хочу спросить: есть ли какое-либо
легкое расширение (подозреваю, что ReSharper так умеет, но его я не рассматриваю) для
Visual Studio 2017, способное выполнять сортировку выделенных строк по их длине?
Ответы
Ответ 1
Собственно, никто мне так и не ответил, сам же я не нашел подходящего расширения.
Но кто ж мне мешал написать свое? Сие я и сделал. Расширение теперь висит в Marketplace,
а именно - здесь. Оформлять сильно все это я, понятное дело, не стал, ибо делал сие
только под себя, но мало ли кому-то когда-нибудь тоже пригодится такая мелочь, а ставить
внушительные расширения хотят далеко не все.
Ну и давайте заодно уж рассмотрим пример создания такого простейшего расширения на
поставленной задаче.
0) Для начала создадим VSIX Project (расширения для Visual Studio можно писать на
Visual C++, Visual Basic, C#. Я это дело писал на C#). Если шаблон такого проекта отсутствует
в списке доступных, установите соответствующий пункт с помощью Visual Studio Installer.
1) Добавим в проект элемент Custom Command и назовем его "CommandSort".
В появившийся класс "CommandSort.cs" добавим следующий код:
using System;
using System.Linq;
using Microsoft.VisualStudio.Shell;
using System.ComponentModel.Design;
namespace LineSorter
{
internal sealed class CommandSort
{
#region Var
private AsyncPackage Package { get; }
public int CommandId { get; } = 0x0100;
public static CommandSort Instance { get; private set; }
public static Guid CommandSet { get; } = new Guid("e9f69e2b-6313-4c2b-9765-1ddd6439d519");
#endregion
#region Init
private CommandSort(AsyncPackage Package, OleMenuCommandService CommandService)
{
this.Package = Package ?? throw new ArgumentNullException(nameof(Package));
CommandService = CommandService ?? throw new ArgumentNullException(nameof(CommandService));
CommandID menuCommandID = new CommandID(CommandSet, CommandId);
MenuCommand menuItem = new MenuCommand(Execute, menuCommandID);
CommandService.AddCommand(menuItem);
}
public static async System.Threading.Tasks.Task InitializeAsync(AsyncPackage
Package)
{
ThreadHelper.ThrowIfNotOnUIThread();
Instance = new CommandSort(Package, await Package.GetServiceAsync((typeof(IMenuCommandService)))
as OleMenuCommandService);
}
#endregion
#region Functions
private void Execute(object sender, EventArgs e)
{
ThreadHelper.ThrowIfNotOnUIThread();
TextSelection.GetSelection(Package).OrderBy(x => x.Length).ThenBy(x =>
x).ReplaceSelection();
}
#endregion
}
}
Инициализатор нашей команды, ее ID и GUID в рамках проекта будут установлены VS автоматически.
По сути от нас требуется изменить лишь метод Execute.
2) Для более удобной работы с выделенным текстом создадим класс "TextSelection" и
в "TextSelection.cs" внесем таковой код:
using EnvDTE;
using System;
using System.Linq;
using System.Windows;
using System.Collections.Generic;
using Microsoft.VisualStudio.TextManager.Interop;
namespace LineSorter
{
public static class TextSelection
{
#region Var
public static IServiceProvider ServiceProvider { get; set; }
#endregion
#region Functions
///
/// Получим выделенный текст и избавим его от пробелов и табуляции в начале/конце
строк
///
public static IEnumerable GetSelection(IServiceProvider ServiceProvider)
{
TextSelection.ServiceProvider = ServiceProvider;
IVsTextManager2 textManager = ServiceProvider.GetService(typeof(SVsTextManager))
as IVsTextManager2;
int result = textManager.GetActiveView2(1, null, (uint)_VIEWFRAMETYPE.vftCodeWindow,
out IVsTextView view);
view.GetSelectedText(out string selectedText);
return selectedText.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).Select(x
=> x.Trim(new char[] { ' ', '\t' }));
}
public static void ReplaceSelection(this IEnumerable Selections)
{
ReplaceSelection(Selections, ServiceProvider);
}
///
/// Заменим выделенный текст на указанную коллекцию строк
///
public static void ReplaceSelection(this IEnumerable Selections,
IServiceProvider ServiceProvider)
{
DTE dte = ServiceProvider?.GetService(typeof(DTE)) as DTE;
if (dte is null) return;
IDataObject obj = Clipboard.GetDataObject();
Clipboard.SetText(string.Join("\r\n", Selections));
dte.ExecuteCommand("Edit.Paste");
Clipboard.SetDataObject(obj);
}
#endregion
}
}
Логика кода, думаю, вполне ясна. Однако заострю внимание на методе замены выделенного
текста на новую коллекцию (ReplaceSelection). Изначально код выглядел так:
DTE dte = ServiceProvider?.GetService(typeof(DTE)) as DTE;
if (dte is null) return;
EnvDTE.TextSelection selection = dte.ActiveDocument?.Selection as EnvDTE.TextSelection;
if (selection is null) return;
selection.Text = string.Join("\r\n", Selections);
И все кажется вполне логичным: просто заменяем выделенный текст на новый. Во многих
мануалах я как раз такой подход и видел. Однако предостерегаю от такого подхода к работе
с выделенным текстом, так как работает это жутко медленно. Свойство EnvDTE.TextSelection.Text
при установке зачем-то парсит весь вставляемый текст, разбивая его на смысловые единицы.
Поясню: к примеру, мы имеем 2 таких using'a, которые мы хотим отсортировать:
using Microsoft.VisualStudio.Shell.Interop;
using Task = System.Threading.Tasks.Task;
Для нашего алгоритма важно лишь то, что это 2 строки. Однако указанное свойство разбивает
все это дело на смысловой набор:
using, Microsoft, VisualStudio, Shell, Interop, using, Task, System, Threading, Tasks, Task
И мы имеем уже не 2 строки, а 11 отдельно обрабатываемых элементов. С ростом числа
сортируемых строк число обрабатываемых смысловых единиц катастрофически растет, так
что простенькое действие при таком подходе начинает занимать внушительный промежуток
времени. Именно поэтому я воспользовался маленькой хитростью:
Я сохраняю текущее состояние буфера обмена
После этого загоняю в него объединенную коллекцию наших строк
А теперь уже запускаю функцию вставки из самой VS (как бы Ctrl +
V). Так что текст моментально вставляется на место выделенного,
после чего форматируется самой средой
После сего действа возвращаю буферу обмена прошлое состояние
Честно - не знаю, насколько хорошо это решение, однако оно уж точно быстрее стандартного
пути. Если будут идеи более правильной реализации - буду рад выслушать!
3) Что ж. Разобрались, собственно, с кодом. Осталось навести порядок в конфиге (CommandSortPackage.vsct):
Таким образом мы добавляем кнопку с указанным текстом и ссылающуюся на наше действие
в контекстное меню VS, а также связываем с ним горячие клавиши:
(Ctrl + E) + (Ctrl + L)
4) Вот и все, наше простенькое расширение готово. Вы можете отдебажить его с помощью
экспериментальной версии VS, или же просто собрать его, после чего среди уймы пакетов
Вы найдете заветный файлик с расширением *.vsix, при помощи которого Вы можете либо
сразу установить расширение в свою студию, либо же даже опубликовать его)
А на сием - все) Спасибо за внимание)
Как добавить дополнительную кнопку в стандартное контекстное меню, которое появляется
при щелчке правой кнопкой мыши рядом с кодом?
Например такая функциональность:
правый щелчок вызывает стандартное меню
в меню помимо стандартных есть новая кнопка
щелчок по ней вызывает действие (например появляется сообщение)
Ответы
Ответ 1
Ответ частично найден.
Надо просто создать стандартный проект - Visual Stutio Package. Выбрать Menu Command
в качестве интерфейса в диалоговом окне при создании (первый вариант).
Потом открыть файл .vsct и поменять родителя у группы:
Именно
Привяжет группу (с кнопкой) в меню, появляющееся при нажатии правой кнопкой в коде.
тут важен id этого меню IDM_VS_CTXT_CODEWIN и стандартный guid guidSHLMainMenu.
Можно посмотреть другие id и guid (лучше в VS - там с описанием)
https://msdn.microsoft.com/ru-ru/library/vstudio/microsoft.visualstudio.shell.vsmenus_members(v=vs.100).aspx#mainBody
В обработчике можно написать что угодно и оно заработает
private void MenuItemCallback(object sender, EventArgs e)
{
MessageBox.Show("Уже что-то заработало!!!");
}
Привет! Я пытаюсь затолкать свой шаблон проекта в (Visual C#/Web)(Аналогично тому
как там лежит DevExpress Template Gallery на моём скриншоте). Пытаюсь сделать это следующими
строками кода в файле MyTemplate.vstemplate:
My Template GalleryStart Template GallerymyProj.icoCSharpWebWeb4.0004d72892d-abc3-428d-8fa7-8421a83d6e0atrueXafariApplicationtrue
Ответы
Ответ 1
Ручной способ
Создаете проект. Заполняете нужными начальными файлами для шаблона. Сохраняете.
В меню Файл выбираете Экспорт шаблона.
В Wizard'е выбираете: Project Template.
Заполняете поля.
Архив закинется в папку <Рабочая папка студии>\My Exported Templates и в папку <Рабочая
папка студии>\Templates\ProjectTemplates (если не сняли галку в wizard'е):
Этот архив нужно поместить в папку <Рабочая папка студии>\Templates\ProjectTemplates\Visual
C#\Web (ее нет, поэтому необходимо создать):
Заходите в студию. И Profit.
С помощью VSIX
Дожен быть готов архив. Смотри 1-5 пункты ручного способа.
Создаете проект VSIX.
В проекте создаете нужную иерархию папок. В данном случае получается следующая иерархия:
Закидывайте архив в папку ProjectTemplates\CSharp\Web.
В манифесте указываете нужные данные. Затем заходите в пункт Assets и добавляете:
Сохраняетесь. Билдитесь. Запускаете VSIX. Открываете студию.
ProjectTemplate + VSIX
Создаете проект типа ProjectTemplate заполняете его.
Выбираете в студии файл с расширением *.vstemplate (по умолчанию его имя совпадает
с именем проекта). И в его свойствах в строке Category прописываете путь иерархии,
который будет начинаться от CSharp. Т.е. если указываете SubA/SubB/SubC, то шаблон
будет лежать в CSharp/SubA/SubB/SubC. В нашем случае указываем только Web:
Создаем проект VSIX.
В манифесте указываете нужные данные:
Билдитесь.
P.S. Немного про элемент TemplateData.
Вы указали ProjectSubType значение Web, но этот элемент может иметь только всего
4 значения:
SmartDevice-NETCFv1 - указывает поддержку .NET Compact Framework версии 1.0
SmartDevice-NETCFv1 - указывает поддержку .NET Compact Framework версии 2.0
CSharp - Задается только тогда, когда элемент ProjectType имеет значение Web (т.е.
это проект типа веб-сайт). Указывает на языка программирования.
VisualBasic - тоже самое, что и предыдущий пункт, только язык программирования VisualBasic.
Здравствуйте друзья! После создания проекта Visual Studio (Visual C#\Extensibility\C# Project Template), удаления тега вместе с его содержимым в файле MyTemplate.vstemplate мы получаем вот такой проект:
После сборки этого проекта в папке **bin** образуется архив с двумя файлами:
Как настроить проект таким образом, что-бы при сборке в этот архив ложилась ещё произвольная папка с произвольными файлами из проекта?
При этом нельзя что-бы шаблон генерировал все эти файлы при запуске его ярлыка из VisualStudio(т.е. нельзя добавлять файлы в файл с расширением .vstemplate как я понимаю) (А делать этого нельзя т.к. этот шаблон используется для вызова WizardTemplate который уже сам генерирует шаблон по пути который передан от самого Template. А собственно файлы которые находятся в шаблоне по условию нужно использовать в этом Wizard'e. Такие дела) Заранее спасибо за помощь!
Ответ
Смог решить данную проблему с помощью Post Build Action.
Файлы прикрепил к проекту с действием при компилляции "None", а в PostBuildAction написал скрипт доупаковку архива zip с помощью утилиты 7z.
Всем привет! Можно ли как-нибудь самостоятельно написать свой конструктор пользовательских компонентов? Я заметил что в некоторых фреймворках для Visual Studio существуют собственные конструкторы пользовательских компонентов (DevExpress к примеру). Где можно прочесть о создании собственных конструкторов? Заранее спасибо!
Ответ
Нашел книгу в которой есть сведенья о дизайнере компонентов, сервисах и о разработке компонентов в целом, которые могут пригодиться в разработке. 1) C#. Разработка компонентов в MS Visual Studio 2005/2008 (Павел Агуров) Так-же полезную информацию удалось найти в архиве MSDN: 2) Writing Custom Designers for .NET Components 3) Разработка собственного дизайнера форм для .NET-приложений Если кто-то находил или найдёт ещё информацию по дизайнерам, то буду очень благодарен если вы дополните ответ!
Товарищи, проблема следующая: привык в своих проектах сортировать usingи (и не только) по длине строки. То есть код рода: using System.Collections.Generic;
using System;
using System.Linq;
using System.IO;
Я обязательно переделаю в: using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
И только тогда успокоюсь)
Так вот. Я немного устал делать это вручную, так что хочу спросить: есть ли какое-либо легкое расширение (подозреваю, что ReSharper так умеет, но его я не рассматриваю) для Visual Studio 2017, способное выполнять сортировку выделенных строк по их длине?
Ответ
Собственно, никто мне так и не ответил, сам же я не нашел подходящего расширения. Но кто ж мне мешал написать свое? Сие я и сделал. Расширение теперь висит в Marketplace, а именно - здесь Оформлять сильно все это я, понятное дело, не стал, ибо делал сие только под себя, но мало ли кому-то когда-нибудь тоже пригодится такая мелочь, а ставить внушительные расширения хотят далеко не все.
Ну и давайте заодно уж рассмотрим пример создания такого простейшего расширения на поставленной задаче.
0) Для начала создадим VSIX Project (расширения для Visual Studio можно писать на Visual C++, Visual Basic, C#. Я это дело писал на C#). Если шаблон такого проекта отсутствует в списке доступных, установите соответствующий пункт с помощью Visual Studio Installer. 1) Добавим в проект элемент Custom Command и назовем его "CommandSort".
В появившийся класс "CommandSort.cs" добавим следующий код: using System;
using System.Linq;
using Microsoft.VisualStudio.Shell;
using System.ComponentModel.Design; namespace LineSorter
{
internal sealed class CommandSort
{
#region Var
private AsyncPackage Package { get; }
public int CommandId { get; } = 0x0100;
public static CommandSort Instance { get; private set; }
public static Guid CommandSet { get; } = new Guid("e9f69e2b-6313-4c2b-9765-1ddd6439d519");
#endregion #region Init
private CommandSort(AsyncPackage Package, OleMenuCommandService CommandService)
{
this.Package = Package ?? throw new ArgumentNullException(nameof(Package));
CommandService = CommandService ?? throw new ArgumentNullException(nameof(CommandService));
CommandID menuCommandID = new CommandID(CommandSet, CommandId);
MenuCommand menuItem = new MenuCommand(Execute, menuCommandID);
CommandService.AddCommand(menuItem);
}
public static async System.Threading.Tasks.Task InitializeAsync(AsyncPackage Package)
{
ThreadHelper.ThrowIfNotOnUIThread();
Instance = new CommandSort(Package, await Package.GetServiceAsync((typeof(IMenuCommandService))) as OleMenuCommandService);
}
#endregion #region Functions
private void Execute(object sender, EventArgs e)
{
ThreadHelper.ThrowIfNotOnUIThread();
TextSelection.GetSelection(Package).OrderBy(x => x.Length).ThenBy(x => x).ReplaceSelection();
}
#endregion
}
}
Инициализатор нашей команды, ее ID и GUID в рамках проекта будут установлены VS автоматически. По сути от нас требуется изменить лишь метод Execute
2) Для более удобной работы с выделенным текстом создадим класс "TextSelection" и в "TextSelection.cs" внесем таковой код: using EnvDTE;
using System;
using System.Linq;
using System.Windows;
using System.Collections.Generic;
using Microsoft.VisualStudio.TextManager.Interop; namespace LineSorter
{
public static class TextSelection
{
#region Var
public static IServiceProvider ServiceProvider { get; set; }
#endregion #region Functions
///
/// Получим выделенный текст и избавим его от пробелов и табуляции в начале/конце строк
///
public static IEnumerable GetSelection(IServiceProvider ServiceProvider)
{
TextSelection.ServiceProvider = ServiceProvider;
IVsTextManager2 textManager = ServiceProvider.GetService(typeof(SVsTextManager)) as IVsTextManager2;
int result = textManager.GetActiveView2(1, null, (uint)_VIEWFRAMETYPE.vftCodeWindow, out IVsTextView view);
view.GetSelectedText(out string selectedText);
return selectedText.Split(new char[] { ' ', ' ' }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim(new char[] { ' ', '\t' }));
} public static void ReplaceSelection(this IEnumerable Selections)
{
ReplaceSelection(Selections, ServiceProvider);
}
///
/// Заменим выделенный текст на указанную коллекцию строк
///
public static void ReplaceSelection(this IEnumerable Selections, IServiceProvider ServiceProvider)
{
DTE dte = ServiceProvider?.GetService(typeof(DTE)) as DTE;
if (dte is null) return;
IDataObject obj = Clipboard.GetDataObject();
Clipboard.SetText(string.Join(" ", Selections));
dte.ExecuteCommand("Edit.Paste");
Clipboard.SetDataObject(obj);
}
#endregion
}
}
Логика кода, думаю, вполне ясна. Однако заострю внимание на методе замены выделенного текста на новую коллекцию (ReplaceSelection). Изначально код выглядел так: DTE dte = ServiceProvider?.GetService(typeof(DTE)) as DTE;
if (dte is null) return;
EnvDTE.TextSelection selection = dte.ActiveDocument?.Selection as EnvDTE.TextSelection;
if (selection is null) return;
selection.Text = string.Join(" ", Selections);
И все кажется вполне логичным: просто заменяем выделенный текст на новый. Во многих мануалах я как раз такой подход и видел. Однако предостерегаю от такого подхода к работе с выделенным текстом, так как работает это жутко медленно. Свойство EnvDTE.TextSelection.Text при установке зачем-то парсит весь вставляемый текст, разбивая его на смысловые единицы. Поясню: к примеру, мы имеем 2 таких using'a, которые мы хотим отсортировать: using Microsoft.VisualStudio.Shell.Interop;
using Task = System.Threading.Tasks.Task;
Для нашего алгоритма важно лишь то, что это 2 строки. Однако указанное свойство разбивает все это дело на смысловой набор: using, Microsoft, VisualStudio, Shell, Interop, using, Task, System, Threading, Tasks, Task
И мы имеем уже не 2 строки, а 11 отдельно обрабатываемых элементов. С ростом числа сортируемых строк число обрабатываемых смысловых единиц катастрофически растет, так что простенькое действие при таком подходе начинает занимать внушительный промежуток времени. Именно поэтому я воспользовался маленькой хитростью:
Я сохраняю текущее состояние буфера обмена
После этого загоняю в него объединенную коллекцию наших строк
А теперь уже запускаю функцию вставки из самой VS (как бы Ctrl +
V). Так что текст моментально вставляется на место выделенного,
после чего форматируется самой средой
После сего действа возвращаю буферу обмена прошлое состояние
Честно - не знаю, насколько хорошо это решение, однако оно уж точно быстрее стандартного пути. Если будут идеи более правильной реализации - буду рад выслушать!
3) Что ж. Разобрались, собственно, с кодом. Осталось навести порядок в конфиге (CommandSortPackage.vsct):
Таким образом мы добавляем кнопку с указанным текстом и ссылающуюся на наше действие в контекстное меню VS, а также связываем с ним горячие клавиши:
(Ctrl + E) + (Ctrl + L)
4) Вот и все, наше простенькое расширение готово. Вы можете отдебажить его с помощью экспериментальной версии VS, или же просто собрать его, после чего среди уймы пакетов Вы найдете заветный файлик с расширением *.vsix, при помощи которого Вы можете либо сразу установить расширение в свою студию, либо же даже опубликовать его)
Делаю шаблон проекта VS и хочу, чтобы создаваемый проект содержал настройки и аргументы запуска внешней программы. Файл с настройками должен быть просто скопирован в каталог проекта. Сами настройки VS хранит в файле $projectname$.csproj.user на который нет никаких ссылок из файла проекта (.csproj). Тут возникает проблема: VS не копирует файлы, которые не задействованы в файле проекта. В $projectname$.csproj должно быть прописано что-то вроде такого , чтобы файл $projectname$.csproj.user был, все-таки, скопирован. Но тогда он появляется в Solution Explorer и настройки из него считываются только после переоткрытия решения. Project.vstemplate: AssemblyInfo.csProjectTemplate.csproj.user Как можно решить эту проблему?
Ответ
Можно скрыть элемент в Solution Explorer, если указать значение метасвойства Visible За старый формат проекта не отвечаю. Скорее всего, там надо указывать отдельным элементом: false Это поможет избавиться от лишнего элемента в проекте, но вряд ли поможет с чтением настроек только после переоткрытия решения. Это попахивает ограничением IDE: разработчики, скорее всего, просто не рассчитывали, что пользовательские настройки будут создаваться не пользователем. Кстати, если перейдёте на новый формат проекта, то там профили запуска лежат отдельным красивым файлом launchSettings.json, и профилей может быть много с разными настройками. Рекомендую.
Как добавить дополнительную кнопку в стандартное контекстное меню, которое появляется при щелчке правой кнопкой мыши рядом с кодом? Например такая функциональность:
правый щелчок вызывает стандартное меню
в меню помимо стандартных есть новая кнопка
щелчок по ней вызывает действие (например появляется сообщение)
Ответ
Ответ частично найден. Надо просто создать стандартный проект - Visual Stutio Package. Выбрать Menu Command в качестве интерфейса в диалоговом окне при создании (первый вариант).
Потом открыть файл .vsct и поменять родителя у группы: Именно Привяжет группу (с кнопкой) в меню, появляющееся при нажатии правой кнопкой в коде. тут важен id этого меню IDM_VS_CTXT_CODEWIN и стандартный guid guidSHLMainMenu.
Можно посмотреть другие id и guid (лучше в VS - там с описанием)
https://msdn.microsoft.com/ru-ru/library/vstudio/microsoft.visualstudio.shell.vsmenus_members(v=vs.100).aspx#mainBody В обработчике можно написать что угодно и оно заработает private void MenuItemCallback(object sender, EventArgs e)
{
MessageBox.Show("Уже что-то заработало!!!");
}
Привет! Я пытаюсь затолкать свой шаблон проекта в (Visual C#/Web)(Аналогично тому как там лежит DevExpress Template Gallery на моём скриншоте). Пытаюсь сделать это следующими строками кода в файле MyTemplate.vstemplate: My Template GalleryStart Template GallerymyProj.icoCSharpWebWeb4.0004d72892d-abc3-428d-8fa7-8421a83d6e0atrueXafariApplicationtrue
Ответ
Ручной способ
Создаете проект. Заполняете нужными начальными файлами для шаблона. Сохраняете.
В меню Файл выбираете Экспорт шаблона.
В Wizard'е выбираете: Project Template.
Заполняете поля.
Архив закинется в папку <Рабочая папка студии>\My Exported Templates и в папку <Рабочая папка студии>\Templates\ProjectTemplates (если не сняли галку в wizard'е):
Этот архив нужно поместить в папку <Рабочая папка студии>\Templates\ProjectTemplates\Visual C#\Web (ее нет, поэтому необходимо создать):
Заходите в студию. И Profit.
С помощью VSIX
Дожен быть готов архив. Смотри 1-5 пункты ручного способа
Создаете проект VSIX.
В проекте создаете нужную иерархию папок. В данном случае получается следующая иерархия:
Закидывайте архив в папку ProjectTemplates\CSharp\Web
В манифесте указываете нужные данные. Затем заходите в пункт Assets и добавляете:
Сохраняетесь. Билдитесь. Запускаете VSIX. Открываете студию.
ProjectTemplate + VSIX
Создаете проект типа ProjectTemplate заполняете его.
Выбираете в студии файл с расширением *.vstemplate (по умолчанию его имя совпадает с именем проекта). И в его свойствах в строке Category прописываете путь иерархии, который будет начинаться от CSharp. Т.е. если указываете SubA/SubB/SubC, то шаблон будет лежать в CSharp/SubA/SubB/SubC. В нашем случае указываем только Web:
Создаем проект VSIX.
В манифесте указываете нужные данные:
Билдитесь.
P.S. Немного про элемент TemplateData.
Вы указали ProjectSubType значение Web, но этот элемент может иметь только всего 4 значения:
SmartDevice-NETCFv1 - указывает поддержку .NET Compact Framework версии 1.0
SmartDevice-NETCFv1 - указывает поддержку .NET Compact Framework версии 2.0
CSharp - Задается только тогда, когда элемент ProjectType имеет значение Web (т.е. это проект типа веб-сайт). Указывает на языка программирования.
VisualBasic - тоже самое, что и предыдущий пункт, только язык программирования VisualBasic