【VS拓展入门】<四> 菜单的编辑

IDE 菜单栏包含 " 文件"、" 编辑"、" 视图"、" 窗口" 和 " 帮助" 等菜单类别。用户扩展Visual Studio的菜单建议参考官方准则说明:Visual Studio 的菜单和命令
扩展的菜单在项目的 .vsct 文件中声明。

从 Visual Studio 2019 开始,由扩展提供的顶级菜单放置在 " 扩展 " 菜单下。

一、示例一:在 IDE 菜单栏上创建新菜单

1、创建菜单命令

  1. 创建 VSIX 项目模板,并命名为TopLevelMenu。
  2. 通过" Visual c # 项> Extensibility(扩展性) > Command(命令)",添加自定义命令 TopLevelMenuCommand.cs。

此时解决方案目录如下:


2、通过修改_.vsct_ 文件创建新菜单
在 节点内包含多个节点,找到name属性为"guidTopLevelMenuPackageCmdSet "的节点,添加元素,如下:

<IDSymbol name="TopLevelMenu" value="0x1021"/>

在<Commands>节点之内,<Groups>节点之前创建<Menus>节点,并添加<Menu>节点,如下:

    <Menus>
	    <Menu guid="guidTopLevelMenuPackageCmdSet" id="TopLevelMenu" priority="0x700" type="Menu">
		    <Parent guid="guidSHLMainMenu" id="IDG_VS_MM_TOOLSADDINS" />
		    <Strings>
			    <ButtonText>Test Menu</ButtonText>
		    </Strings>
	    </Menu>
    </Menus>

在 <Groups> 部分中,找到 <Group> 并将元素更改 <Parent> 为指向刚刚添加的菜单:

      <Group guid="guidTopLevelMenuPackageCmdSet" id="MyMenuGroup" priority="0x0600">
	      <Parent guid="guidTopLevelMenuPackageCmdSet" id="TopLevelMenu"/>
      </Group>

在 <Buttons> 部分中,找到 <Button> 节点。 然后,在 <Strings> 节点中,将 <ButtonText> 元素更改为 调用 TopLevelMenuCommand:

<ButtonText>调用 TopLevelMenuCommand</ButtonText>

调试效果如下:


点击命令效果:

二、示例二:向菜单中添加子菜单

此示例基于示例一,在示例一的基础上继续扩展。
1、通过修改.vsct 文件创建子菜单
打开TopLevelMenuPackage. vsct。
继续找到name属性为"guidTopLevelMenuPackageCmdSet "的<GuidSymbol>节点,添加<IDSymbol>元素,如下:

			<IDSymbol name="SubMenu" value="0x1100"/>
			<IDSymbol name="SubMenuGroup" value="0x1150"/>
			<IDSymbol name="cmdidTestSubCommand" value="0x0105"/>

将新创建的子菜单添加到 <Menus> 部分:

			<Menu guid="guidTopLevelMenuPackageCmdSet" id="SubMenu" priority="0x0100" type="Menu">
				<Parent guid="guidTopLevelMenuPackageCmdSet" id="MyMenuGroup"/>
				<Strings>
					<ButtonText>Sub Menu</ButtonText>
					<CommandName>Sub Menu</CommandName>
				</Strings>
			</Menu>

将定义的菜单组添加到 <Groups> 部分,并使其成为子菜单的子菜单。

			<Group guid="guidTopLevelMenuPackageCmdSet" id="SubMenuGroup" priority="0x0000">
				<Parent guid="guidTopLevelMenuPackageCmdSet" id="SubMenu"/>
			</Group>

向元素<Buttons>部分添加一个新 <Button>  ,以将定义的命令cmdidTestSubCommand成为子菜单上的一项。

			<Button guid="guidTopLevelMenuPackageCmdSet" id="cmdidTestSubCommand" priority="0x0000" type="Button">
				<Parent guid="guidTopLevelMenuPackageCmdSet" id="SubMenuGroup" />
				<Icon guid="guidImages" id="bmpPic2" />
				<Strings>
					<CommandName>cmdidTestSubCommand</CommandName>
					<ButtonText>调用Sub Command</ButtonText>
				</Strings>
			</Button>

运行调试,效果如下:


