VisualStudio VSPackage Custom Command

622 views Asked by At

I'm writing a little extension for Visual Studio 2015. I added a VSPackage to to embed some CustomCommands on the Shortcut Menu you get when you RightClick on a Project or a Folder inside the Solution Explorer.

What I would like to do now is "open the Add New Item dialog and select the one of the templates I've installed with this VSPackage".

This is the Code I use to initialize my commands:

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);
}

The CreateCustomTemplate callback code is this:(for the moment I simply create a messageBox, just to ensure it works)

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);
}

So, to recap, how do I open the Add New Item dialog box and select a specific item template?

As an example, when you try to create a Class or a UserControl right-clicking on a folder in the solution explorer RightClick on Folder, then Add -> UserControl

You obtain something similar to this: Result

And this is, exactly, what I'm trying to achieve. Obviously I would like to create my own template and not the UserControl.

If you need any clarification feel free to ask. Thank you in advance for any suggestion

2

There are 2 answers

0
Easly On BEST ANSWER

In the end I resolved my problem.

Unfortunately the command File.AddNewItem (or Project.AddNewItem) is not suitable in my case as I want to see the AddNewFile Dialog (and these command simply adds the item to the specified project).

I Found the solution digging the web, exactly here, specifically thanks to the answer of Vladimir.Ilic.

This is the code I use to achieve my goal:

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.");
    }
}

Big thanks to the ones that passed by and that have tried (or even thinked) to help!

Hope this helps someone,

Sincerely

1
Carlos Quintero On

You can execute the commands "Project.AddNewItem" or "File.AddNewItem" to show the dialog. There are several ways to execute a command programmatically, the easiest is to get the EnvDTE.DTE instance and call dte.ExecuteCommand(commandName).

As for selecting the desired template, see the parameters for the command File.AddNewItem. With some luck the are the same for the Project.AddNewItem command.