Пользовательская команда VisualStudio VSPackage

Я пишу небольшое расширение для Visual Studio 2015. Я добавил VSPackage для встраивания некоторых пользовательских команд в контекстное меню, которое вы получаете, когда вы щелкаете правой кнопкой мыши по проекту или папке в обозревателе решений.

Теперь я хотел бы "открыть диалоговое окно" Добавить новый элемент "и выбрать один из шаблонов, которые я установил с этим VSPackage".

Это код, который я использую для инициализации моих команд:

private TemplateCommand(Package package)
{
    if (package == null)
        throw new ArgumentNullException(nameof(package));

    _package = package;

    var commandService = ServiceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
    if (commandService == null)
        return;

    AddCommand(commandService, CommandId, CreateCustomTemplate);
}

Код обратного вызова CreateCustomTemplate таков:(на данный момент я просто создаю блок сообщений, просто чтобы убедиться, что он работает)

private void CreateCustomTemplate(object sender, EventArgs eventArgs)
{
    //TODO: code to replace!
    var message = string.Format(CultureInfo.CurrentCulture, "Inside {0}.CreateCustomTemplate()", GetType().FullName);

    // Show a message box to prove we were here
    VsShellUtilities.ShowMessageBox(
        ServiceProvider,
        message,
        "CREATE CustomTemplate",
        OLEMSGICON.OLEMSGICON_INFO,
        OLEMSGBUTTON.OLEMSGBUTTON_OK,
        OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
}

Итак, подведем итог: как открыть диалоговое окно "Добавить новый элемент" и выбрать шаблон конкретного элемента?

Например, когда вы пытаетесь создать Class или UserControl, щелкните правой кнопкой мыши папку в обозревателе решений. Щелкните правой кнопкой мыши на папке, затем нажмите Добавить -> UserControl UserControl">

Вы получаете что-то похожее на это: Результат

И это именно то, чего я пытаюсь достичь. Очевидно, я хотел бы создать свой собственный шаблон, а не UserControl.

Если вам нужны какие-либо разъяснения, не стесняйтесь спрашивать. Заранее спасибо за любое предложение

2 ответа

Решение

В конце концов я решил свою проблему.

К сожалению, команда File.AddNewItem (или Project.AddNewItem) не подходит в моем случае, так как я хочу видеть диалог AddNewFile (и эти команды просто добавляют элемент в указанный проект).

Я нашел решение рытье в сети, именно здесь, особенно благодаря ответу Vladimir.Ilic.

Вот код, который я использую для достижения своей цели:

internal sealed class TemplateCommand
{
    private const int CustomCommandId = 0x1023;

    private static readonly Guid CommandSet = COMMANDSET_GUID;
    private readonly Package _package;


    // ReSharper disable MemberCanBePrivate.Global
    // ReSharper disable UnusedAutoPropertyAccessor.Global
    public IServiceProvider ServiceProvider => _package;

    public static TemplateCommand Instance { get; set; }
    // ReSharper restore UnusedAutoPropertyAccessor.Global
    // ReSharper restore MemberCanBePrivate.Global

    public static void Initialize(Package package)
    {
        Instance = new TemplateCommand(package);
    }

    private TemplateCommand(Package package)
    {
        if (package == null)
            throw new ArgumentNullException(nameof(package));

        _package = package;

        var commandService = ServiceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
        if (commandService == null)
            return;

        AddCommand(commandService, CustomCommandId, CreateCustomCommand);
    }

    private static void AddCommand(IMenuCommandService commandService, int commandId, EventHandler callback)
    {
        var command = new CommandID(CommandSet, commandId);
        var menuItem = new MenuCommand(callback, command);
        commandService.AddCommand(menuItem);
    }

    private void CreateCustomCommand(object sender, EventArgs eventArgs)
    {
        AddNewItem("MyCustomCommand");
    }

    private void AddNewItem(string itemName)
    {
        var dte = ServiceProvider.GetService(typeof(DTE)) as DTE;
        if (dte == null)
            return;

        int iDontShowAgain;
        uint projectItemId;
        var strFilter = string.Empty;

        var hierarchy = GetCurrentVsHierarchySelection(out projectItemId);
        if (hierarchy == null)
            return;

        var project = ToDteProject(hierarchy);
        if (project == null)
            return;

        var vsProject = ToVsProject(project);
        if (vsProject == null)
            return;

        var addItemDialog = ServiceProvider.GetService(typeof(IVsAddProjectItemDlg)) as IVsAddProjectItemDlg;
        if (addItemDialog == null)
            return;

        const uint uiFlags = (uint)(__VSADDITEMFLAGS.VSADDITEM_AddNewItems | __VSADDITEMFLAGS.VSADDITEM_SuggestTemplateName | __VSADDITEMFLAGS.VSADDITEM_AllowHiddenTreeView);
        const string categoryNameInNewFileDialog = "MyCustomTemplates";

        // ProjectGuid for C# projects
        var projGuid = new Guid("FAE04EC0-301F-11D3-BF4B-00C04F79EFBC");

        string projectDirectoryPath;
        hierarchy.GetCanonicalName(projectItemId, out projectDirectoryPath);
        var itemNameInNewFileDialog = itemName;
        addItemDialog.AddProjectItemDlg(projectItemId,
                                        ref projGuid,
                                        vsProject,
                                        uiFlags,
                                        categoryNameInNewFileDialog,
                                        itemNameInNewFileDialog,
                                        ref projectDirectoryPath,
                                        ref strFilter,
                                        out iDontShowAgain);
    }

    private static IVsHierarchy GetCurrentVsHierarchySelection(out uint projectItemId)
    {
        IntPtr hierarchyPtr, selectionContainerPtr;
        IVsMultiItemSelect mis;
        var monitorSelection = (IVsMonitorSelection)Package.GetGlobalService(typeof(SVsShellMonitorSelection));
        monitorSelection.GetCurrentSelection(out hierarchyPtr, out projectItemId, out mis, out selectionContainerPtr);

        var hierarchy = Marshal.GetTypedObjectForIUnknown(hierarchyPtr, typeof(IVsHierarchy)) as IVsHierarchy;
        return hierarchy;
    }

    private static Project ToDteProject(IVsHierarchy hierarchy)
    {
        if (hierarchy == null)
            throw new ArgumentNullException(nameof(hierarchy));

        object prjObject;
        if (hierarchy.GetProperty(0xfffffffe, (int)__VSHPROPID.VSHPROPID_ExtObject, out prjObject) == VSConstants.S_OK)
            return (Project)prjObject;

        throw new ArgumentException("Hierarchy is not a project.");
    }

    private IVsProject ToVsProject(Project project)
    {
        if (project == null)
            throw new ArgumentNullException(nameof(project));

        var vsSln = ServiceProvider.GetService(typeof(IVsSolution)) as IVsSolution;
        if (vsSln == null)
            throw new ArgumentException("Project is not a VS project.");

        IVsHierarchy vsHierarchy;
        vsSln.GetProjectOfUniqueName(project.UniqueName, out vsHierarchy);
        // ReSharper disable SuspiciousTypeConversion.Global
        var vsProject = vsHierarchy as IVsProject;
        // ReSharper restore SuspiciousTypeConversion.Global
        if (vsProject != null)
            return vsProject;

        throw new ArgumentException("Project is not a VS project.");
    }
}

Большое спасибо тем, кто прошел мимо и которые пытались (или даже думали) помочь!

Надеюсь, это кому-то поможет,

Искренне

Вы можете выполнить команды "Project.AddNewItem" или "File.AddNewItem", чтобы показать диалоговое окно. Есть несколько способов выполнить команду программно, самый простой - получить экземпляр EnvDTE.DTE и вызвать dte.ExecuteCommand(commandName).

Что касается выбора нужного шаблона, см. Параметры для команды File.AddNewItem. Если повезет, то для команды Project.AddNewItem то же самое.

Другие вопросы по тегам