2、添加命令
打开自定义命令TopLevelMenuCommand.cs,添加命令定义:

public const int cmdidTestSubCmd = 0x0105;

在构造方法,为子菜单添命令。

CommandID subCommandID = new CommandID(CommandSet, cmdidTestSubCmd);
MenuCommand subItem = new MenuCommand(new EventHandler(SubItemCallback), subCommandID);
commandService.AddCommand(subItem);

添加事件回调:

private void SubItemCallback(object sender, EventArgs e)
{
    ThreadHelper.ThrowIfNotOnUIThread();
    string message = string.Format(CultureInfo.CurrentCulture, "Inside {0}.SubItemCallback()", this.GetType().FullName);
    string title = "CmdIdTestSubCmd";

    // Show a message box to prove we were here
    VsShellUtilities.ShowMessageBox(
        this.package,
        message,
        title,
        OLEMSGICON.OLEMSGICON_INFO,
        OLEMSGBUTTON.OLEMSGBUTTON_OK,
        OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
}

运行调试,点击子菜单命令效果如下:

三、示例三:创建动态项列表命令


动态菜单列表以菜单上的占位符开头。 每次显示菜单时,IDE请求VSPackage,显示在占位符中的所有命令 。 动态列表可以出现在菜单上的任何位置。 但是,动态列表通常存储在子菜单上或菜单底部。如窗口菜单底部动态显示当前打开窗口:


通过使用这些设计模式,可以在不影响菜单上其他命令的位置的情况下,使命令的动态列表展开和收缩。
从技术上讲,动态列表还可以应用于工具栏。 但是,官方不建议这种用法,因为工具栏应保持不变,除非用户执行特定步骤来更改它。

在本示例中,动态 MRU 列表显示在现有子菜单的底部,并与子菜单的其余部分分隔开。此示例基于示例二,在示例二的基础上继续扩展。
1、通过修改_.vsct_ 文件创建动态项占位
在 Symbols 部分中,在 GuidSymbol 名为 guidTestCommandPackageCmdSet 的节点中,添加 MRUListGroup 组和命令的符号 cmdidMRUList ,如下所示:

			<IDSymbol name="MRUListGroup" value="0x1200"/>
			<IDSymbol name="cmdidMRUList" value="0x0200"/>

在Groups元素部分,添加声明的组:

			<Group guid="guidTopLevelMenuPackageCmdSet" id="MRUListGroup" priority="0x0100">
				<Parent guid="guidTopLevelMenuPackageCmdSet" id="SubMenu"/>
			</Group>

添加动态命令“查看当前选择脚本”占位按钮,DynamicItemStart标志允许动态生成命令:

			<Button guid="guidTopLevelMenuPackageCmdSet" id="cmdidMRUList" type="Button" priority="0x0100">
				<Parent guid="guidTopLevelMenuPackageCmdSet" id="MRUListGroup" />
				<CommandFlag>DynamicItemStart</CommandFlag>
				<Strings>
					<CommandName>cmdidMRUList</CommandName>
					<ButtonText>查看当前选择脚本</ButtonText>
				</Strings>
			</Button>

运行调试,效果如下:


2、填充 MRU 列表
添加命名空间:

using System.Collections;

添加命令定义:

public const uint cmdidMRUList = 0x200;

在构造函数内添加以下代码:

this.InitMRUMenu(commandService);

添加以下代码:
InitMRUMenu():初始化 MRU list 菜单命令
InitializeMRUList():初始化在 MRU 列表中显示的项的字符串列表。通过mc.Visible = false;可以将命令设为不可见。
BeforeQueryStatus为显示菜单命令之前调用的事件处理。

        #region InitializeMRU

        private int numMRUItems = 4;//生成项数
        private int baseMRUID = (int)cmdidMRUList;//初始占位符ID
        private ArrayList mruList;//命令名称字符串

        /// <summary>
        /// 初始化在 MRU 列表中显示的项的字符串列表
        /// </summary>
        private void InitializeMRUList()
        {
            if (null != mruList) return;

            mruList = new ArrayList();

            for (int i = 0; i < this.numMRUItems; i++) mruList.Add(string.Format(CultureInfo.CurrentCulture, "C# Script {0}.cs", i + 1));
        }
        /// <summary>
        /// 初始化 MRU list 菜单命令
        /// </summary>
        /// <param name="mcs"></param>
        private void InitMRUMenu(OleMenuCommandService mcs)
        {
            InitializeMRUList();

            for (int i = 0; i < this.numMRUItems; i++)
            {
                var cmdID = new CommandID(CommandSet, this.baseMRUID + i);//赋值ID
                var mc = new OleMenuCommand(OnMRUExec, cmdID);
                mc.BeforeQueryStatus += OnMRUQueryStatus;//当客户端请求命令的状态时调用,准备打卡
                mcs.AddCommand(mc);
            }

        }
        /// <summary>
        /// 显示回调
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnMRUQueryStatus(object sender, EventArgs e)
        {
            OleMenuCommand menuCommand = sender as OleMenuCommand;
            if (null == menuCommand) return;

            int MRUItemIndex = menuCommand.CommandID.ID - this.baseMRUID;
            if (MRUItemIndex >= 0 && MRUItemIndex < this.mruList.Count)
            {
                menuCommand.Text = this.mruList[MRUItemIndex] as string;
            }

        }
        /// <summary>
        /// 触发处理命令回调
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnMRUExec(object sender, EventArgs e)
        {
            var menuCommand = sender as OleMenuCommand;
            if (null == menuCommand) return;

            int MRUItemIndex = menuCommand.CommandID.ID - this.baseMRUID;

            if (MRUItemIndex >= 0 && MRUItemIndex < this.mruList.Count)
            {
                string selection = this.mruList[MRUItemIndex] as string;
                //向后排序
                for (int i = MRUItemIndex; i > 0; i--)
                {
                    this.mruList[i] = this.mruList[i - 1];
                }
                this.mruList[0] = selection;

                System.Windows.Forms.MessageBox.Show(string.Format(CultureInfo.CurrentCulture, "打开脚本 {0}", selection));
            }

        }
        #endregion

运行调试效果,选中当前“查看当前选择脚本”,效果如下:


再次打开列表,选中C# Scripts 2.cs:

发现列表更新,点击“查看当前选择脚本”,弹出“打开C# Scripts 2.cs”,效果如下:
 

四、示例四:动态更改菜单命令的文本

在示例三中使用了BeforeQueryStatus事件接口和OleMenuCommand,通过示例四继续加深对这两个部分的理解。为了避免和其他菜单命令混淆,进行清理卸载扩展插件,并创建新的Visx工程。


1、创建菜单命令

  1. 创建 VSIX 项目模板,并命名为MenuText。
  2. 通过" Visual c # 项> Extensibility(扩展性) > Command(命令)",添加自定义命令 ChangeMenuText.cs

此时解决方案目录如下:

2、修改.vsct文件,添加标志
添加动态命令“Invoke ChangeMenuText”占位按钮,并添加添加<CommandFlag>元素,TextChanges标志命令文本可修改:

      <Button guid="guidMenuTextPackageCmdSet" id="ChangeMenuTextId" priority="0x0100" type="Button">
        <Parent guid="guidMenuTextPackageCmdSet" id="MyMenuGroup" />
        <Icon guid="guidImages" id="bmpPic1" />
        <CommandFlag>TextChanges</CommandFlag>
        <Strings>
          <ButtonText>Invoke ChangeMenuText</ButtonText>
        </Strings>
      </Button>

3、修改_ChangeMenuText.cs_添加事件
修改ChangeMenuText构造方法,使用OleMenuCommand命令替代MenuCommand,并注册BeforeQueryStatus的事件处理。代码如下:

        private ChangeMenuText(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 OleMenuCommand(this.Execute, menuCommandID);
            menuItem.BeforeQueryStatus += OnBeforeQueryStatus;
            commandService.AddCommand(menuItem);
        }

添加OnBeforeQueryStatus实现:

        private void OnBeforeQueryStatus(object sender, EventArgs e)
        {
            var myCommand = sender as OleMenuCommand;
            if (null != myCommand)
            {
                myCommand.Text = "New Text";
            }
        }
  • OleMenuCommand继承自MenuCommand,并包含BeforeQueryStatus事件处理。对BeforeQueryStatus,官方文档的说明只有一句话:“当客户端请求命令时调用”。但在事件内加入弹窗或断点调试时发现,请求命令时机并不是特表明确,不像Execute()方法一样,点击即刻触发。我通过反编译Microsoft.VisualStudio.Shell.15.0.dll程序集查找到相关信息:


BeforeQueryStatus事件处理和OleStatus有关,当调用OleStatus的get访问器就会触发。

运行调试效果,效果如下:


点击"Invoke ChangeMenuText"按钮,弹出默认提示框后,发现按钮名称改变:

五、示例五:更改菜单命令按钮的外观

在示例四的基础上,添加更改菜单命令按钮外观的功能。

1、修改ChangeMenuText.cs

修改Execute命令执行方法,当命令按钮文本变成“New Text”时,触发命令按钮后,按钮不可选中:

        private void Execute(object sender, EventArgs e)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var command = sender as OleMenuCommand;
            if (command.Text == "New Text") ChangeMyCommand(command.CommandID.ID, false);
        }
        public bool ChangeMyCommand(int cmdID, bool enableCmd)
        {
            bool cmdUpdated = false;
            var mcs = this.package.GetService<IMenuCommandService, OleMenuCommandService>();
            var newCmdID = new CommandID(CommandSet, cmdID);
            MenuCommand mc = mcs.FindCommand(newCmdID);
            if (mc != null)
            {
                mc.Enabled = enableCmd;
                //mc.Checked = true;
                cmdUpdated = true;
            }
            return cmdUpdated;
        }
  • 通过package.GetService<IMenuCommandService, OleMenuCommandService>():获取当前包的菜单服务。
  • 通过调用服务FindCommand:通过CommandID查找到菜单命令。
  • 通过Enabled属性修改命令按钮不可用。通过Visible可设为不可见。通过Checked可设置选中状态。

