我创建了一个插件项目,并且添加了三个插件,项目结构如下:
A区代表图标资源,一般存放插件的图标,16*16的位图。
B区代表项目中插件的个数,有几个则代表有几个插件,这里有三个。
C区这个文件是项目配置文件,项目中所有插件都在这个文件里面配置,如配置插件的图标,名字,快捷键等。
下面粘贴出C区的配置代码和B区中一个插件的代码。学习插件开发的首先集中注意自己可以修改的代码,因为其中有许多默认部分代码是可以不用管的。
.vsct 文件
<?xml version="1.0" encoding="utf-8"?>
<CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This is the file that defines the actual layout and type of the commands.
It is divided in different sections (e.g. command definition, command
placement, ...), with each defining a specific set of properties.
See the comment before each section for more details about how to
use it. -->
<!-- The VSCT compiler (the tool that translates this file into the binary
format that VisualStudio will consume) has the ability to run a preprocessor
on the vsct file; this preprocessor is (usually) the C++ preprocessor, so
it is possible to define includes and macros with the same syntax used
in C++ files. Using this ability of the compiler here, we include some files
defining some of the constants that we will use inside the file. -->
<!--This is the file that defines the IDs for all the commands exposed by VisualStudio. -->
<Extern href="stdidcmd.h" />
<!--This header contains the command ids for the menus provided by the shell. -->
<Extern href="vsshlids.h" />
<!--The Commands section is where commands, menus, and menu groups are defined.
This section uses a Guid to identify the package that provides the command defined inside it. -->
<Commands package="guidSelfVSIXProjectPackage">
<!-- Inside this section we have different sub-sections: one for the menus, another
for the menu groups, one for the buttons (the actual commands), one for the combos
and the last one for the bitmaps used. Each element is identified by a command id that
is a unique pair of guid and numeric identifier; the guid part of the identifier is usually
called "command set" and is used to group different command inside a logically related
group; your package should define its own command set in order to avoid collisions
with command ids defined by other packages. -->
<!-- In this section you can define new menu groups. A menu group is a container for
other menus or buttons (commands); from a visual point of view you can see the
group as the part of a menu contained between two lines. The parent of a group
must be a menu. -->
<Groups>
<Group guid="guidSelfVSIXProjectPackageCmdSet" id="MyMenuGroup" priority="0x0600">
<!-- 父级菜单所在位置:
IDM_VS_MENU_TOOLS 默认菜单工具栏
IDM_VS_CTXT_CODEWIN 代码文件,如.cs文件在编辑器中点右键(上下文右键)
IDM_VS_CTXT_FOLDERNODE 在文件夹上点右键
IDM_VS_CTXT_ITEMNODE 在项目中的文件上点右键
IDM_VS_CTXT_NOCOMMANDS 没有菜单
IDM_VS_CTXT_PROJNODE 在项目节点上点右键
IDM_VS_CTXT_SOLNNODE 在解决方案上点右键
具体介绍官网:https://docs.microsoft.com/zh-cn/visualstudio/extensibility/internals/guids-and-ids-of-visual-studio-menus?view=vs-2019
<Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/>
-->
<Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_CODEWIN" />
</Group>
<Group guid="guidSelfVSIXProjectPackageCmdSet1" id="MyMenuGroup" priority="0x0600">
<Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_CODEWIN" />
</Group>
<Group guid="guidSelfVSIXProjectPackageCmdSet2" id="MyMenuGroup" priority="0x0600">
<Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_CODEWIN" />
</Group>
</Groups>
<!--Buttons section. -->
<!--This section defines the elements the user can interact with, like a menu command or a button
or combo box in a toolbar. -->
<Buttons>
<!--To define a menu group you have to specify its ID, the parent menu and its display priority.
The command is visible and enabled by default. If you need to change the visibility, status, etc, you can use
the CommandFlag node.
You can add more than one CommandFlag node e.g.:
<CommandFlag>DefaultInvisible</CommandFlag>
<CommandFlag>DynamicVisibility</CommandFlag>
If you do not want an image next to your command, remove the Icon node /> -->
<Button guid="guidSelfVSIXProjectPackageCmdSet" id="SelfCommand1Id" priority="0x0100" type="Button">
<Parent guid="guidSelfVSIXProjectPackageCmdSet" id="MyMenuGroup" />
<Icon guid="guidImages" id="bmpPic1" />
<Strings>
<!-- 显示的名称 -->
<ButtonText>正则匹配</ButtonText>
</Strings>
</Button>
<Button guid="guidSelfVSIXProjectPackageCmdSet1" id="cmdidSelfCommand2" priority="0x0100" type="Button">
<Parent guid="guidSelfVSIXProjectPackageCmdSet1" id="MyMenuGroup" />
<Icon guid="guidImages1" id="bmpPic1" />
<Strings>
<ButtonText>字符串长度</ButtonText>
</Strings>
</Button>
<Button guid="guidSelfVSIXProjectPackageCmdSet2" id="cmdidSelfCommand3" priority="0x0100" type="Button">
<Parent guid="guidSelfVSIXProjectPackageCmdSet2" id="MyMenuGroup" />
<Icon guid="guidImages2" id="bmpPic1" />
<Strings>
<ButtonText>是否为纯数字/字母</ButtonText>
</Strings>
</Button>
</Buttons>
<!--The bitmaps section is used to define the bitmaps that are used for the commands.-->
<Bitmaps>
<!-- The bitmap id is defined in a way that is a little bit different from the others:
the declaration starts with a guid for the bitmap strip, then there is the resource id of the
bitmap strip containing the bitmaps and then there are the numeric ids of the elements used
inside a button definition. An important aspect of this declaration is that the element id
must be the actual index (1-based) of the bitmap inside the bitmap strip. -->
<!-- 16*16为一个icon,id为1,如果图片较大,则第二个16*16为第二个icon,id为2,以此类推,例如下:
<Bitmap guid="guidImages" href="Resources\SelfCommand1.png" usedList="bmpPic1,bmpPic2"/>
透明图生成网址:http://ico.toolscat.com/
-->
<Bitmap guid="guidImages" href="Resources\zenze.ico" usedList="bmpPic1" />
<Bitmap guid="guidImages1" href="Resources\strlength.png" usedList="bmpPic1" />
<Bitmap guid="guidImages2" href="Resources\numorabc.png" usedList="bmpPic1" />
</Bitmaps>
</Commands>
<!--配置快捷键
<KeyBinding guid="" id="" >
如: 按住Control+1+1即是快捷键
-->
<KeyBindings>
<KeyBinding guid="guidSelfVSIXProjectPackageCmdSet" id="SelfCommand1Id" key1="1" mod1="CONTROL" key2="1" mod2="CONTROL" editor="guidVSStd97" />
<KeyBinding guid="guidSelfVSIXProjectPackageCmdSet1" id="cmdidSelfCommand2" key1="2" mod1="CONTROL" key2="2" mod2="CONTROL" editor="guidVSStd97" />
<KeyBinding guid="guidSelfVSIXProjectPackageCmdSet2" id="cmdidSelfCommand3" key1="3" mod1="CONTROL" key2="3" mod2="CONTROL" editor="guidVSStd97" />
</KeyBindings>
<Symbols>
<!-- This is the package guid. -->
<GuidSymbol name="guidSelfVSIXProjectPackage" value="{a1947d48-d552-4542-be80-8d5e29667c0a}" />
<!-- This is the guid used to group the menu commands together -->
<GuidSymbol name="guidSelfVSIXProjectPackageCmdSet" value="{93d4d243-8331-475a-aead-f962c6e967f9}">
<IDSymbol name="MyMenuGroup" value="0x1020" />
<IDSymbol name="SelfCommand1Id" value="0x0100" />
</GuidSymbol>
<!-- 多张16*16依此类推
<IDSymbol name="bmpPic2" value="2" />
-->
<GuidSymbol name="guidImages" value="{2289c900-339b-45b1-80b9-3889493ae0ac}">
<IDSymbol name="bmpPic1" value="1" />
</GuidSymbol>
<GuidSymbol value="{8ab0b9c2-630b-48f9-b0ea-cebea25eebc4}" name="guidSelfVSIXProjectPackageCmdSet1">
<IDSymbol value="4128" name="MyMenuGroup" />
<IDSymbol value="256" name="cmdidSelfCommand2" />
</GuidSymbol>
<GuidSymbol value="{0e26f892-002f-4c9c-864a-de74bcd3b639}" name="guidImages1">
<IDSymbol name="bmpPic1" value="1" />
</GuidSymbol>
<GuidSymbol value="{fc9cf163-e329-4ae6-a1a1-7a0b154aad7d}" name="guidSelfVSIXProjectPackageCmdSet2">
<IDSymbol value="4128" name="MyMenuGroup" />
<IDSymbol value="256" name="cmdidSelfCommand3" />
</GuidSymbol>
<GuidSymbol value="{5fe90ea3-c78b-4ad4-8f2b-75a2b1b77091}" name="guidImages2">
<IDSymbol name="bmpPic1" value="1" />
</GuidSymbol>
</Symbols>
</CommandTable>
SelfCommand1.cs
using System;
using System.ComponentModel.Design;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using EnvDTE;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Task = System.Threading.Tasks.Task;
namespace SelfVSIXProject
{
///
/// Command handler
///
internal sealed class SelfCommand1
{
///
/// Command ID.
///
public const int CommandId = 0x0100;
///
/// Command menu group (command set GUID).
///
public static readonly Guid CommandSet = new Guid("93d4d243-8331-475a-aead-f962c6e967f9");
///
/// VS Package that provides this command, not null.
///
private readonly AsyncPackage package;
///
/// Initializes a new instance of the class.
/// Adds our command handlers for menu (commands must exist in the command table file)
///
/// Owner package, not null.
/// Command service to add command to, not null.
private SelfCommand1(AsyncPackage package, OleMenuCommandService commandService)
{
this.package = package ?? throw new ArgumentNullException(nameof(package));
commandService = commandService ?? throw new ArgumentNullException(nameof(commandService));
var menuCommandID = new CommandID(CommandSet, CommandId);
var menuItem = new MenuCommand(this.Execute, menuCommandID);
commandService.AddCommand(menuItem);
}
///
/// Gets the instance of the command.
///
public static SelfCommand1 Instance
{
get;
private set;
}
///
/// Gets the service provider from the owner package.
///
private Microsoft.VisualStudio.Shell.IAsyncServiceProvider ServiceProvider
{
get
{
return this.package;
}
}
///
/// Initializes the singleton instance of the command.
///
/// Owner package, not null.
public static async Task InitializeAsync(AsyncPackage package)
{
// Switch to the main thread - the call to AddCommand in SelfCommand1's constructor requires
// the UI thread.
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);
OleMenuCommandService commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService;
Instance = new SelfCommand1(package, commandService);
}
///
/// This function is the callback used to execute the command when the menu item is clicked.
/// See the constructor to see how the menu item is associated with this function using
/// OleMenuCommandService service and MenuCommand class.
///
/// Event sender.
/// Event args.
private void Execute(object sender, EventArgs e)
{
ThreadHelper.ThrowIfNotOnUIThread();
//自定义功能
//操作选中功能, 用正则表达式判断是否匹配成功
//格式例:(inputStr)(pattern) => (123qwe)([0-9]+) 则提示true
DTE dte = ServiceProvider.GetServiceAsync(typeof(DTE)).Result as DTE;
string selectTXT = string.Empty;
if (dte.ActiveDocument != null && dte.ActiveDocument.Type == "Text")
{
var selection = (TextSelection)dte.ActiveDocument.Selection;
string text = selection?.Text;
if (string.IsNullOrWhiteSpace(text))
{
ShowMsgBox("您正在使用正则表达式匹配判断功能,请先选中内容,且格式为:\n inputStr @Regex pattern ");
}
else
{
text = text.Trim();
bool isMatch = Regex.IsMatch(text, @"^.+\@Regex.+$");
if (!isMatch)
{
ShowMsgBox("您正在使用正则表达式匹配判断功能,请使用格式为:\n inputStr @Regex pattern ");
}
else
{
//inputStr @Regex pattern
string[] Strs = text.Split(new string[] { "@Regex" }, StringSplitOptions.RemoveEmptyEntries);
if (Strs == null || Strs.Length != 2)
{
ShowMsgBox("您正在使用正则表达式匹配判断功能,请使用格式为:\n inputStr @Regex pattern ");
}
else
{
ShowMsgBox($"匹配结果:{(Regex.IsMatch(Strs[0].Trim(), Strs[1].Trim()) ? "匹配成功" : "匹配失败")}");
}
}
//selection.Text = text; //相当于修改当前代码,在两端加内容
//selectTXT = text; //直接用selection.Text无效
}
}
}
public void ShowMsgBox(string msg)
{
VsShellUtilities.ShowMessageBox(this.package, msg, "", OLEMSGICON.OLEMSGICON_NOICON, OLEMSGBUTTON.OLEMSGBUTTON_OK, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
}
}
}
粘贴出来的这个插件代码很简单,一看就会。且其中包含了插件开发很多基本的东西。
觉得不错的,去 该文章对应的GitHub点个Star吧!