none
Paste special menu

    Question

  • Hi, how I can add a menuitem (e.g "Paste as BSON") inside

           Edit- > Paste Special

    menu (like "Paste JSON as Classes)? I think, I need a ID for the submenu, but I can't find this for "Edit- > Paste Special" ;(regards,

    Dulcinea

    Tuesday, January 19, 2016 12:52 PM

Answers

  • Wrote a quick proof of concept sample to illustrate this. Will try and get the details and source uploaded to the VSX Arcana blog shortly. But here are the details: You should add your button to the already existing menu group that those other two commands are already using. Which in this case, would be the guidSHLMainMenu:IDG_VS_EDIT_PASTE group. For example:

    <CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable" xmlns:xs="http://www.w3.org/2001/XMLSchema">

      <Extern href="stdidcmd.h"/>
      <Extern href="vsshlids.h"/>

      <Commands package="guidPasteTextAsCommentPackage">
        <Buttons>
          <Button guid="guidPasteTextAsCommentPackageCmdSet" id="PasteTextAsCommentId" priority="0x0300" type="Button">
            <Parent guid="guidSHLMainMenu" id="IDG_VS_EDIT_PASTE" />
            <CommandFlag>DefaultInvisible</CommandFlag>
            <CommandFlag>DynamicVisibility</CommandFlag>
            <CommandFlag>DefaultDisabled</CommandFlag>
            <Strings>
              <ButtonText>Paste Text As Comment</ButtonText>
            </Strings>
          </Button>
        </Buttons>
      </Commands>

      <Symbols>
        <GuidSymbol name="guidPasteTextAsCommentPackage" value="{bf80cf02-5d87-4f70-b9a4-50ec511b5a95}" />
        <GuidSymbol name="guidPasteTextAsCommentPackageCmdSet" value="{0b870634-0b51-44d3-95a0-85177eb029d2}">
          <IDSymbol name="PasteTextAsCommentId" value="0x0100" />
        </GuidSymbol>
      </Symbols>

    </CommandTable>

    Then you can show and enable the command programmatically from your command's beforeQueryStatus event handler. For example:

    using System;
    using System.Windows;
    using System.ComponentModel.Design;
    using System.Globalization;
    using Microsoft.VisualStudio.Shell;
    using Microsoft.VisualStudio.Shell.Interop;
    using Microsoft.VisualStudio.Editor;
    using Microsoft.VisualStudio.ComponentModelHost;
    using Microsoft.VisualStudio.TextManager;
    using EnvDTE;
    using EnvDTE80;

    namespace ElmersPackage
    {
        internal sealed class PasteTextAsComment
        {
            public const int CommandId = 0x0100;
            public static readonly Guid CommandSet = new Guid("0b870634-0b51-44d3-95a0-85177eb029d2");
            private readonly Package package;

            private DTE2 _dte = null;
            DTE2 GetDTE()
            {
                if (_dte == null)
                {
                    _dte = Package.GetGlobalService(typeof(SDTE)) as DTE2;
                }
                return _dte;
            }

            private IVsEditorAdaptersFactoryService _adapterFactoryService = null;
            IVsEditorAdaptersFactoryService GetAdapterFactoryService()
            {
                if (_adapterFactoryService==null)
                {
                    IComponentModel componentModel = (IComponentModel)Package.GetGlobalService(typeof(SComponentModel));
                    _adapterFactoryService = componentModel.GetService<IVsEditorAdaptersFactoryService>();
                }
                return _adapterFactoryService;
            }

            private PasteTextAsComment(Package package)
            {
                if (package == null)
                {
                    throw new ArgumentNullException("package");
                }

                this.package = package;

                OleMenuCommandService commandService = this.ServiceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
                if (commandService != null)
                {
                    var menuCommandID = new CommandID(CommandSet, CommandId);
                    var menuItem = new OleMenuCommand(this.MenuItemCallback, menuCommandID);
                    commandService.AddCommand(menuItem);
                    menuItem.BeforeQueryStatus += MenuItem_BeforeQueryStatus;
                }
            }

            public static PasteTextAsComment Instance
            {
                get;
                private set;
            }

            private IServiceProvider ServiceProvider
            {
                get
                {
                    return this.package;
                }
            }

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

            private void MenuItem_BeforeQueryStatus(object sender, EventArgs e)
            {
                OleMenuCommand pasteCommand = (OleMenuCommand)sender;

                // hidden by default
                pasteCommand.Visible = false;
                pasteCommand.Enabled = false;

                Document activeDoc = GetDTE().ActiveDocument;
                if (activeDoc != null && activeDoc.ProjectItem!=null && activeDoc.ProjectItem.ContainingProject!=null)
                {
                    string lang = activeDoc.Language;
                    if (activeDoc.Language.Equals("CSharp"))
                    {
                        // show command if active document is a csharp file.
                        pasteCommand.Visible = true;
                    }

                    // enable commmand if command is visible and there is text on the clipboard
                    pasteCommand.Enabled = (pasteCommand.Visible) ? Clipboard.ContainsText() : false;
                }
            }

            private void MenuItemCallback(object sender, EventArgs e)
            {
                string comment = string.Format("// {0}", Clipboard.GetText());
                DTE2 dte = GetDTE();
                try
                {
                    dte.UndoContext.Open("Paste Text as Comment");
                    var selection = (TextSelection)dte.ActiveDocument.Selection;
                    if (selection!= null)
                    {
                        selection.Insert(comment);
                        dte.ActiveDocument.Activate();
                        dte.ExecuteCommand("Edit.FormatDocument");
                    }
                }
                finally
                {
                    dte.UndoContext.Close();
                }
            }
        }
    }

    Then you need to ensure that the package is loaded to ensure your BeforeQueryStatus handler is getting invoked. And given the above example is specific to CSharp, I just added the following ProvideAutoLoad attribute to my package object:

           [ProvideAutoLoad(VSConstants.UICONTEXT.CSharpProject_string)]

    Sincerely,


    Ed Dore

    Wednesday, January 20, 2016 10:13 PM
    Moderator

All replies

  • Where exactly in the VS IDE is this menu being displayed? Is there a particular editor you are pasting content to? Using the EnableVSIPLogging registry value might help ID the actual command. Back in the days of OLE Linking/Embedding, you needed to ensure you had a specific clipboard format present on the clipboard, but I suspect that isn't the case here.

    But if you can tell me the context in which you're invoking the menu item, I may be able to track down the details.

    Thanks,


    Ed Dore

    Wednesday, January 20, 2016 4:32 AM
    Moderator
  • Hi,

    thanks for you answer.

    I need to place  my new commmand "Paste as BISON" at the right Position, under "Paste Special" menu in Vs2015 (see image).

    I have created an new extension project (vsix) in VS2015, with a template for a command.  This command appears under "Tools" menu (default), but I need this under "Edit/Paste Special"...

    With activated VSIPLogging I can find the id for the commands "paste Json as classes" and "Paste XML as classes", but I think, I need the Id for the submenu "Paste Special". Is this right?

    Thanks for your help,

    Dulcinea


    • Edited by DulcineaS Wednesday, January 20, 2016 7:54 AM
    Wednesday, January 20, 2016 7:52 AM
  • Wrote a quick proof of concept sample to illustrate this. Will try and get the details and source uploaded to the VSX Arcana blog shortly. But here are the details: You should add your button to the already existing menu group that those other two commands are already using. Which in this case, would be the guidSHLMainMenu:IDG_VS_EDIT_PASTE group. For example:

    <CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable" xmlns:xs="http://www.w3.org/2001/XMLSchema">

      <Extern href="stdidcmd.h"/>
      <Extern href="vsshlids.h"/>

      <Commands package="guidPasteTextAsCommentPackage">
        <Buttons>
          <Button guid="guidPasteTextAsCommentPackageCmdSet" id="PasteTextAsCommentId" priority="0x0300" type="Button">
            <Parent guid="guidSHLMainMenu" id="IDG_VS_EDIT_PASTE" />
            <CommandFlag>DefaultInvisible</CommandFlag>
            <CommandFlag>DynamicVisibility</CommandFlag>
            <CommandFlag>DefaultDisabled</CommandFlag>
            <Strings>
              <ButtonText>Paste Text As Comment</ButtonText>
            </Strings>
          </Button>
        </Buttons>
      </Commands>

      <Symbols>
        <GuidSymbol name="guidPasteTextAsCommentPackage" value="{bf80cf02-5d87-4f70-b9a4-50ec511b5a95}" />
        <GuidSymbol name="guidPasteTextAsCommentPackageCmdSet" value="{0b870634-0b51-44d3-95a0-85177eb029d2}">
          <IDSymbol name="PasteTextAsCommentId" value="0x0100" />
        </GuidSymbol>
      </Symbols>

    </CommandTable>

    Then you can show and enable the command programmatically from your command's beforeQueryStatus event handler. For example:

    using System;
    using System.Windows;
    using System.ComponentModel.Design;
    using System.Globalization;
    using Microsoft.VisualStudio.Shell;
    using Microsoft.VisualStudio.Shell.Interop;
    using Microsoft.VisualStudio.Editor;
    using Microsoft.VisualStudio.ComponentModelHost;
    using Microsoft.VisualStudio.TextManager;
    using EnvDTE;
    using EnvDTE80;

    namespace ElmersPackage
    {
        internal sealed class PasteTextAsComment
        {
            public const int CommandId = 0x0100;
            public static readonly Guid CommandSet = new Guid("0b870634-0b51-44d3-95a0-85177eb029d2");
            private readonly Package package;

            private DTE2 _dte = null;
            DTE2 GetDTE()
            {
                if (_dte == null)
                {
                    _dte = Package.GetGlobalService(typeof(SDTE)) as DTE2;
                }
                return _dte;
            }

            private IVsEditorAdaptersFactoryService _adapterFactoryService = null;
            IVsEditorAdaptersFactoryService GetAdapterFactoryService()
            {
                if (_adapterFactoryService==null)
                {
                    IComponentModel componentModel = (IComponentModel)Package.GetGlobalService(typeof(SComponentModel));
                    _adapterFactoryService = componentModel.GetService<IVsEditorAdaptersFactoryService>();
                }
                return _adapterFactoryService;
            }

            private PasteTextAsComment(Package package)
            {
                if (package == null)
                {
                    throw new ArgumentNullException("package");
                }

                this.package = package;

                OleMenuCommandService commandService = this.ServiceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
                if (commandService != null)
                {
                    var menuCommandID = new CommandID(CommandSet, CommandId);
                    var menuItem = new OleMenuCommand(this.MenuItemCallback, menuCommandID);
                    commandService.AddCommand(menuItem);
                    menuItem.BeforeQueryStatus += MenuItem_BeforeQueryStatus;
                }
            }

            public static PasteTextAsComment Instance
            {
                get;
                private set;
            }

            private IServiceProvider ServiceProvider
            {
                get
                {
                    return this.package;
                }
            }

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

            private void MenuItem_BeforeQueryStatus(object sender, EventArgs e)
            {
                OleMenuCommand pasteCommand = (OleMenuCommand)sender;

                // hidden by default
                pasteCommand.Visible = false;
                pasteCommand.Enabled = false;

                Document activeDoc = GetDTE().ActiveDocument;
                if (activeDoc != null && activeDoc.ProjectItem!=null && activeDoc.ProjectItem.ContainingProject!=null)
                {
                    string lang = activeDoc.Language;
                    if (activeDoc.Language.Equals("CSharp"))
                    {
                        // show command if active document is a csharp file.
                        pasteCommand.Visible = true;
                    }

                    // enable commmand if command is visible and there is text on the clipboard
                    pasteCommand.Enabled = (pasteCommand.Visible) ? Clipboard.ContainsText() : false;
                }
            }

            private void MenuItemCallback(object sender, EventArgs e)
            {
                string comment = string.Format("// {0}", Clipboard.GetText());
                DTE2 dte = GetDTE();
                try
                {
                    dte.UndoContext.Open("Paste Text as Comment");
                    var selection = (TextSelection)dte.ActiveDocument.Selection;
                    if (selection!= null)
                    {
                        selection.Insert(comment);
                        dte.ActiveDocument.Activate();
                        dte.ExecuteCommand("Edit.FormatDocument");
                    }
                }
                finally
                {
                    dte.UndoContext.Close();
                }
            }
        }
    }

    Then you need to ensure that the package is loaded to ensure your BeforeQueryStatus handler is getting invoked. And given the above example is specific to CSharp, I just added the following ProvideAutoLoad attribute to my package object:

           [ProvideAutoLoad(VSConstants.UICONTEXT.CSharpProject_string)]

    Sincerely,


    Ed Dore

    Wednesday, January 20, 2016 10:13 PM
    Moderator
  • Hi, great! I am waiting for this example on VSX Arcana blog!

    Thanks!

    Thursday, January 21, 2016 1:15 PM