上述代码也可以直接简写为:

        private void Execute(object sender, EventArgs e)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            var command = sender as OleMenuCommand;
            if (command.Text == "New Text")
            {
                MenuCommand mc = package.GetService<IMenuCommandService, OleMenuCommandService>().FindCommand(command.CommandID);
                if (mc != null) mc.Enabled = false;
            }
        }

运行调试效果,效果如下:


点击"Invoke ChangeMenuText"按钮,弹出默认提示框后,发现按钮名称改变:

再次点击按钮,按钮不可用:

博客的示例源码:​​​​​​​GitHub - 21thCenturyBoy/VSIX_HelloWorld: 扩展VisualStudio编译器示例

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
我要挣钱(http://www.51zhengqian.net)编辑 目录 第1章 掀起你的盖头来——初识Android 1.1 认识Android 1.2 Android的背景 1.2.1 Android的历史 1.2.2 Android的发展 1.3 我的Android我做主 1.3.1 开发基于Android平台的应用 1.3.2 参加Android开发者大赛 1.3.3 个人英雄主义再现——得到更多人的认可和尊重 1.3.4 获得应有的收益——AndroidMarket 1.4 真实体验——Android模拟器 1.4.1 模拟器概述 1.4.2 模拟器和真机的区别 1.4.3 模拟器使用注意事项 1.5 更上一层楼——加入Android开发社区 1.6 本章小结 第2章 工欲善其事 必先利其器——搭建Android开发环境 2.1 开发Android应用前的准备 2.1.1 Android开发系统要求 2.1.2 Android软件开发包 2.1.3 其他注意事项 2.2 Windows开发环境搭建 2.2.1 JDK、Eclipse、AndroidSDK软件安装 2.2.2 SDK的家在哪里——设定AndroidSDKHome 2.2.3 真的准备好了吗——开发环境验证 2.2.4 创建Android虚拟设备(AVD) 2.3 Linux一族——Ubuntu开发环境搭建 2.3.1 Java、Eclipse和ADT插件安装 2.3.2 设定AndroidSDKHome 2.4 MacOS一族——苹果开发环境搭建 2.5 本章小结 第3章 清点可用资本——AndroidSDK介绍 3.1 AndroidSDK基础 3.2 深入探寻AndroidSDK的密码 3.2.1 AndroidSDK目录结构 3.2.2 android.jar及内部结构 3.2.3 SDK文档及阅读技巧 3.2.4 先来热热身——AndroidSDK例子解析 3.2.5 SDK提供的工具介绍 3.3 Android典型包分析 3.3.1 开发的基石——AndroidAPI核心开发包介绍 3.3.2 拓展开发外延——Android可选API介绍 3.4 本章小结 第4章 赚钱的市场——AndroidMarket及应用发布 4.1 GoogleMarket产生背景与目的 4.2 体验“选货”的乐趣——在G1上体验Market的使用 4.3 Android开发活动及特色应用 4.3.1 开发应用的领域 4.3.2 AndroidMarket特色应用一览 4.4 你也可以做东家——申请Market账号 4.4.1 卖东西要先入伙——准备工作 4.4.2 入伙过程——申请 4.5 开张了——在Market上发布应用 4.5.1 发布时可能遇到的错误 4.5.2 卖东西也要签名——生成签名文件 4.5.3 打包、签名、发布应用 4.6 本章小结 第5章 千里之行始于足下——第一个应用HelloWorld 5.1 HelloWorld应用分析 5.1.1 新建一个Android工程 5.1.2 填写工程的信息 5.1.3 编程实现 5.1.4 运行项目 5.2 调试项目 5.2.1 设置断点 5.2.2 Debug项目 5.2.3 断点调试 5.3 本章小结 第6章 磨刀不误砍柴工——Android应用程序结构介绍 6.1 Android体系结构介绍 6.1.1 应用程序(Application) 6.1.2 应用程序框架(ApplicationFramework) 6.1.3 库(Libraries)和运行环境(RunTime) 6.2 Android应用程序组成 6.2.1 Activity介绍 6.2.2 BroadcastIntentReceiver介绍 6.2.3 Service介绍 6.2.4 ContentProvider介绍 6.3 Android应用工程文件组成 6.4 本章小结 第7章 良好的学习开端——Android基本组件介绍 7.1 第一印象很重要——界面UI元素介绍 7.1.1 视图组件(View) 7.1.2 视图容器组件(Viewgroup) 7.1.3 布局组件(Layout) 7.1.4 布局参数(LayoutParams) 7.2 我的美丽我做主——Android中应用界面布局 7.2.1 实例操作演示 7.2.2 实例编程实现 7.3 不积跬步无以至千里——常用widget组件介绍 7.3.1 创建widget组件实例 7.3.2 按钮(Button)介绍与应用 7.3.3 文本框(TextView)介绍与应用 7.3.4 编辑框(EditText)介绍与应用 7.3.5 多项选择(CheckBox)介绍与
在delphi7中,如何用TXMLDocument这个类访问下面这个xml中的"商品名称"这个节点的值:<?xml version="1.0" encoding="gb2312"?> <inv> <fpzl>s</fpzl> <成品油>0</成品油> <getmw/> <CheckEWM/> <PriceKind>1</PriceKind> <autoxxb>0</autoxxb> <购方名称>重庆缙嘉嘉商贸有限公司</购方名称> <购方税号>91500117MA7KPJXP0Y</购方税号> <购方银行帐号>中国银行重庆合川支行113078682068</购方银行帐号> <购方地址电话>重庆市合川区草街拓展园区15723038760</购方地址电话> <税率>0</税率> <备注/> <开票人>张三</开票人> <复核人>周雯燕</复核人> <收款人>陈溢倩</收款人> <销方银行帐号>中国银行鄯善县新城东路支行107670942234</销方银行帐号> <销方地址电话>广州市番禺区456315896</销方地址电话> <合计金额>995575.22</合计金额> <合计税额>129424.78</合计税额> <销售单据编号>HS.GY.DBHS20230526-0025121</销售单据编号> <销方名称>航信培训企业</销方名称> <销方税号>440001999999179</销方税号> <原发票代码/> <原发票号码/> <通知单编号/> <电子邮箱/> <手机号/> <开票日期/> <红冲原因></红冲原因> <details> <detail> <商品编号/> <商品名称>金属硅鿃</商品名称> <规格型号/> <计量单位>吨</计量单位> <税收分类编码>1080208</税收分类编码> <数量>75</数量> <单价>15000</单价> <金额>1125000</金额> <税额>129424.78</税额> <优惠政策名称/> <税率>13</税率> <零税率标志/> </detail> </details> <清单标志>0</清单标志> </inv>
05-28

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值